• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 Eric Lafortune (eric@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      */
DescriptorClassEnumeration(String descriptor)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      */
classCount()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      */
reset()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      */
hasMoreClassNames()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      */
nextFluff()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      */
nextClassName()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      */
isInnerClassName()219     public boolean isInnerClassName()
220     {
221         return isInnerClassName;
222     }
223 
224 
225     /**
226      * A main method for testing the class name enumeration.
227      */
main(String[] args)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