Home | History | Annotate | Download | only in util
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard.classfile.util;
     22 
     23 import proguard.classfile.*;
     24 
     25 import java.util.Stack;
     26 
     27 /**
     28  * A <code>DescriptorClassEnumeration</code> provides an enumeration of all
     29  * classes mentioned in a given descriptor or signature.
     30  *
     31  * @author Eric Lafortune
     32  */
     33 public class DescriptorClassEnumeration
     34 {
     35     private String  descriptor;
     36 
     37     private int     index;
     38     private int     nestingLevel;
     39     private boolean isInnerClassName;
     40     private String  accumulatedClassName;
     41     private Stack   accumulatedClassNames;
     42 
     43 
     44     /**
     45      * Creates a new DescriptorClassEnumeration for the given descriptor.
     46      */
     47     public DescriptorClassEnumeration(String descriptor)
     48     {
     49         this.descriptor = descriptor;
     50     }
     51 
     52 
     53     /**
     54      * Returns the number of classes contained in the descriptor. This
     55      * is the number of class names that the enumeration will return.
     56      */
     57     public int classCount()
     58     {
     59         int count = 0;
     60 
     61         reset();
     62 
     63         nextFluff();
     64         while (hasMoreClassNames())
     65         {
     66             count++;
     67 
     68             nextClassName();
     69             nextFluff();
     70         }
     71 
     72         reset();
     73 
     74         return count;
     75     }
     76 
     77 
     78     /**
     79      * Resets the enumeration.
     80      */
     81     private void reset()
     82     {
     83         index                 = 0;
     84         nestingLevel          = 0;
     85         isInnerClassName      = false;
     86         accumulatedClassName  = null;
     87         accumulatedClassNames = null;
     88     }
     89 
     90 
     91     /**
     92      * Returns whether the enumeration can provide more class names from the
     93      * descriptor.
     94      */
     95     public boolean hasMoreClassNames()
     96     {
     97         return index < descriptor.length();
     98     }
     99 
    100 
    101     /**
    102      * Returns the next fluff (surrounding class names) from the descriptor.
    103      */
    104     public String nextFluff()
    105     {
    106         int fluffStartIndex = index;
    107 
    108         // Find the first token marking the start of a class name 'L' or '.'.
    109         loop: while (index < descriptor.length())
    110         {
    111             switch (descriptor.charAt(index++))
    112             {
    113                 case ClassConstants.TYPE_GENERIC_START:
    114                 {
    115                     nestingLevel++;
    116 
    117                     // Make sure we have a stack.
    118                     if (accumulatedClassNames == null)
    119                     {
    120                         accumulatedClassNames = new Stack();
    121                     }
    122 
    123                     // Remember the accumulated class name.
    124                     accumulatedClassNames.push(accumulatedClassName);
    125 
    126                     break;
    127                 }
    128                 case ClassConstants.TYPE_GENERIC_END:
    129                 {
    130                     nestingLevel--;
    131 
    132                     // Return to the accumulated class name outside the
    133                     // generic block.
    134                     accumulatedClassName = (String)accumulatedClassNames.pop();
    135 
    136                     continue loop;
    137                 }
    138                 case ClassConstants.TYPE_GENERIC_BOUND:
    139                 {
    140                     continue loop;
    141                 }
    142                 case ClassConstants.TYPE_CLASS_START:
    143                 {
    144                     // We've found the start of an ordinary class name.
    145                     nestingLevel += 2;
    146                     isInnerClassName = false;
    147                     break loop;
    148                 }
    149                 case ClassConstants.TYPE_CLASS_END:
    150                 {
    151                     nestingLevel -= 2;
    152                     break;
    153                 }
    154                 case JavaConstants.INNER_CLASS_SEPARATOR:
    155                 {
    156                     // We've found the start of an inner class name in a signature.
    157                     isInnerClassName = true;
    158                     break loop;
    159                 }
    160                 case ClassConstants.TYPE_GENERIC_VARIABLE_START:
    161                 {
    162                     // We've found the start of a type identifier. Skip to the end.
    163                     while (descriptor.charAt(index++) != ClassConstants.TYPE_CLASS_END);
    164                     break;
    165                 }
    166             }
    167 
    168             if (nestingLevel == 1 &&
    169                 descriptor.charAt(index) != ClassConstants.TYPE_GENERIC_END)
    170             {
    171                 // We're at the start of a type parameter. Skip to the start
    172                 // of the bounds.
    173                 while (descriptor.charAt(index++) != ClassConstants.TYPE_GENERIC_BOUND);
    174             }
    175         }
    176 
    177         return descriptor.substring(fluffStartIndex, index);
    178     }
    179 
    180 
    181     /**
    182      * Returns the next class name from the descriptor.
    183      */
    184     public String nextClassName()
    185     {
    186         int classNameStartIndex = index;
    187 
    188         // Find the first token marking the end of a class name '<' or ';'.
    189         loop: while (true)
    190         {
    191             switch (descriptor.charAt(index))
    192             {
    193                 case ClassConstants.TYPE_GENERIC_START:
    194                 case ClassConstants.TYPE_CLASS_END:
    195                 case JavaConstants.INNER_CLASS_SEPARATOR:
    196                 {
    197                     break loop;
    198                 }
    199             }
    200 
    201             index++;
    202         }
    203 
    204         String className = descriptor.substring(classNameStartIndex, index);
    205 
    206         // Recompose the inner class name if necessary.
    207         accumulatedClassName = isInnerClassName ?
    208             accumulatedClassName + ClassConstants.INNER_CLASS_SEPARATOR + className :
    209             className;
    210 
    211         return accumulatedClassName;
    212     }
    213 
    214 
    215     /**
    216      * Returns whether the most recently returned class name was a recomposed
    217      * inner class name from a signature.
    218      */
    219     public boolean isInnerClassName()
    220     {
    221         return isInnerClassName;
    222     }
    223 
    224 
    225     /**
    226      * A main method for testing the class name enumeration.
    227      */
    228     public static void main(String[] args)
    229     {
    230         try
    231         {
    232             for (int index = 0; index < args.length; index++)
    233             {
    234                 String descriptor = args[index];
    235 
    236                 System.out.println("Descriptor ["+descriptor+"]");
    237                 DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor);
    238                 System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
    239                 while (enumeration.hasMoreClassNames())
    240                 {
    241                     System.out.println("  Name:  ["+enumeration.nextClassName()+"]");
    242                     System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
    243                 }
    244             }
    245         }
    246         catch (Exception ex)
    247         {
    248             ex.printStackTrace();
    249         }
    250     }
    251 }
    252