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