001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.classfile.util;
022:
023: import proguard.classfile.ClassConstants;
024:
025: /**
026: * A <code>DescriptorClassEnumeration</code> provides an enumeration of all
027: * classes mentioned in a given descriptor or signature.
028: *
029: * @author Eric Lafortune
030: */
031: public class DescriptorClassEnumeration {
032: private String descriptor;
033:
034: private int index;
035: private int nestingLevel;
036: private boolean isInnerClassName;
037: private String accumulatedClassName;
038:
039: /**
040: * Creates a new DescriptorClassEnumeration for the given descriptor.
041: */
042: public DescriptorClassEnumeration(String descriptor) {
043: this .descriptor = descriptor;
044: }
045:
046: /**
047: * Returns the number of classes contained in the descriptor. This
048: * is the number of class names that the enumeration will return.
049: */
050: public int classCount() {
051: int count = 0;
052:
053: nextFluff();
054: while (hasMoreClassNames()) {
055: count++;
056:
057: nextClassName();
058: nextFluff();
059: }
060:
061: index = 0;
062:
063: return count;
064: }
065:
066: /**
067: * Returns whether the enumeration can provide more class names from the
068: * descriptor.
069: */
070: public boolean hasMoreClassNames() {
071: return index < descriptor.length();
072: }
073:
074: /**
075: * Returns the next fluff (surrounding class names) from the descriptor.
076: */
077: public String nextFluff() {
078: int fluffStartIndex = index;
079:
080: // Find the first token marking the start of a class name 'L' or '.'.
081: loop: while (index < descriptor.length()) {
082: switch (descriptor.charAt(index++)) {
083: case ClassConstants.INTERNAL_TYPE_GENERIC_START: {
084: nestingLevel++;
085: break;
086: }
087: case ClassConstants.INTERNAL_TYPE_GENERIC_END: {
088: nestingLevel--;
089: break;
090: }
091: case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND: {
092: continue loop;
093: }
094: case ClassConstants.INTERNAL_TYPE_CLASS_START: {
095: // We've found the start of an ordinary class name.
096: nestingLevel += 2;
097: isInnerClassName = false;
098: break loop;
099: }
100: case ClassConstants.INTERNAL_TYPE_CLASS_END: {
101: nestingLevel -= 2;
102: break;
103: }
104: case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: {
105: // We've found the start of an inner class name in a signature.
106: isInnerClassName = true;
107: break loop;
108: }
109: case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: {
110: // We've found the start of a type identifier. Skip to the end.
111: while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END)
112: ;
113: break;
114: }
115: }
116:
117: if (nestingLevel == 1
118: && descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END) {
119: // We're at the start of a type parameter. Skip to the start
120: // of the bounds.
121: while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND)
122: ;
123: }
124: }
125:
126: return descriptor.substring(fluffStartIndex, index);
127: }
128:
129: /**
130: * Returns the next class name from the descriptor.
131: */
132: public String nextClassName() {
133: int classNameStartIndex = index;
134:
135: // Find the first token marking the end of a class name '<' or ';'.
136: loop: while (true) {
137: switch (descriptor.charAt(index)) {
138: case ClassConstants.INTERNAL_TYPE_GENERIC_START:
139: case ClassConstants.INTERNAL_TYPE_CLASS_END: {
140: break loop;
141: }
142: }
143:
144: index++;
145: }
146:
147: String className = descriptor.substring(classNameStartIndex,
148: index);
149:
150: // Recompose the inner class name if necessary.
151: accumulatedClassName = isInnerClassName ? accumulatedClassName
152: + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR
153: + className : className;
154:
155: return accumulatedClassName;
156: }
157:
158: /**
159: * Returns whether the most recently returned class name was a recomposed
160: * inner class name from a signature.
161: */
162: public boolean isInnerClassName() {
163: return isInnerClassName;
164: }
165:
166: /**
167: * A main method for testing the class name enumeration.
168: */
169: public static void main(String[] args) {
170: try {
171: System.out.println("Descriptor [" + args[0] + "]");
172: DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(
173: args[0]);
174: System.out.println("Class count = "
175: + enumeration.classCount());
176: System.out.println(" Fluff: [" + enumeration.nextFluff()
177: + "]");
178: while (enumeration.hasMoreClassNames()) {
179: System.out.println(" Name: ["
180: + enumeration.nextClassName() + "]");
181: System.out.println(" Fluff: ["
182: + enumeration.nextFluff() + "]");
183: }
184: } catch (Exception ex) {
185: ex.printStackTrace();
186: }
187: }
188: }
|