diff options
author | Joe Onorato <joeo@android.com> | 2009-08-31 10:12:00 -0700 |
---|---|---|
committer | Joe Onorato <joeo@android.com> | 2009-08-31 10:12:00 -0700 |
commit | b72c5c2e5482cf10117b2b25f642f7616b2326c3 (patch) | |
tree | f02ba1bc29f4fe6853d9b7008eed37cdcfb96e81 /src/proguard/classfile/util/ClassUtil.java | |
parent | a23344a828357fe4b6596f8af5fed467d72757ab (diff) | |
download | proguard-b72c5c2e5482cf10117b2b25f642f7616b2326c3.tar.gz |
ProGuard 4.4android-sdk-tools_r4android-sdk-tools_r3android-sdk-2.1_r1android-sdk-2.0_r1android-sdk-2.0.1_r1android-sdk-2.0.1-docs_r1android-2.1_r2.1sandroid-2.1_r2.1p2android-2.1_r2.1pandroid-2.1_r2android-2.1_r1android-2.0_r1android-2.0.1_r1eclair-sholes-release2eclair-sholes-releaseeclair-releaseeclair-passion-release
Diffstat (limited to 'src/proguard/classfile/util/ClassUtil.java')
-rw-r--r-- | src/proguard/classfile/util/ClassUtil.java | 1154 |
1 files changed, 1154 insertions, 0 deletions
diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java new file mode 100644 index 0000000..5f25f01 --- /dev/null +++ b/src/proguard/classfile/util/ClassUtil.java @@ -0,0 +1,1154 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.util; + +import proguard.classfile.ClassConstants; + +import java.util.List; + +/** + * Utility methods for converting between internal and external representations + * of names and descriptions. + * + * @author Eric Lafortune + */ +public class ClassUtil +{ + private static final String EMPTY_STRING = ""; + + + /** + * Checks whether the given class magic number is correct. + * @param magicNumber the magic number. + * @throws UnsupportedOperationException when the magic number is incorrect. + */ + public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException + { + if (magicNumber != ClassConstants.MAGIC) + { + throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class"); + } + } + + + /** + * Returns the combined class version number. + * @param majorVersion the major part of the class version number. + * @param minorVersion the minor part of the class version number. + * @return the combined class version number. + */ + public static int internalClassVersion(int majorVersion, int minorVersion) + { + return (majorVersion << 16) | minorVersion; + } + + + /** + * Returns the major part of the given class version number. + * @param classVersion the combined class version number. + * @return the major part of the class version number. + */ + public static int internalMajorClassVersion(int classVersion) + { + return classVersion >>> 16; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalMinorClassVersion(int classVersion) + { + return classVersion & 0xffff; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalClassVersion(String classVersion) + { + return + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 : + 0; + } + + + /** + * Returns the minor part of the given class version number. + * @param classVersion the combined class version number. + * @return the minor part of the class version number. + */ + public static String externalClassVersion(int classVersion) + { + switch (classVersion) + { + case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0; + case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2; + case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3; + case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4; + case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5; + case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6; + default: return null; + } + } + + + /** + * Checks whether the given class version number is supported. + * @param classVersion the combined class version number. + * @throws UnsupportedOperationException when the version is not supported. + */ + public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException + { + if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 || + classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6) + { + throw new UnsupportedOperationException("Unsupported version number ["+ + internalMajorClassVersion(classVersion)+"."+ + internalMinorClassVersion(classVersion)+"] for class format"); + } + } + + + /** + * Converts an external class name into an internal class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassName(String externalClassName) + { + return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class description into an external class description. + * @param accessFlags the access flags of the class. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class description, + * e.g. "<code>public java.lang.Object</code>". + */ + public static String externalFullClassDescription(int accessFlags, + String internalClassName) + { + return externalClassAccessFlags(accessFlags) + + externalClassName(internalClassName); + } + + + /** + * Converts an internal class name into an external class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class name, + * e.g. "<code>java.lang.Object</code>". + */ + public static String externalClassName(String internalClassName) + { + return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) && + //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ? + //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) : + internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class name into an external short class name, without + * package specification. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the external short class name, + * e.g. "<code>Object</code>". + */ + public static String externalShortClassName(String externalClassName) + { + int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + return externalClassName.substring(index+1); + } + + + /** + * Returns whether the given internal type is an array type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is an array type, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayType(String internalType) + { + return internalType.length() > 1 && + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY; + } + + + /** + * Returns the number of dimensions of the given internal type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int internalArrayTypeDimensionCount(String internalType) + { + int dimensions = 0; + while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + dimensions++; + } + + return dimensions; + } + + + /** + * Returns whether the given internal class name is one of the interfaces + * that is implemented by all array types. These class names are + * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and + * "<code>java/io/Serializable</code>" + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return <code>true</code> if the given type is an array interface name, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayInterfaceName(String internalClassName) + { + return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName); + } + + + /** + * Returns whether the given internal type is a plain primitive type + * (not void). + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalPrimitiveType(char internalType) + { + return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN || + internalType == ClassConstants.INTERNAL_TYPE_BYTE || + internalType == ClassConstants.INTERNAL_TYPE_CHAR || + internalType == ClassConstants.INTERNAL_TYPE_SHORT || + internalType == ClassConstants.INTERNAL_TYPE_INT || + internalType == ClassConstants.INTERNAL_TYPE_FLOAT || + internalType == ClassConstants.INTERNAL_TYPE_LONG || + internalType == ClassConstants.INTERNAL_TYPE_DOUBLE; + } + + + /** + * Returns whether the given internal type is a primitive Category 2 type. + * @param internalType the internal type, + * e.g. "<code>L</code>". + * @return <code>true</code> if the given type is a Category 2 type, + * <code>false</code> otherwise. + */ + public static boolean isInternalCategory2Type(String internalType) + { + return internalType.length() == 1 && + (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG || + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE); + } + + + /** + * Returns whether the given internal type is a plain class type + * (including an array type of a plain class type). + * @param internalType the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalClassType(String internalType) + { + int length = internalType.length(); + return length > 1 && +// internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_CLASS_START && + internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END; + } + + + /** + * Returns the internal type of a given class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalTypeFromClassName(String internalClassName) + { + return internalArrayTypeFromClassName(internalClassName, 0); + } + + + /** + * Returns the internal array type of a given class name with a given number + * of dimensions. If the number of dimensions is 0, the class name itself is + * returned. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @param dimensionCount the number of array dimensions. + * @return the internal array type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalArrayTypeFromClassName(String internalClassName, + int dimensionCount) + { + StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2); + + for (int dimension = 0; dimension < dimensionCount; dimension++) + { + buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + } + + return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START) + .append(internalClassName) + .append(ClassConstants.INTERNAL_TYPE_CLASS_END) + .toString(); + } + + + /** + * Returns the internal element type of a given internal array type. + * @param internalArrayType the internal array type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the internal type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>I</code>". + */ + public static String internalTypeFromArrayType(String internalArrayType) + { + int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY); + return internalArrayType.substring(index+1); + } + + + /** + * Returns the internal class name of a given internal class type + * (including an array type). Types involving primitive types are returned + * unchanged. + * @param internalClassType the internal class type, + * e.g. "<code>[Ljava/lang/Object;</code>", + * "<code>Ljava/lang/Object;</code>", or + * "<code>java/lang/Object</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassNameFromClassType(String internalClassType) + { + return isInternalClassType(internalClassType) ? + internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1, + internalClassType.length()-1) : + internalClassType; + } + + + /** + * Returns the internal class name of any given internal descriptor type, + * disregarding array prefixes. + * @param internalClassType the internal class type, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>[[I</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>" or + * <code>null</code>. + */ + public static String internalClassNameFromType(String internalClassType) + { + if (!isInternalClassType(internalClassType)) + { + return null; + } + + // Is it an array type? + if (isInternalArrayType(internalClassType)) + { + internalClassType = internalTypeFromArrayType(internalClassType); + } + + return internalClassNameFromClassType(internalClassType); + } + + + /** + * Returns the internal type of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the internal return type, + * e.g. "<code>Z</code>". + */ + public static String internalMethodReturnType(String internalMethodDescriptor) + { + int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + return internalMethodDescriptor.substring(index + 1); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the number of parameters, + * e.g. 2. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int counter = 0; + while (internalTypeEnumeration.hasMoreTypes()) + { + internalTypeEnumeration.nextType(); + + counter++; + } + + return counter; + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the size taken up on the stack, + * e.g. 3. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor) + { + return internalMethodParameterSize(internalMethodDescriptor, true); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param accessFlags the access flags of the method, + * e.g. 0. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + int accessFlags) + { + return internalMethodParameterSize(internalMethodDescriptor, + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two spaces, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param isStatic specifies whether the method is static, + * e.g. false. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + boolean isStatic) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int size = isStatic ? 0 : 1; + while (internalTypeEnumeration.hasMoreTypes()) + { + String internalType = internalTypeEnumeration.nextType(); + + size += internalTypeSize(internalType); + } + + return size; + } + + + /** + * Returns the size taken up on the stack by the given internal type. + * The size is 1, except for long and double types, for which it is 2, + * and for the void type, for which 0 is returned. + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return the size taken up on the stack, + * e.g. 1. + */ + public static int internalTypeSize(String internalType) + { + if (internalType.length() == 1) + { + char internalPrimitiveType = internalType.charAt(0); + if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG || + internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE) + { + return 2; + } + else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID) + { + return 0; + } + } + + return 1; + } + + + /** + * Converts an external type into an internal type. + * @param externalType the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + * @return the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + */ + public static String internalType(String externalType) + { + // Strip the array part, if any. + int dimensionCount = externalArrayTypeDimensionCount(externalType); + if (dimensionCount > 0) + { + externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length()); + } + + // Analyze the actual type part. + char internalTypeChar = + externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID ) ? + ClassConstants.INTERNAL_TYPE_VOID : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ? + ClassConstants.INTERNAL_TYPE_BOOLEAN : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE ) ? + ClassConstants.INTERNAL_TYPE_BYTE : + externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR ) ? + ClassConstants.INTERNAL_TYPE_CHAR : + externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT ) ? + ClassConstants.INTERNAL_TYPE_SHORT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_INT ) ? + ClassConstants.INTERNAL_TYPE_INT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT ) ? + ClassConstants.INTERNAL_TYPE_FLOAT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG ) ? + ClassConstants.INTERNAL_TYPE_LONG : + externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ? + ClassConstants.INTERNAL_TYPE_DOUBLE : + externalType.equals("%" ) ? + '%' : + (char)0; + + String internalType = + internalTypeChar != 0 ? String.valueOf(internalTypeChar) : + ClassConstants.INTERNAL_TYPE_CLASS_START + + internalClassName(externalType) + + ClassConstants.INTERNAL_TYPE_CLASS_END; + + // Prepend the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType; + } + + return internalType; + } + + + /** + * Returns the number of dimensions of the given external type. + * @param externalType the external type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int externalArrayTypeDimensionCount(String externalType) + { + int dimensions = 0; + int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length(); + int offset = externalType.length() - length; + while (externalType.regionMatches(offset, + ClassConstants.EXTERNAL_TYPE_ARRAY, + 0, + length)) + { + dimensions++; + offset -= length; + } + + return dimensions; + } + + + /** + * Converts an internal type into an external type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + */ + public static String externalType(String internalType) + { + // Strip the array part, if any. + int dimensionCount = internalArrayTypeDimensionCount(internalType); + if (dimensionCount > 0) + { + internalType = internalType.substring(dimensionCount); + } + + // Analyze the actual type part. + char internalTypeChar = internalType.charAt(0); + + String externalType = + internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID ? + ClassConstants.EXTERNAL_TYPE_VOID : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN ? + ClassConstants.EXTERNAL_TYPE_BOOLEAN : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE ? + ClassConstants.EXTERNAL_TYPE_BYTE : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR ? + ClassConstants.EXTERNAL_TYPE_CHAR : + internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT ? + ClassConstants.EXTERNAL_TYPE_SHORT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_INT ? + ClassConstants.EXTERNAL_TYPE_INT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT ? + ClassConstants.EXTERNAL_TYPE_FLOAT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG ? + ClassConstants.EXTERNAL_TYPE_LONG : + internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE ? + ClassConstants.EXTERNAL_TYPE_DOUBLE : + internalTypeChar == '%' ? + "%" : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ? + externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) : + null; + + if (externalType == null) + { + throw new IllegalArgumentException("Unknown type ["+internalType+"]"); + } + + // Append the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + externalType += ClassConstants.EXTERNAL_TYPE_ARRAY; + } + + return externalType; + } + + + /** + * Returns whether the given internal descriptor String represents a method + * descriptor. + * @param internalDescriptor the internal descriptor String, + * e.g. "<code>(II)Z</code>". + * @return <code>true</code> if the given String is a method descriptor, + * <code>false</code> otherwise. + */ + public static boolean isInternalMethodDescriptor(String internalDescriptor) + { + return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN; + } + + + /** + * Returns whether the given member String represents an external method + * name with arguments. + * @param externalMemberNameAndArguments the external member String, + * e.g. "<code>myField</code>" or + * e.g. "<code>myMethod(int,int)</code>". + * @return <code>true</code> if the given String refers to a method, + * <code>false</code> otherwise. + */ + public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) + { + return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0; + } + + + /** + * Returns the name part of the given external method name and arguments. + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the name part of the String, e.g. "<code>myMethod</code>". + */ + public static String externalMethodName(String externalMethodNameAndArguments) + { + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + return externalTypeEnumeration.methodName(); + } + + + /** + * Converts the given external method return type and name and arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + String externalMethodNameAndArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + while (externalTypeEnumeration.hasMoreTypes()) + { + internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts the given external method return type and List of arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalArguments the external method arguments, + * e.g. <code>{ "int", "int" }</code>. + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + List externalArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + for (int index = 0; index < externalArguments.size(); index++) + { + internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts an internal field description into an external full field description. + * @param accessFlags the access flags of the field. + * @param fieldName the field name, + * e.g. "<code>myField</code>". + * @param internalFieldDescriptor the internal field descriptor, + * e.g. "<code>Z</code>". + * @return the external full field description, + * e.g. "<code>public boolean myField</code>". + */ + public static String externalFullFieldDescription(int accessFlags, + String fieldName, + String internalFieldDescriptor) + { + return externalFieldAccessFlags(accessFlags) + + externalType(internalFieldDescriptor) + + ' ' + + fieldName; + } + + + /** + * Converts an internal method description into an external full method description. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param accessFlags the access flags of the method. + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external full method description, + * e.g. "<code>public boolean myMethod(int,int)</code>" or + * "<code>public MyClass(int,int)</code>". + */ + public static String externalFullMethodDescription(String internalClassName, + int accessFlags, + String internalMethodName, + String internalMethodDescriptor) + { + return externalMethodAccessFlags(accessFlags) + + externalMethodReturnTypeAndName(internalClassName, + internalMethodName, + internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + + externalMethodArguments(internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE; + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags) + { + return externalClassAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags) + { + return externalFieldAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @return the external method access description, + * e.g. "<code>public synchronized </code>". + */ + public static String externalMethodAccessFlags(int accessFlags) + { + return externalMethodAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external method access description, + * e.g. "public synchronized ". + */ + public static String externalMethodAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts an internal method descriptor into an external method return type. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type, + * e.g. "<code>boolean</code>". + */ + public static String externalMethodReturnType(String internalMethodDescriptor) + { + return externalType(internalMethodReturnType(internalMethodDescriptor)); + } + + + /** + * Converts an internal class name, method name, and method descriptor to + * an external method return type and name. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type and name, + * e.g. "<code>boolean myMethod</code>" or + * "<code>MyClass</code>". + */ + private static String externalMethodReturnTypeAndName(String internalClassName, + String internalMethodName, + String internalMethodDescriptor) + { + return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + externalShortClassName(externalClassName(internalClassName)) : + (externalMethodReturnType(internalMethodDescriptor) + + ' ' + + internalMethodName); + } + + + /** + * Converts an internal method descriptor into an external method argument + * description. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method argument description, + * e.g. "<code>int,int</code>". + */ + public static String externalMethodArguments(String internalMethodDescriptor) + { + StringBuffer externalMethodNameAndArguments = new StringBuffer(); + + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + while (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType())); + if (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR); + } + } + + return externalMethodNameAndArguments.toString(); + } + + + /** + * Returns the internal package name of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package name, + * e.g. "<code>java/lang</code>". + */ + public static String internalPackageName(String internalClassName) + { + String internalPackagePrefix = internalPackagePrefix(internalClassName); + int length = internalPackagePrefix.length(); + return length > 0 ? + internalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the internal package prefix of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package prefix, + * e.g. "<code>java/lang/</code>". + */ + public static String internalPackagePrefix(String internalClassName) + { + return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + internalClassName.length() - 2) + 1); + } + + + /** + * Returns the external package name of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package name, + * e.g. "<code>java.lang</code>". + */ + public static String externalPackageName(String externalClassName) + { + String externalPackagePrefix = externalPackagePrefix(externalClassName); + int length = externalPackagePrefix.length(); + return length > 0 ? + externalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the external package prefix of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package prefix, + * e.g. "<code>java.lang.</code>". + */ + public static String externalPackagePrefix(String externalClassName) + { + return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + externalClassName.length() - 2) + 1); + } +} |