1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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 */ 21package proguard.classfile.util; 22 23import proguard.classfile.ClassConstants; 24 25import 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 */ 33public 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 nextFluff(); 62 while (hasMoreClassNames()) 63 { 64 count++; 65 66 nextClassName(); 67 nextFluff(); 68 } 69 70 index = 0; 71 72 return count; 73 } 74 75 76 /** 77 * Returns whether the enumeration can provide more class names from the 78 * descriptor. 79 */ 80 public boolean hasMoreClassNames() 81 { 82 return index < descriptor.length(); 83 } 84 85 86 /** 87 * Returns the next fluff (surrounding class names) from the descriptor. 88 */ 89 public String nextFluff() 90 { 91 int fluffStartIndex = index; 92 93 // Find the first token marking the start of a class name 'L' or '.'. 94 loop: while (index < descriptor.length()) 95 { 96 switch (descriptor.charAt(index++)) 97 { 98 case ClassConstants.INTERNAL_TYPE_GENERIC_START: 99 { 100 nestingLevel++; 101 102 // Make sure we have a stack. 103 if (accumulatedClassNames == null) 104 { 105 accumulatedClassNames = new Stack(); 106 } 107 108 // Remember the accumulated class name. 109 accumulatedClassNames.push(accumulatedClassName); 110 111 break; 112 } 113 case ClassConstants.INTERNAL_TYPE_GENERIC_END: 114 { 115 nestingLevel--; 116 117 // Return to the accumulated class name outside the 118 // generic block. 119 accumulatedClassName = (String)accumulatedClassNames.pop(); 120 121 continue loop; 122 } 123 case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND: 124 { 125 continue loop; 126 } 127 case ClassConstants.INTERNAL_TYPE_CLASS_START: 128 { 129 // We've found the start of an ordinary class name. 130 nestingLevel += 2; 131 isInnerClassName = false; 132 break loop; 133 } 134 case ClassConstants.INTERNAL_TYPE_CLASS_END: 135 { 136 nestingLevel -= 2; 137 break; 138 } 139 case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: 140 { 141 // We've found the start of an inner class name in a signature. 142 isInnerClassName = true; 143 break loop; 144 } 145 case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: 146 { 147 // We've found the start of a type identifier. Skip to the end. 148 while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END); 149 break; 150 } 151 } 152 153 if (nestingLevel == 1 && 154 descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END) 155 { 156 // We're at the start of a type parameter. Skip to the start 157 // of the bounds. 158 while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND); 159 } 160 } 161 162 return descriptor.substring(fluffStartIndex, index); 163 } 164 165 166 /** 167 * Returns the next class name from the descriptor. 168 */ 169 public String nextClassName() 170 { 171 int classNameStartIndex = index; 172 173 // Find the first token marking the end of a class name '<' or ';'. 174 loop: while (true) 175 { 176 switch (descriptor.charAt(index)) 177 { 178 case ClassConstants.INTERNAL_TYPE_GENERIC_START: 179 case ClassConstants.INTERNAL_TYPE_CLASS_END: 180 case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: 181 { 182 break loop; 183 } 184 } 185 186 index++; 187 } 188 189 String className = descriptor.substring(classNameStartIndex, index); 190 191 // Recompose the inner class name if necessary. 192 accumulatedClassName = isInnerClassName ? 193 accumulatedClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR + className : 194 className; 195 196 return accumulatedClassName; 197 } 198 199 200 /** 201 * Returns whether the most recently returned class name was a recomposed 202 * inner class name from a signature. 203 */ 204 public boolean isInnerClassName() 205 { 206 return isInnerClassName; 207 } 208 209 210 /** 211 * A main method for testing the class name enumeration. 212 */ 213 public static void main(String[] args) 214 { 215 try 216 { 217 for (int index = 0; index < args.length; index++) 218 { 219 String descriptor = args[index]; 220 221 System.out.println("Descriptor ["+descriptor+"]"); 222 DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); 223 System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); 224 while (enumeration.hasMoreClassNames()) 225 { 226 System.out.println(" Name: ["+enumeration.nextClassName()+"]"); 227 System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); 228 } 229 } 230 } 231 catch (Exception ex) 232 { 233 ex.printStackTrace(); 234 } 235 } 236} 237