/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2014 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.*; 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(JavaConstants.CLASS_VERSION_1_0) || classVersion.equals(JavaConstants.CLASS_VERSION_1_1) ? ClassConstants.CLASS_VERSION_1_0 : classVersion.equals(JavaConstants.CLASS_VERSION_1_2) ? ClassConstants.CLASS_VERSION_1_2 : classVersion.equals(JavaConstants.CLASS_VERSION_1_3) ? ClassConstants.CLASS_VERSION_1_3 : classVersion.equals(JavaConstants.CLASS_VERSION_1_4) ? ClassConstants.CLASS_VERSION_1_4 : classVersion.equals(JavaConstants.CLASS_VERSION_1_5_ALIAS) || classVersion.equals(JavaConstants.CLASS_VERSION_1_5) ? ClassConstants.CLASS_VERSION_1_5 : classVersion.equals(JavaConstants.CLASS_VERSION_1_6_ALIAS) || classVersion.equals(JavaConstants.CLASS_VERSION_1_6) ? ClassConstants.CLASS_VERSION_1_6 : classVersion.equals(JavaConstants.CLASS_VERSION_1_7_ALIAS) || classVersion.equals(JavaConstants.CLASS_VERSION_1_7) ? ClassConstants.CLASS_VERSION_1_7 : classVersion.equals(JavaConstants.CLASS_VERSION_1_8_ALIAS) || classVersion.equals(JavaConstants.CLASS_VERSION_1_8) ? ClassConstants.CLASS_VERSION_1_8 : 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.CLASS_VERSION_1_0: return JavaConstants.CLASS_VERSION_1_0; case ClassConstants.CLASS_VERSION_1_2: return JavaConstants.CLASS_VERSION_1_2; case ClassConstants.CLASS_VERSION_1_3: return JavaConstants.CLASS_VERSION_1_3; case ClassConstants.CLASS_VERSION_1_4: return JavaConstants.CLASS_VERSION_1_4; case ClassConstants.CLASS_VERSION_1_5: return JavaConstants.CLASS_VERSION_1_5; case ClassConstants.CLASS_VERSION_1_6: return JavaConstants.CLASS_VERSION_1_6; case ClassConstants.CLASS_VERSION_1_7: return JavaConstants.CLASS_VERSION_1_7; case ClassConstants.CLASS_VERSION_1_8: return JavaConstants.CLASS_VERSION_1_8; 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.CLASS_VERSION_1_0 || classVersion > ClassConstants.CLASS_VERSION_1_8) { throw new UnsupportedOperationException("Unsupported class version number ["+ internalMajorClassVersion(classVersion)+"."+ internalMinorClassVersion(classVersion)+"] (maximum "+ ClassConstants.CLASS_VERSION_1_8_MAJOR+"."+ ClassConstants.CLASS_VERSION_1_8_MINOR+", Java "+ JavaConstants.CLASS_VERSION_1_8+")"); } } /** * Converts an external class name into an internal class name. * @param externalClassName the external class name, * e.g. "java.lang.Object" * @return the internal class name, * e.g. "java/lang/Object". */ public static String internalClassName(String externalClassName) { return externalClassName.replace(JavaConstants.PACKAGE_SEPARATOR, ClassConstants.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. "java/lang/Object". * @return the external class description, * e.g. "public java.lang.Object". */ 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. "java/lang/Object". * @return the external class name, * e.g. "java.lang.Object". */ public static String externalClassName(String internalClassName) { return //internalClassName.startsWith(ClassConstants.PACKAGE_JAVA_LANG) && //internalClassName.indexOf(ClassConstants.PACKAGE_SEPARATOR, ClassConstants.PACKAGE_JAVA_LANG.length() + 1) < 0 ? //internalClassName.substring(ClassConstants.PACKAGE_JAVA_LANG.length()) : internalClassName.replace(ClassConstants.PACKAGE_SEPARATOR, JavaConstants.PACKAGE_SEPARATOR); } /** * Returns the external base type of an external array type, dropping any * array brackets. * @param externalArrayType the external array type, * e.g. "java.lang.Object[][]" * @return the external base type, * e.g. "java.lang.Object". */ public static String externalBaseType(String externalArrayType) { int index = externalArrayType.indexOf(JavaConstants.TYPE_ARRAY); return index >= 0 ? externalArrayType.substring(0, index) : externalArrayType; } /** * Returns the external short class name of an external class name, dropping * the package specification. * @param externalClassName the external class name, * e.g. "java.lang.Object" * @return the external short class name, * e.g. "Object". */ public static String externalShortClassName(String externalClassName) { int index = externalClassName.lastIndexOf(JavaConstants.PACKAGE_SEPARATOR); return externalClassName.substring(index+1); } /** * Returns whether the given internal type is an array type. * @param internalType the internal type, * e.g. "[[Ljava/lang/Object;". * @return true if the given type is an array type, * false otherwise. */ public static boolean isInternalArrayType(String internalType) { return internalType.length() > 1 && internalType.charAt(0) == ClassConstants.TYPE_ARRAY; } /** * Returns the number of dimensions of the given internal type. * @param internalType the internal type, * e.g. "[[Ljava/lang/Object;". * @return the number of dimensions, e.g. 2. */ public static int internalArrayTypeDimensionCount(String internalType) { int dimensions = 0; while (internalType.charAt(dimensions) == ClassConstants.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 * "java/lang/Object", "java/lang/Cloneable", and * "java/io/Serializable" * @param internalClassName the internal class name, * e.g. "java/lang/Object". * @return true if the given type is an array interface name, * false otherwise. */ public static boolean isInternalArrayInterfaceName(String internalClassName) { return ClassConstants.NAME_JAVA_LANG_OBJECT.equals(internalClassName) || ClassConstants.NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) || ClassConstants.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. "I". * @return true if the given type is a class type, * false otherwise. */ public static boolean isInternalPrimitiveType(char internalType) { return internalType == ClassConstants.TYPE_BOOLEAN || internalType == ClassConstants.TYPE_BYTE || internalType == ClassConstants.TYPE_CHAR || internalType == ClassConstants.TYPE_SHORT || internalType == ClassConstants.TYPE_INT || internalType == ClassConstants.TYPE_FLOAT || internalType == ClassConstants.TYPE_LONG || internalType == ClassConstants.TYPE_DOUBLE; } /** * Returns whether the given internal type is a primitive Category 2 type. * @param internalType the internal type, * e.g. "L". * @return true if the given type is a Category 2 type, * false otherwise. */ public static boolean isInternalCategory2Type(String internalType) { return internalType.length() == 1 && (internalType.charAt(0) == ClassConstants.TYPE_LONG || internalType.charAt(0) == ClassConstants.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. "Ljava/lang/Object;". * @return true if the given type is a class type, * false otherwise. */ public static boolean isInternalClassType(String internalType) { int length = internalType.length(); return length > 1 && // internalType.charAt(0) == ClassConstants.TYPE_CLASS_START && internalType.charAt(length-1) == ClassConstants.TYPE_CLASS_END; } /** * Returns the internal type of a given class name. * @param internalClassName the internal class name, * e.g. "java/lang/Object". * @return the internal type, * e.g. "Ljava/lang/Object;". */ 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. "java/lang/Object". * @param dimensionCount the number of array dimensions. * @return the internal array type of the array elements, * e.g. "Ljava/lang/Object;". */ 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.TYPE_ARRAY); } return buffer.append(ClassConstants.TYPE_CLASS_START) .append(internalClassName) .append(ClassConstants.TYPE_CLASS_END) .toString(); } /** * Returns the internal element type of a given internal array type. * @param internalArrayType the internal array type, * e.g. "[[Ljava/lang/Object;" or * "[I". * @return the internal type of the array elements, * e.g. "Ljava/lang/Object;" or * "I". */ public static String internalTypeFromArrayType(String internalArrayType) { int index = internalArrayType.lastIndexOf(ClassConstants.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. "[Ljava/lang/Object;", * "Ljava/lang/Object;", or * "java/lang/Object". * @return the internal class name, * e.g. "java/lang/Object". */ public static String internalClassNameFromClassType(String internalClassType) { return isInternalClassType(internalClassType) ? internalClassType.substring(internalClassType.indexOf(ClassConstants.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. "Ljava/lang/Object;" or * "[[I". * @return the internal class name, * e.g. "java/lang/Object" or * null. */ 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 whether the given method name refers to a class initializer or * an instance initializer. * @param internalMethodName the internal method name, * e.g. "<clinit>". * @return whether the method name refers to an initializer, * e.g. true. */ public static boolean isInitializer(String internalMethodName) { return internalMethodName.equals(ClassConstants.METHOD_NAME_CLINIT) || internalMethodName.equals(ClassConstants.METHOD_NAME_INIT); } /** * Returns the internal type of the given internal method descriptor. * @param internalMethodDescriptor the internal method descriptor, * e.g. "(II)Z". * @return the internal return type, * e.g. "Z". */ public static String internalMethodReturnType(String internalMethodDescriptor) { int index = internalMethodDescriptor.indexOf(ClassConstants.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. "(ID)Z". * @return the number of parameters, * e.g. 2. */ public static int internalMethodParameterCount(String internalMethodDescriptor) { int counter = 0; int index = 1; while (true) { char c = internalMethodDescriptor.charAt(index++); switch (c) { case ClassConstants.TYPE_ARRAY: { // Just ignore all array characters. break; } case ClassConstants.TYPE_CLASS_START: { counter++; // Skip the class name. index = internalMethodDescriptor.indexOf(ClassConstants.TYPE_CLASS_END, index) + 1; break; } default: { counter++; break; } case ClassConstants.METHOD_ARGUMENTS_CLOSE: { 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. "(ID)Z". * @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. "(ID)Z". * @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.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. "(ID)Z". * @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) { int size = isStatic ? 0 : 1; int index = 1; while (true) { char c = internalMethodDescriptor.charAt(index++); switch (c) { case ClassConstants.TYPE_LONG: case ClassConstants.TYPE_DOUBLE: { size += 2; break; } case ClassConstants.TYPE_CLASS_START: { size++; // Skip the class name. index = internalMethodDescriptor.indexOf(ClassConstants.TYPE_CLASS_END, index) + 1; break; } case ClassConstants.TYPE_ARRAY: { size++; // Skip all array characters. while ((c = internalMethodDescriptor.charAt(index++)) == ClassConstants.TYPE_ARRAY) {} if (c == ClassConstants.TYPE_CLASS_START) { // Skip the class type. index = internalMethodDescriptor.indexOf(ClassConstants.TYPE_CLASS_END, index) + 1; } break; } default: { size++; break; } case ClassConstants.METHOD_ARGUMENTS_CLOSE: { 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. "I". * @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.TYPE_LONG || internalPrimitiveType == ClassConstants.TYPE_DOUBLE) { return 2; } else if (internalPrimitiveType == ClassConstants.TYPE_VOID) { return 0; } } return 1; } /** * Converts an external type into an internal type. * @param externalType the external type, * e.g. "java.lang.Object[][]" or * "int[]". * @return the internal type, * e.g. "[[Ljava/lang/Object;" or * "[I". */ 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 * JavaConstants.TYPE_ARRAY.length()); } // Analyze the actual type part. char internalTypeChar = externalType.equals(JavaConstants.TYPE_VOID ) ? ClassConstants.TYPE_VOID : externalType.equals(JavaConstants.TYPE_BOOLEAN) ? ClassConstants.TYPE_BOOLEAN : externalType.equals(JavaConstants.TYPE_BYTE ) ? ClassConstants.TYPE_BYTE : externalType.equals(JavaConstants.TYPE_CHAR ) ? ClassConstants.TYPE_CHAR : externalType.equals(JavaConstants.TYPE_SHORT ) ? ClassConstants.TYPE_SHORT : externalType.equals(JavaConstants.TYPE_INT ) ? ClassConstants.TYPE_INT : externalType.equals(JavaConstants.TYPE_FLOAT ) ? ClassConstants.TYPE_FLOAT : externalType.equals(JavaConstants.TYPE_LONG ) ? ClassConstants.TYPE_LONG : externalType.equals(JavaConstants.TYPE_DOUBLE ) ? ClassConstants.TYPE_DOUBLE : externalType.equals("%" ) ? '%' : (char)0; String internalType = internalTypeChar != 0 ? String.valueOf(internalTypeChar) : ClassConstants.TYPE_CLASS_START + internalClassName(externalType) + ClassConstants.TYPE_CLASS_END; // Prepend the array part, if any. for (int count = 0; count < dimensionCount; count++) { internalType = ClassConstants.TYPE_ARRAY + internalType; } return internalType; } /** * Returns the number of dimensions of the given external type. * @param externalType the external type, * e.g. "[[Ljava/lang/Object;". * @return the number of dimensions, e.g. 2. */ public static int externalArrayTypeDimensionCount(String externalType) { int dimensions = 0; int length = JavaConstants.TYPE_ARRAY.length(); int offset = externalType.length() - length; while (externalType.regionMatches(offset, JavaConstants.TYPE_ARRAY, 0, length)) { dimensions++; offset -= length; } return dimensions; } /** * Converts an internal type into an external type. * @param internalType the internal type, * e.g. "[[Ljava/lang/Object;" or * "[I". * @return the external type, * e.g. "java.lang.Object[][]" or * "int[]". */ 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.TYPE_VOID ? JavaConstants.TYPE_VOID : internalTypeChar == ClassConstants.TYPE_BOOLEAN ? JavaConstants.TYPE_BOOLEAN : internalTypeChar == ClassConstants.TYPE_BYTE ? JavaConstants.TYPE_BYTE : internalTypeChar == ClassConstants.TYPE_CHAR ? JavaConstants.TYPE_CHAR : internalTypeChar == ClassConstants.TYPE_SHORT ? JavaConstants.TYPE_SHORT : internalTypeChar == ClassConstants.TYPE_INT ? JavaConstants.TYPE_INT : internalTypeChar == ClassConstants.TYPE_FLOAT ? JavaConstants.TYPE_FLOAT : internalTypeChar == ClassConstants.TYPE_LONG ? JavaConstants.TYPE_LONG : internalTypeChar == ClassConstants.TYPE_DOUBLE ? JavaConstants.TYPE_DOUBLE : internalTypeChar == '%' ? "%" : internalTypeChar == ClassConstants.TYPE_CLASS_START ? externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.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 += JavaConstants.TYPE_ARRAY; } return externalType; } /** * Returns whether the given internal descriptor String represents a method * descriptor. * @param internalDescriptor the internal descriptor String, * e.g. "(II)Z". * @return true if the given String is a method descriptor, * false otherwise. */ public static boolean isInternalMethodDescriptor(String internalDescriptor) { return internalDescriptor.charAt(0) == ClassConstants.METHOD_ARGUMENTS_OPEN; } /** * Returns whether the given member String represents an external method * name with arguments. * @param externalMemberNameAndArguments the external member String, * e.g. "myField" or * e.g. "myMethod(int,int)". * @return true if the given String refers to a method, * false otherwise. */ public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) { return externalMemberNameAndArguments.indexOf(JavaConstants.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. "myMethod(int,int)". * @return the name part of the String, e.g. "myMethod". */ 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. "boolean". * @param externalMethodNameAndArguments the external method name and arguments, * e.g. "myMethod(int,int)". * @return the internal method descriptor, * e.g. "(II)Z". */ public static String internalMethodDescriptor(String externalReturnType, String externalMethodNameAndArguments) { StringBuffer internalMethodDescriptor = new StringBuffer(); internalMethodDescriptor.append(ClassConstants.METHOD_ARGUMENTS_OPEN); ExternalTypeEnumeration externalTypeEnumeration = new ExternalTypeEnumeration(externalMethodNameAndArguments); while (externalTypeEnumeration.hasMoreTypes()) { internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); } internalMethodDescriptor.append(ClassConstants.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. "boolean". * @param externalArguments the external method arguments, * e.g. { "int", "int" }. * @return the internal method descriptor, * e.g. "(II)Z". */ public static String internalMethodDescriptor(String externalReturnType, List externalArguments) { StringBuffer internalMethodDescriptor = new StringBuffer(); internalMethodDescriptor.append(ClassConstants.METHOD_ARGUMENTS_OPEN); for (int index = 0; index < externalArguments.size(); index++) { internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); } internalMethodDescriptor.append(ClassConstants.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. "myField". * @param internalFieldDescriptor the internal field descriptor, * e.g. "Z". * @return the external full field description, * e.g. "public boolean myField". */ 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. "mypackage/MyClass". * @param accessFlags the access flags of the method. * @param internalMethodName the internal method name, * e.g. "myMethod" or * "<init>". * @param internalMethodDescriptor the internal method descriptor, * e.g. "(II)Z". * @return the external full method description, * e.g. "public boolean myMethod(int,int)" or * "public MyClass(int,int)". */ public static String externalFullMethodDescription(String internalClassName, int accessFlags, String internalMethodName, String internalMethodDescriptor) { return externalMethodAccessFlags(accessFlags) + externalMethodReturnTypeAndName(internalClassName, internalMethodName, internalMethodDescriptor) + JavaConstants.METHOD_ARGUMENTS_OPEN + externalMethodArguments(internalMethodDescriptor) + JavaConstants.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. "public final ". */ 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. "public final ". */ public static String externalClassAccessFlags(int accessFlags, String prefix) { if (accessFlags == 0) { return EMPTY_STRING; } StringBuffer string = new StringBuffer(50); if ((accessFlags & ClassConstants.ACC_PUBLIC) != 0) { string.append(prefix).append(JavaConstants.ACC_PUBLIC).append(' '); } if ((accessFlags & ClassConstants.ACC_PRIVATE) != 0) { // Only in InnerClasses attributes. string.append(prefix).append(JavaConstants.ACC_PRIVATE).append(' '); } if ((accessFlags & ClassConstants.ACC_PROTECTED) != 0) { // Only in InnerClasses attributes. string.append(prefix).append(JavaConstants.ACC_PROTECTED).append(' '); } if ((accessFlags & ClassConstants.ACC_STATIC) != 0) { // Only in InnerClasses attributes. string.append(prefix).append(JavaConstants.ACC_STATIC).append(' '); } if ((accessFlags & ClassConstants.ACC_FINAL) != 0) { string.append(prefix).append(JavaConstants.ACC_FINAL).append(' '); } if ((accessFlags & ClassConstants.ACC_ANNOTATTION) != 0) { string.append(prefix).append(JavaConstants.ACC_ANNOTATION); } if ((accessFlags & ClassConstants.ACC_INTERFACE) != 0) { string.append(prefix).append(JavaConstants.ACC_INTERFACE).append(' '); } else if ((accessFlags & ClassConstants.ACC_ENUM) != 0) { string.append(prefix).append(JavaConstants.ACC_ENUM).append(' '); } else if ((accessFlags & ClassConstants.ACC_ABSTRACT) != 0) { string.append(prefix).append(JavaConstants.ACC_ABSTRACT).append(' '); } else if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).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. "public volatile ". */ 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. "public volatile ". */ public static String externalFieldAccessFlags(int accessFlags, String prefix) { if (accessFlags == 0) { return EMPTY_STRING; } StringBuffer string = new StringBuffer(50); if ((accessFlags & ClassConstants.ACC_PUBLIC) != 0) { string.append(prefix).append(JavaConstants.ACC_PUBLIC).append(' '); } if ((accessFlags & ClassConstants.ACC_PRIVATE) != 0) { string.append(prefix).append(JavaConstants.ACC_PRIVATE).append(' '); } if ((accessFlags & ClassConstants.ACC_PROTECTED) != 0) { string.append(prefix).append(JavaConstants.ACC_PROTECTED).append(' '); } if ((accessFlags & ClassConstants.ACC_STATIC) != 0) { string.append(prefix).append(JavaConstants.ACC_STATIC).append(' '); } if ((accessFlags & ClassConstants.ACC_FINAL) != 0) { string.append(prefix).append(JavaConstants.ACC_FINAL).append(' '); } if ((accessFlags & ClassConstants.ACC_VOLATILE) != 0) { string.append(prefix).append(JavaConstants.ACC_VOLATILE).append(' '); } if ((accessFlags & ClassConstants.ACC_TRANSIENT) != 0) { string.append(prefix).append(JavaConstants.ACC_TRANSIENT).append(' '); } if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).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. "public synchronized ". */ 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.ACC_PUBLIC) != 0) { string.append(prefix).append(JavaConstants.ACC_PUBLIC).append(' '); } if ((accessFlags & ClassConstants.ACC_PRIVATE) != 0) { string.append(prefix).append(JavaConstants.ACC_PRIVATE).append(' '); } if ((accessFlags & ClassConstants.ACC_PROTECTED) != 0) { string.append(prefix).append(JavaConstants.ACC_PROTECTED).append(' '); } if ((accessFlags & ClassConstants.ACC_STATIC) != 0) { string.append(prefix).append(JavaConstants.ACC_STATIC).append(' '); } if ((accessFlags & ClassConstants.ACC_FINAL) != 0) { string.append(prefix).append(JavaConstants.ACC_FINAL).append(' '); } if ((accessFlags & ClassConstants.ACC_SYNCHRONIZED) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNCHRONIZED).append(' '); } if ((accessFlags & ClassConstants.ACC_BRIDGE) != 0) { string.append(prefix).append(JavaConstants.ACC_BRIDGE).append(' '); } if ((accessFlags & ClassConstants.ACC_VARARGS) != 0) { string.append(prefix).append(JavaConstants.ACC_VARARGS).append(' '); } if ((accessFlags & ClassConstants.ACC_NATIVE) != 0) { string.append(prefix).append(JavaConstants.ACC_NATIVE).append(' '); } if ((accessFlags & ClassConstants.ACC_ABSTRACT) != 0) { string.append(prefix).append(JavaConstants.ACC_ABSTRACT).append(' '); } if ((accessFlags & ClassConstants.ACC_STRICT) != 0) { string.append(prefix).append(JavaConstants.ACC_STRICT).append(' '); } if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); } return string.toString(); } /** * Converts internal method parameter access flags into an external access * description. * @param accessFlags the method parameter access flags. * @return the external method parameter access description, * e.g. "final mandated ". */ public static String externalParameterAccessFlags(int accessFlags) { return externalParameterAccessFlags(accessFlags, ""); } /** * Converts internal method parameter access flags into an external access * description. * @param accessFlags the method parameter access flags. * @param prefix a prefix that is added to each access modifier. * @return the external method parameter access description, * e.g. "final mandated ". */ public static String externalParameterAccessFlags(int accessFlags, String prefix) { if (accessFlags == 0) { return EMPTY_STRING; } StringBuffer string = new StringBuffer(50); if ((accessFlags & ClassConstants.ACC_FINAL) != 0) { string.append(prefix).append(JavaConstants.ACC_FINAL).append(' '); } if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); } if ((accessFlags & ClassConstants.ACC_MANDATED) != 0) { string.append(prefix).append(JavaConstants.ACC_MANDATED).append(' '); } return string.toString(); } /** * Converts an internal method descriptor into an external method return type. * @param internalMethodDescriptor the internal method descriptor, * e.g. "(II)Z". * @return the external method return type, * e.g. "boolean". */ 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. "mypackage/MyClass". * @param internalMethodName the internal method name, * e.g. "myMethod" or * "<init>". * @param internalMethodDescriptor the internal method descriptor, * e.g. "(II)Z". * @return the external method return type and name, * e.g. "boolean myMethod" or * "MyClass". */ private static String externalMethodReturnTypeAndName(String internalClassName, String internalMethodName, String internalMethodDescriptor) { return internalMethodName.equals(ClassConstants.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. "(II)Z". * @return the external method argument description, * e.g. "int,int". */ 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(JavaConstants.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. "java/lang/Object". * @return the internal package name, * e.g. "java/lang". */ 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. "java/lang/Object". * @return the internal package prefix, * e.g. "java/lang/". */ public static String internalPackagePrefix(String internalClassName) { return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.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. "java.lang.Object". * @return the external package name, * e.g. "java.lang". */ 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. "java.lang.Object". * @return the external package prefix, * e.g. "java.lang.". */ public static String externalPackagePrefix(String externalClassName) { return externalClassName.substring(0, externalClassName.lastIndexOf(JavaConstants.PACKAGE_SEPARATOR, externalClassName.length() - 2) + 1); } }