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