blob: 5f25f013e40eb3d289535b4cfe8786da2145738b [file] [log] [blame]
/*
* 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>&lt;init&gt;</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>&lt;init&gt;</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);
}
}