• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
21 package proguard.classfile.util;
22 
23 import proguard.classfile.ClassConstants;
24 
25 import java.util.List;
26 
27 /**
28  * Utility methods for converting between internal and external representations
29  * of names and descriptions.
30  *
31  * @author Eric Lafortune
32  */
33 public class ClassUtil
34 {
35     private static final String EMPTY_STRING = "";
36 
37 
38     /**
39      * Checks whether the given class magic number is correct.
40      * @param magicNumber the magic number.
41      * @throws UnsupportedOperationException when the magic number is incorrect.
42      */
checkMagicNumber(int magicNumber)43     public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException
44     {
45         if (magicNumber != ClassConstants.MAGIC)
46         {
47             throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class");
48         }
49     }
50 
51 
52     /**
53      * Returns the combined class version number.
54      * @param majorVersion the major part of the class version number.
55      * @param minorVersion the minor part of the class version number.
56      * @return the combined class version number.
57      */
internalClassVersion(int majorVersion, int minorVersion)58     public static int internalClassVersion(int majorVersion, int minorVersion)
59     {
60         return (majorVersion << 16) | minorVersion;
61     }
62 
63 
64     /**
65      * Returns the major part of the given class version number.
66      * @param classVersion the combined class version number.
67      * @return the major part of the class version number.
68      */
internalMajorClassVersion(int classVersion)69     public static int internalMajorClassVersion(int classVersion)
70     {
71         return classVersion >>> 16;
72     }
73 
74 
75     /**
76      * Returns the internal class version number.
77      * @param classVersion the external class version number.
78      * @return the internal class version number.
79      */
internalMinorClassVersion(int classVersion)80     public static int internalMinorClassVersion(int classVersion)
81     {
82         return classVersion & 0xffff;
83     }
84 
85 
86     /**
87      * Returns the internal class version number.
88      * @param classVersion the external class version number.
89      * @return the internal class version number.
90      */
internalClassVersion(String classVersion)91     public static int internalClassVersion(String classVersion)
92     {
93         return
94             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) ||
95             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 :
96             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 :
97             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 :
98             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 :
99             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) ||
100             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 :
101             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) ||
102             classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 :
103                                                                              0;
104     }
105 
106 
107     /**
108      * Returns the minor part of the given class version number.
109      * @param classVersion the combined class version number.
110      * @return the minor part of the class version number.
111      */
externalClassVersion(int classVersion)112     public static String externalClassVersion(int classVersion)
113     {
114         switch (classVersion)
115         {
116             case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0;
117             case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2;
118             case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3;
119             case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4;
120             case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5;
121             case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6;
122             default:                                        return null;
123         }
124     }
125 
126 
127     /**
128      * Checks whether the given class version number is supported.
129      * @param classVersion the combined class version number.
130      * @throws UnsupportedOperationException when the version is not supported.
131      */
checkVersionNumbers(int classVersion)132     public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException
133     {
134         if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 ||
135             classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6)
136         {
137             throw new UnsupportedOperationException("Unsupported version number ["+
138                                                     internalMajorClassVersion(classVersion)+"."+
139                                                     internalMinorClassVersion(classVersion)+"] for class format");
140         }
141     }
142 
143 
144     /**
145      * Converts an external class name into an internal class name.
146      * @param externalClassName the external class name,
147      *                          e.g. "<code>java.lang.Object</code>"
148      * @return the internal class name,
149      *                          e.g. "<code>java/lang/Object</code>".
150      */
internalClassName(String externalClassName)151     public static String internalClassName(String externalClassName)
152     {
153         return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR,
154                                          ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
155     }
156 
157 
158     /**
159      * Converts an internal class description into an external class description.
160      * @param accessFlags       the access flags of the class.
161      * @param internalClassName the internal class name,
162      *                          e.g. "<code>java/lang/Object</code>".
163      * @return the external class description,
164      *                          e.g. "<code>public java.lang.Object</code>".
165      */
externalFullClassDescription(int accessFlags, String internalClassName)166     public static String externalFullClassDescription(int    accessFlags,
167                                                       String internalClassName)
168     {
169         return externalClassAccessFlags(accessFlags) +
170                externalClassName(internalClassName);
171     }
172 
173 
174     /**
175      * Converts an internal class name into an external class name.
176      * @param internalClassName the internal class name,
177      *                          e.g. "<code>java/lang/Object</code>".
178      * @return the external class name,
179      *                          e.g. "<code>java.lang.Object</code>".
180      */
externalClassName(String internalClassName)181     public static String externalClassName(String internalClassName)
182     {
183         return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) &&
184                //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ?
185                //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) :
186                internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
187                                          ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
188     }
189 
190 
191     /**
192      * Converts an internal class name into an external short class name, without
193      * package specification.
194      * @param externalClassName the external class name,
195      *                          e.g. "<code>java.lang.Object</code>"
196      * @return the external short class name,
197      *                          e.g. "<code>Object</code>".
198      */
externalShortClassName(String externalClassName)199     public static String externalShortClassName(String externalClassName)
200     {
201         int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
202         return externalClassName.substring(index+1);
203     }
204 
205 
206     /**
207      * Returns whether the given internal type is an array type.
208      * @param internalType the internal type,
209      *                     e.g. "<code>[[Ljava/lang/Object;</code>".
210      * @return <code>true</code> if the given type is an array type,
211      *         <code>false</code> otherwise.
212      */
isInternalArrayType(String internalType)213     public static boolean isInternalArrayType(String internalType)
214     {
215         return internalType.length() > 1 &&
216                internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY;
217     }
218 
219 
220     /**
221      * Returns the number of dimensions of the given internal type.
222      * @param internalType the internal type,
223      *                     e.g. "<code>[[Ljava/lang/Object;</code>".
224      * @return the number of dimensions, e.g. 2.
225      */
internalArrayTypeDimensionCount(String internalType)226     public static int internalArrayTypeDimensionCount(String internalType)
227     {
228         int dimensions = 0;
229         while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY)
230         {
231             dimensions++;
232         }
233 
234         return dimensions;
235     }
236 
237 
238     /**
239      * Returns whether the given internal class name is one of the interfaces
240      * that is implemented by all array types. These class names are
241      * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and
242      * "<code>java/io/Serializable</code>"
243      * @param internalClassName the internal class name,
244      *                          e.g. "<code>java/lang/Object</code>".
245      * @return <code>true</code> if the given type is an array interface name,
246      *         <code>false</code> otherwise.
247      */
isInternalArrayInterfaceName(String internalClassName)248     public static boolean isInternalArrayInterfaceName(String internalClassName)
249     {
250         return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName)    ||
251                ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) ||
252                ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName);
253     }
254 
255 
256     /**
257      * Returns whether the given internal type is a plain primitive type
258      * (not void).
259      * @param internalType the internal type,
260      *                     e.g. "<code>I</code>".
261      * @return <code>true</code> if the given type is a class type,
262      *         <code>false</code> otherwise.
263      */
isInternalPrimitiveType(char internalType)264     public static boolean isInternalPrimitiveType(char internalType)
265     {
266         return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN ||
267                internalType == ClassConstants.INTERNAL_TYPE_BYTE    ||
268                internalType == ClassConstants.INTERNAL_TYPE_CHAR    ||
269                internalType == ClassConstants.INTERNAL_TYPE_SHORT   ||
270                internalType == ClassConstants.INTERNAL_TYPE_INT     ||
271                internalType == ClassConstants.INTERNAL_TYPE_FLOAT   ||
272                internalType == ClassConstants.INTERNAL_TYPE_LONG    ||
273                internalType == ClassConstants.INTERNAL_TYPE_DOUBLE;
274     }
275 
276 
277     /**
278      * Returns whether the given internal type is a primitive Category 2 type.
279      * @param internalType the internal type,
280      *                     e.g. "<code>L</code>".
281      * @return <code>true</code> if the given type is a Category 2 type,
282      *         <code>false</code> otherwise.
283      */
isInternalCategory2Type(String internalType)284     public static boolean isInternalCategory2Type(String internalType)
285     {
286         return internalType.length() == 1 &&
287                (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG ||
288                 internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE);
289     }
290 
291 
292     /**
293      * Returns whether the given internal type is a plain class type
294      * (including an array type of a plain class type).
295      * @param internalType the internal type,
296      *                     e.g. "<code>Ljava/lang/Object;</code>".
297      * @return <code>true</code> if the given type is a class type,
298      *         <code>false</code> otherwise.
299      */
isInternalClassType(String internalType)300     public static boolean isInternalClassType(String internalType)
301     {
302         int length = internalType.length();
303         return length > 1 &&
304 //             internalType.charAt(0)        == ClassConstants.INTERNAL_TYPE_CLASS_START &&
305                internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END;
306     }
307 
308 
309     /**
310      * Returns the internal type of a given class name.
311      * @param internalClassName the internal class name,
312      *                          e.g. "<code>java/lang/Object</code>".
313      * @return the internal type,
314      *                          e.g. "<code>Ljava/lang/Object;</code>".
315      */
internalTypeFromClassName(String internalClassName)316     public static String internalTypeFromClassName(String internalClassName)
317     {
318         return internalArrayTypeFromClassName(internalClassName, 0);
319     }
320 
321 
322     /**
323      * Returns the internal array type of a given class name with a given number
324      * of dimensions. If the number of dimensions is 0, the class name itself is
325      * returned.
326      * @param internalClassName the internal class name,
327      *                          e.g. "<code>java/lang/Object</code>".
328      * @param dimensionCount    the number of array dimensions.
329      * @return the internal array type of the array elements,
330      *                          e.g. "<code>Ljava/lang/Object;</code>".
331      */
internalArrayTypeFromClassName(String internalClassName, int dimensionCount)332     public static String internalArrayTypeFromClassName(String internalClassName,
333                                                         int    dimensionCount)
334     {
335         StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2);
336 
337         for (int dimension = 0; dimension < dimensionCount; dimension++)
338         {
339             buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY);
340         }
341 
342         return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START)
343                      .append(internalClassName)
344                      .append(ClassConstants.INTERNAL_TYPE_CLASS_END)
345                      .toString();
346     }
347 
348 
349     /**
350      * Returns the internal element type of a given internal array type.
351      * @param internalArrayType the internal array type,
352      *                          e.g. "<code>[[Ljava/lang/Object;</code>" or
353      *                               "<code>[I</code>".
354      * @return the internal type of the array elements,
355      *                          e.g. "<code>Ljava/lang/Object;</code>" or
356      *                               "<code>I</code>".
357      */
internalTypeFromArrayType(String internalArrayType)358     public static String internalTypeFromArrayType(String internalArrayType)
359     {
360         int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY);
361         return internalArrayType.substring(index+1);
362     }
363 
364 
365     /**
366      * Returns the internal class name of a given internal class type
367      * (including an array type). Types involving primitive types are returned
368      * unchanged.
369      * @param internalClassType the internal class type,
370      *                          e.g. "<code>[Ljava/lang/Object;</code>",
371      *                               "<code>Ljava/lang/Object;</code>", or
372      *                               "<code>java/lang/Object</code>".
373      * @return the internal class name,
374      *                          e.g. "<code>java/lang/Object</code>".
375      */
internalClassNameFromClassType(String internalClassType)376     public static String internalClassNameFromClassType(String internalClassType)
377     {
378         return isInternalClassType(internalClassType) ?
379             internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1,
380                                         internalClassType.length()-1) :
381             internalClassType;
382     }
383 
384 
385     /**
386      * Returns the internal class name of any given internal descriptor type,
387      * disregarding array prefixes.
388      * @param internalClassType the internal class type,
389      *                          e.g. "<code>Ljava/lang/Object;</code>" or
390      *                               "<code>[[I</code>".
391      * @return the internal class name,
392      *                          e.g. "<code>java/lang/Object</code>" or
393      *                               <code>null</code>.
394      */
internalClassNameFromType(String internalClassType)395     public static String internalClassNameFromType(String internalClassType)
396     {
397         if (!isInternalClassType(internalClassType))
398         {
399             return null;
400         }
401 
402         // Is it an array type?
403         if (isInternalArrayType(internalClassType))
404         {
405             internalClassType = internalTypeFromArrayType(internalClassType);
406         }
407 
408         return internalClassNameFromClassType(internalClassType);
409     }
410 
411 
412     /**
413      * Returns the internal type of the given internal method descriptor.
414      * @param internalMethodDescriptor the internal method descriptor,
415      *                                 e.g. "<code>(II)Z</code>".
416      * @return the internal return type,
417      *                                 e.g. "<code>Z</code>".
418      */
internalMethodReturnType(String internalMethodDescriptor)419     public static String internalMethodReturnType(String internalMethodDescriptor)
420     {
421         int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
422         return internalMethodDescriptor.substring(index + 1);
423     }
424 
425 
426     /**
427      * Returns the number of parameters of the given internal method descriptor.
428      * @param internalMethodDescriptor the internal method descriptor,
429      *                                 e.g. "<code>(ID)Z</code>".
430      * @return the number of parameters,
431      *                                 e.g. 2.
432      */
internalMethodParameterCount(String internalMethodDescriptor)433     public static int internalMethodParameterCount(String internalMethodDescriptor)
434     {
435         InternalTypeEnumeration internalTypeEnumeration =
436             new InternalTypeEnumeration(internalMethodDescriptor);
437 
438         int counter = 0;
439         while (internalTypeEnumeration.hasMoreTypes())
440         {
441             internalTypeEnumeration.nextType();
442 
443             counter++;
444         }
445 
446         return counter;
447     }
448 
449 
450     /**
451      * Returns the size taken up on the stack by the parameters of the given
452      * internal method descriptor. This accounts for long and double parameters
453      * taking up two entries.
454      * @param internalMethodDescriptor the internal method descriptor,
455      *                                 e.g. "<code>(ID)Z</code>".
456      * @return the size taken up on the stack,
457      *                                 e.g. 3.
458      */
internalMethodParameterSize(String internalMethodDescriptor)459     public static int internalMethodParameterSize(String internalMethodDescriptor)
460     {
461         return internalMethodParameterSize(internalMethodDescriptor, true);
462     }
463 
464 
465     /**
466      * Returns the size taken up on the stack by the parameters of the given
467      * internal method descriptor. This accounts for long and double parameters
468      * taking up two entries, and a non-static method taking up an additional
469      * entry.
470      * @param internalMethodDescriptor the internal method descriptor,
471      *                                 e.g. "<code>(ID)Z</code>".
472      * @param accessFlags              the access flags of the method,
473      *                                 e.g. 0.
474      * @return the size taken up on the stack,
475      *                                 e.g. 4.
476      */
internalMethodParameterSize(String internalMethodDescriptor, int accessFlags)477     public static int internalMethodParameterSize(String internalMethodDescriptor,
478                                                   int    accessFlags)
479     {
480         return internalMethodParameterSize(internalMethodDescriptor,
481                                            (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0);
482     }
483 
484 
485     /**
486      * Returns the size taken up on the stack by the parameters of the given
487      * internal method descriptor. This accounts for long and double parameters
488      * taking up two spaces, and a non-static method taking up an additional
489      * entry.
490      * @param internalMethodDescriptor the internal method descriptor,
491      *                                 e.g. "<code>(ID)Z</code>".
492      * @param isStatic                 specifies whether the method is static,
493      *                                 e.g. false.
494      * @return the size taken up on the stack,
495      *                                 e.g. 4.
496      */
internalMethodParameterSize(String internalMethodDescriptor, boolean isStatic)497     public static int internalMethodParameterSize(String  internalMethodDescriptor,
498                                                   boolean isStatic)
499     {
500         InternalTypeEnumeration internalTypeEnumeration =
501             new InternalTypeEnumeration(internalMethodDescriptor);
502 
503         int size = isStatic ? 0 : 1;
504         while (internalTypeEnumeration.hasMoreTypes())
505         {
506             String internalType = internalTypeEnumeration.nextType();
507 
508             size += internalTypeSize(internalType);
509         }
510 
511         return size;
512     }
513 
514 
515     /**
516      * Returns the size taken up on the stack by the given internal type.
517      * The size is 1, except for long and double types, for which it is 2,
518      * and for the void type, for which 0 is returned.
519      * @param internalType the internal type,
520      *                     e.g. "<code>I</code>".
521      * @return the size taken up on the stack,
522      *                     e.g. 1.
523      */
internalTypeSize(String internalType)524     public static int internalTypeSize(String internalType)
525     {
526         if (internalType.length() == 1)
527         {
528             char internalPrimitiveType = internalType.charAt(0);
529             if      (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG ||
530                      internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE)
531             {
532                 return 2;
533             }
534             else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID)
535             {
536                 return 0;
537             }
538         }
539 
540         return 1;
541     }
542 
543 
544     /**
545      * Converts an external type into an internal type.
546      * @param externalType the external type,
547      *                     e.g. "<code>java.lang.Object[][]</code>" or
548      *                          "<code>int[]</code>".
549      * @return the internal type,
550      *                     e.g. "<code>[[Ljava/lang/Object;</code>" or
551      *                          "<code>[I</code>".
552      */
internalType(String externalType)553     public static String internalType(String externalType)
554     {
555         // Strip the array part, if any.
556         int dimensionCount = externalArrayTypeDimensionCount(externalType);
557         if (dimensionCount > 0)
558         {
559             externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length());
560         }
561 
562         // Analyze the actual type part.
563         char internalTypeChar =
564             externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID   ) ?
565                                 ClassConstants.INTERNAL_TYPE_VOID     :
566             externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ?
567                                 ClassConstants.INTERNAL_TYPE_BOOLEAN  :
568             externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE   ) ?
569                                 ClassConstants.INTERNAL_TYPE_BYTE     :
570             externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR   ) ?
571                                 ClassConstants.INTERNAL_TYPE_CHAR     :
572             externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT  ) ?
573                                 ClassConstants.INTERNAL_TYPE_SHORT    :
574             externalType.equals(ClassConstants.EXTERNAL_TYPE_INT    ) ?
575                                 ClassConstants.INTERNAL_TYPE_INT      :
576             externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT  ) ?
577                                 ClassConstants.INTERNAL_TYPE_FLOAT    :
578             externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG   ) ?
579                                 ClassConstants.INTERNAL_TYPE_LONG     :
580             externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ?
581                                 ClassConstants.INTERNAL_TYPE_DOUBLE   :
582             externalType.equals("%"                                 ) ?
583                                 '%'                                   :
584                                 (char)0;
585 
586         String internalType =
587             internalTypeChar != 0 ? String.valueOf(internalTypeChar) :
588                                     ClassConstants.INTERNAL_TYPE_CLASS_START +
589                                     internalClassName(externalType) +
590                                     ClassConstants.INTERNAL_TYPE_CLASS_END;
591 
592         // Prepend the array part, if any.
593         for (int count = 0; count < dimensionCount; count++)
594         {
595             internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType;
596         }
597 
598         return internalType;
599     }
600 
601 
602     /**
603      * Returns the number of dimensions of the given external type.
604      * @param externalType the external type,
605      *                     e.g. "<code>[[Ljava/lang/Object;</code>".
606      * @return the number of dimensions, e.g. 2.
607      */
externalArrayTypeDimensionCount(String externalType)608     public static int externalArrayTypeDimensionCount(String externalType)
609     {
610         int dimensions = 0;
611         int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length();
612         int offset = externalType.length() - length;
613         while (externalType.regionMatches(offset,
614                                           ClassConstants.EXTERNAL_TYPE_ARRAY,
615                                           0,
616                                           length))
617         {
618             dimensions++;
619             offset -= length;
620         }
621 
622         return dimensions;
623     }
624 
625 
626     /**
627      * Converts an internal type into an external type.
628      * @param internalType the internal type,
629      *                     e.g. "<code>[[Ljava/lang/Object;</code>" or
630      *                          "<code>[I</code>".
631      * @return the external type,
632      *                     e.g. "<code>java.lang.Object[][]</code>" or
633      *                          "<code>int[]</code>".
634      */
externalType(String internalType)635     public static String externalType(String internalType)
636     {
637         // Strip the array part, if any.
638         int dimensionCount = internalArrayTypeDimensionCount(internalType);
639         if (dimensionCount > 0)
640         {
641             internalType = internalType.substring(dimensionCount);
642         }
643 
644         // Analyze the actual type part.
645         char internalTypeChar = internalType.charAt(0);
646 
647         String externalType =
648             internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID        ?
649                                 ClassConstants.EXTERNAL_TYPE_VOID        :
650             internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN     ?
651                                 ClassConstants.EXTERNAL_TYPE_BOOLEAN     :
652             internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE        ?
653                                 ClassConstants.EXTERNAL_TYPE_BYTE        :
654             internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR        ?
655                                 ClassConstants.EXTERNAL_TYPE_CHAR        :
656             internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT       ?
657                                 ClassConstants.EXTERNAL_TYPE_SHORT       :
658             internalTypeChar == ClassConstants.INTERNAL_TYPE_INT         ?
659                                 ClassConstants.EXTERNAL_TYPE_INT         :
660             internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT       ?
661                                 ClassConstants.EXTERNAL_TYPE_FLOAT       :
662             internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG        ?
663                                 ClassConstants.EXTERNAL_TYPE_LONG        :
664             internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE      ?
665                                 ClassConstants.EXTERNAL_TYPE_DOUBLE      :
666             internalTypeChar == '%'                                      ?
667                                 "%"                                      :
668             internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ?
669                                 externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) :
670                                 null;
671 
672         if (externalType == null)
673         {
674             throw new IllegalArgumentException("Unknown type ["+internalType+"]");
675         }
676 
677         // Append the array part, if any.
678         for (int count = 0; count < dimensionCount; count++)
679         {
680             externalType += ClassConstants.EXTERNAL_TYPE_ARRAY;
681         }
682 
683         return externalType;
684     }
685 
686 
687     /**
688      * Returns whether the given internal descriptor String represents a method
689      * descriptor.
690      * @param internalDescriptor the internal descriptor String,
691      *                           e.g. "<code>(II)Z</code>".
692      * @return <code>true</code> if the given String is a method descriptor,
693      *         <code>false</code> otherwise.
694      */
isInternalMethodDescriptor(String internalDescriptor)695     public static boolean isInternalMethodDescriptor(String internalDescriptor)
696     {
697         return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN;
698     }
699 
700 
701     /**
702      * Returns whether the given member String represents an external method
703      * name with arguments.
704      * @param externalMemberNameAndArguments the external member String,
705      *                                       e.g. "<code>myField</code>" or
706      *                                       e.g. "<code>myMethod(int,int)</code>".
707      * @return <code>true</code> if the given String refers to a method,
708      *         <code>false</code> otherwise.
709      */
isExternalMethodNameAndArguments(String externalMemberNameAndArguments)710     public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments)
711     {
712         return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0;
713     }
714 
715 
716     /**
717      * Returns the name part of the given external method name and arguments.
718      * @param externalMethodNameAndArguments the external method name and arguments,
719      *                                       e.g. "<code>myMethod(int,int)</code>".
720      * @return the name part of the String, e.g. "<code>myMethod</code>".
721      */
externalMethodName(String externalMethodNameAndArguments)722     public static String externalMethodName(String externalMethodNameAndArguments)
723     {
724         ExternalTypeEnumeration externalTypeEnumeration =
725             new ExternalTypeEnumeration(externalMethodNameAndArguments);
726 
727         return externalTypeEnumeration.methodName();
728     }
729 
730 
731     /**
732      * Converts the given external method return type and name and arguments to
733      * an internal method descriptor.
734      * @param externalReturnType             the external method return type,
735      *                                       e.g. "<code>boolean</code>".
736      * @param externalMethodNameAndArguments the external method name and arguments,
737      *                                       e.g. "<code>myMethod(int,int)</code>".
738      * @return the internal method descriptor,
739      *                                       e.g. "<code>(II)Z</code>".
740      */
internalMethodDescriptor(String externalReturnType, String externalMethodNameAndArguments)741     public static String internalMethodDescriptor(String externalReturnType,
742                                                   String externalMethodNameAndArguments)
743     {
744         StringBuffer internalMethodDescriptor = new StringBuffer();
745         internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
746 
747         ExternalTypeEnumeration externalTypeEnumeration =
748             new ExternalTypeEnumeration(externalMethodNameAndArguments);
749 
750         while (externalTypeEnumeration.hasMoreTypes())
751         {
752             internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType()));
753         }
754 
755         internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
756         internalMethodDescriptor.append(internalType(externalReturnType));
757 
758         return internalMethodDescriptor.toString();
759     }
760 
761 
762     /**
763      * Converts the given external method return type and List of arguments to
764      * an internal method descriptor.
765      * @param externalReturnType the external method return type,
766      *                                       e.g. "<code>boolean</code>".
767      * @param externalArguments the external method arguments,
768      *                                       e.g. <code>{ "int", "int" }</code>.
769      * @return the internal method descriptor,
770      *                                       e.g. "<code>(II)Z</code>".
771      */
internalMethodDescriptor(String externalReturnType, List externalArguments)772     public static String internalMethodDescriptor(String externalReturnType,
773                                                   List   externalArguments)
774     {
775         StringBuffer internalMethodDescriptor = new StringBuffer();
776         internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
777 
778         for (int index = 0; index < externalArguments.size(); index++)
779         {
780             internalMethodDescriptor.append(internalType((String)externalArguments.get(index)));
781         }
782 
783         internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
784         internalMethodDescriptor.append(internalType(externalReturnType));
785 
786         return internalMethodDescriptor.toString();
787     }
788 
789 
790     /**
791      * Converts an internal field description into an external full field description.
792      * @param accessFlags             the access flags of the field.
793      * @param fieldName               the field name,
794      *                                e.g. "<code>myField</code>".
795      * @param internalFieldDescriptor the internal field descriptor,
796      *                                e.g. "<code>Z</code>".
797      * @return the external full field description,
798      *                                e.g. "<code>public boolean myField</code>".
799      */
externalFullFieldDescription(int accessFlags, String fieldName, String internalFieldDescriptor)800     public static String externalFullFieldDescription(int    accessFlags,
801                                                       String fieldName,
802                                                       String internalFieldDescriptor)
803     {
804         return externalFieldAccessFlags(accessFlags) +
805                externalType(internalFieldDescriptor) +
806                ' ' +
807                fieldName;
808     }
809 
810 
811     /**
812      * Converts an internal method description into an external full method description.
813      * @param internalClassName        the internal name of the class of the method,
814      *                                 e.g. "<code>mypackage/MyClass</code>".
815      * @param accessFlags              the access flags of the method.
816      * @param internalMethodName       the internal method name,
817      *                                 e.g. "<code>myMethod</code>" or
818      *                                      "<code>&lt;init&gt;</code>".
819      * @param internalMethodDescriptor the internal method descriptor,
820      *                                 e.g. "<code>(II)Z</code>".
821      * @return the external full method description,
822      *                                 e.g. "<code>public boolean myMethod(int,int)</code>" or
823      *                                      "<code>public MyClass(int,int)</code>".
824      */
externalFullMethodDescription(String internalClassName, int accessFlags, String internalMethodName, String internalMethodDescriptor)825     public static String externalFullMethodDescription(String internalClassName,
826                                                        int    accessFlags,
827                                                        String internalMethodName,
828                                                        String internalMethodDescriptor)
829     {
830         return externalMethodAccessFlags(accessFlags) +
831                externalMethodReturnTypeAndName(internalClassName,
832                                                internalMethodName,
833                                                internalMethodDescriptor) +
834                ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN +
835                externalMethodArguments(internalMethodDescriptor) +
836                ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE;
837     }
838 
839 
840     /**
841      * Converts internal class access flags into an external access description.
842      * @param accessFlags the class access flags.
843      * @return the external class access description,
844      *         e.g. "<code>public final </code>".
845      */
externalClassAccessFlags(int accessFlags)846     public static String externalClassAccessFlags(int accessFlags)
847     {
848         return externalClassAccessFlags(accessFlags, "");
849     }
850 
851 
852     /**
853      * Converts internal class access flags into an external access description.
854      * @param accessFlags the class access flags.
855      * @param prefix      a prefix that is added to each access modifier.
856      * @return the external class access description,
857      *         e.g. "<code>public final </code>".
858      */
externalClassAccessFlags(int accessFlags, String prefix)859     public static String externalClassAccessFlags(int accessFlags, String prefix)
860     {
861         if (accessFlags == 0)
862         {
863             return EMPTY_STRING;
864         }
865 
866         StringBuffer string = new StringBuffer(50);
867 
868         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
869         {
870             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
871         }
872         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
873         {
874             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
875         }
876         if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0)
877         {
878             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION);
879         }
880         if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
881         {
882             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' ');
883         }
884         else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0)
885         {
886             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' ');
887         }
888         else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
889         {
890             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' ');
891         }
892 
893         return string.toString();
894     }
895 
896 
897     /**
898      * Converts internal field access flags into an external access description.
899      * @param accessFlags the field access flags.
900      * @return the external field access description,
901      *         e.g. "<code>public volatile </code>".
902      */
externalFieldAccessFlags(int accessFlags)903     public static String externalFieldAccessFlags(int accessFlags)
904     {
905         return externalFieldAccessFlags(accessFlags, "");
906     }
907 
908 
909     /**
910      * Converts internal field access flags into an external access description.
911      * @param accessFlags the field access flags.
912      * @param prefix      a prefix that is added to each access modifier.
913      * @return the external field access description,
914      *         e.g. "<code>public volatile </code>".
915      */
externalFieldAccessFlags(int accessFlags, String prefix)916     public static String externalFieldAccessFlags(int accessFlags, String prefix)
917     {
918         if (accessFlags == 0)
919         {
920             return EMPTY_STRING;
921         }
922 
923         StringBuffer string = new StringBuffer(50);
924 
925         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
926         {
927             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
928         }
929         if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
930         {
931             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' ');
932         }
933         if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
934         {
935             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' ');
936         }
937         if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
938         {
939             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' ');
940         }
941         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
942         {
943             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
944         }
945         if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0)
946         {
947             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' ');
948         }
949         if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0)
950         {
951             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' ');
952         }
953 
954         return string.toString();
955     }
956 
957 
958     /**
959      * Converts internal method access flags into an external access description.
960      * @param accessFlags the method access flags.
961      * @return the external method access description,
962      *                    e.g. "<code>public synchronized </code>".
963      */
externalMethodAccessFlags(int accessFlags)964     public static String externalMethodAccessFlags(int accessFlags)
965     {
966         return externalMethodAccessFlags(accessFlags, "");
967     }
968 
969 
970     /**
971      * Converts internal method access flags into an external access description.
972      * @param accessFlags the method access flags.
973      * @param prefix      a prefix that is added to each access modifier.
974      * @return the external method access description,
975      *                    e.g. "public synchronized ".
976      */
externalMethodAccessFlags(int accessFlags, String prefix)977     public static String externalMethodAccessFlags(int accessFlags, String prefix)
978     {
979         if (accessFlags == 0)
980         {
981             return EMPTY_STRING;
982         }
983 
984         StringBuffer string = new StringBuffer(50);
985 
986         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
987         {
988             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
989         }
990         if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
991         {
992             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' ');
993         }
994         if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
995         {
996             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' ');
997         }
998         if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
999         {
1000             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' ');
1001         }
1002         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
1003         {
1004             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
1005         }
1006         if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0)
1007         {
1008             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' ');
1009         }
1010         if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
1011         {
1012             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' ');
1013         }
1014         if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
1015         {
1016             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' ');
1017         }
1018         if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0)
1019         {
1020             string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' ');
1021         }
1022 
1023         return string.toString();
1024     }
1025 
1026 
1027     /**
1028      * Converts an internal method descriptor into an external method return type.
1029      * @param internalMethodDescriptor the internal method descriptor,
1030      *                                 e.g. "<code>(II)Z</code>".
1031      * @return the external method return type,
1032      *                                 e.g. "<code>boolean</code>".
1033      */
externalMethodReturnType(String internalMethodDescriptor)1034     public static String externalMethodReturnType(String internalMethodDescriptor)
1035     {
1036         return externalType(internalMethodReturnType(internalMethodDescriptor));
1037     }
1038 
1039 
1040     /**
1041      * Converts an internal class name, method name, and method descriptor to
1042      * an external method return type and name.
1043      * @param internalClassName        the internal name of the class of the method,
1044      *                                 e.g. "<code>mypackage/MyClass</code>".
1045      * @param internalMethodName       the internal method name,
1046      *                                 e.g. "<code>myMethod</code>" or
1047      *                                      "<code>&lt;init&gt;</code>".
1048      * @param internalMethodDescriptor the internal method descriptor,
1049      *                                 e.g. "<code>(II)Z</code>".
1050      * @return the external method return type and name,
1051      *                                 e.g. "<code>boolean myMethod</code>" or
1052      *                                      "<code>MyClass</code>".
1053      */
externalMethodReturnTypeAndName(String internalClassName, String internalMethodName, String internalMethodDescriptor)1054     private static String externalMethodReturnTypeAndName(String internalClassName,
1055                                                           String internalMethodName,
1056                                                           String internalMethodDescriptor)
1057     {
1058         return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
1059             externalShortClassName(externalClassName(internalClassName)) :
1060             (externalMethodReturnType(internalMethodDescriptor) +
1061              ' ' +
1062              internalMethodName);
1063     }
1064 
1065 
1066     /**
1067      * Converts an internal method descriptor into an external method argument
1068      * description.
1069      * @param internalMethodDescriptor the internal method descriptor,
1070      *                                 e.g. "<code>(II)Z</code>".
1071      * @return the external method argument description,
1072      *                                 e.g. "<code>int,int</code>".
1073      */
externalMethodArguments(String internalMethodDescriptor)1074     public static String externalMethodArguments(String internalMethodDescriptor)
1075     {
1076         StringBuffer externalMethodNameAndArguments = new StringBuffer();
1077 
1078         InternalTypeEnumeration internalTypeEnumeration =
1079             new InternalTypeEnumeration(internalMethodDescriptor);
1080 
1081         while (internalTypeEnumeration.hasMoreTypes())
1082         {
1083             externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType()));
1084             if (internalTypeEnumeration.hasMoreTypes())
1085             {
1086                 externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR);
1087             }
1088         }
1089 
1090         return externalMethodNameAndArguments.toString();
1091     }
1092 
1093 
1094     /**
1095      * Returns the internal package name of the given internal class name.
1096      * @param internalClassName the internal class name,
1097      *                          e.g. "<code>java/lang/Object</code>".
1098      * @return the internal package name,
1099      *                          e.g. "<code>java/lang</code>".
1100      */
internalPackageName(String internalClassName)1101     public static String internalPackageName(String internalClassName)
1102     {
1103         String internalPackagePrefix = internalPackagePrefix(internalClassName);
1104         int length = internalPackagePrefix.length();
1105         return length > 0 ?
1106             internalPackagePrefix.substring(0, length - 1) :
1107             "";
1108     }
1109 
1110 
1111     /**
1112      * Returns the internal package prefix of the given internal class name.
1113      * @param internalClassName the internal class name,
1114      *                          e.g. "<code>java/lang/Object</code>".
1115      * @return the internal package prefix,
1116      *                          e.g. "<code>java/lang/</code>".
1117      */
internalPackagePrefix(String internalClassName)1118     public static String internalPackagePrefix(String internalClassName)
1119     {
1120         return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
1121                                                                             internalClassName.length() - 2) + 1);
1122     }
1123 
1124 
1125     /**
1126      * Returns the external package name of the given external class name.
1127      * @param externalClassName the external class name,
1128      *                          e.g. "<code>java.lang.Object</code>".
1129      * @return the external package name,
1130      *                          e.g. "<code>java.lang</code>".
1131      */
externalPackageName(String externalClassName)1132     public static String externalPackageName(String externalClassName)
1133     {
1134         String externalPackagePrefix = externalPackagePrefix(externalClassName);
1135         int length = externalPackagePrefix.length();
1136         return length > 0 ?
1137             externalPackagePrefix.substring(0, length - 1) :
1138             "";
1139     }
1140 
1141 
1142     /**
1143      * Returns the external package prefix of the given external class name.
1144      * @param externalClassName the external class name,
1145      *                          e.g. "<code>java.lang.Object</code>".
1146      * @return the external package prefix,
1147      *                          e.g. "<code>java.lang.</code>".
1148      */
externalPackagePrefix(String externalClassName)1149     public static String externalPackagePrefix(String externalClassName)
1150     {
1151         return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR,
1152                                                                             externalClassName.length() - 2) + 1);
1153     }
1154 }
1155