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