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