package net.sourceforge.javautil.bytecode.api;
import net.sourceforge.javautil.bytecode.BytecodeException;
/**
* This represents a type descriptor.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class TypeDescriptor implements Comparable<TypeDescriptor> {
public static final TypeDescriptor BOOLEAN = new TypeDescriptor("Z", true);
public static final TypeDescriptor CHAR = new TypeDescriptor("C", true);
public static final TypeDescriptor BYTE = new TypeDescriptor("B", true);
public static final TypeDescriptor SHORT = new TypeDescriptor("S", true);
public static final TypeDescriptor INTEGER = new TypeDescriptor("I", true);
public static final TypeDescriptor FLOAT = new TypeDescriptor("F", true);
public static final TypeDescriptor LONG = new TypeDescriptor("J", true);
public static final TypeDescriptor DOUBLE = new TypeDescriptor("D", true);
public static final TypeDescriptor VOID = new TypeDescriptor("V", true);
public static final TypeDescriptor OBJECT = TypeDescriptor.getFor(Object.class);
public static final TypeDescriptor STRING = TypeDescriptor.getFor(String.class);
public static TypeDescriptor getFor (Class clazz) {
if (clazz.isPrimitive()) {
if (clazz == boolean.class) return BOOLEAN;
if (clazz == char.class) return CHAR;
if (clazz == byte.class) return BYTE;
if (clazz == short.class) return SHORT;
if (clazz == int.class) return INTEGER;
if (clazz == float.class) return FLOAT;
if (clazz == long.class) return LONG;
if (clazz == double.class) return DOUBLE;
if (clazz == void.class) return VOID;
}
if (clazz.isArray()) {
int indices = 0;
while (clazz.getComponentType() != null) { indices++; clazz = clazz.getComponentType(); }
return getFor(getFor(clazz.getName()), indices);
}
return getFor(clazz.getName());
}
public static TypeDescriptor[] getFor (Class... classes) {
TypeDescriptor[] types = new TypeDescriptor[classes.length];
for (int i=0; i<classes.length; i++)
types[i] = getFor(classes[i]);
return types;
}
public static TypeDescriptor getFor (String className) {
return new TypeDescriptor(className.replace('.', '/'));
}
public static TypeDescriptor[] getFor (String... classNames) {
TypeDescriptor[] types = new TypeDescriptor[classNames.length];
for (int i=0; i<classNames.length; i++)
types[i] = getFor(classNames[i]);
return types;
}
public static TypeDescriptor getFor (TypeDescriptor rootType, int indices) {
return new TypeDescriptor(rootType, indices);
}
public static String toMethodDescriptor (TypeDescriptor returnType, TypeDescriptor... parameters) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (int i=0; i<parameters.length; i++) {
parameters[i].appendDescriptorString(sb);
}
sb.append(")");
if (returnType == null)
TypeDescriptor.VOID.appendDescriptorString(sb);
else
returnType.appendDescriptorString(sb);
return sb.toString();
}
protected final String name;
protected final boolean primitive;
protected final int arrayIndices;
protected final TypeDescriptor rootComponentType;
protected String descriptor;
private TypeDescriptor(String name) {
this.name = name;
this.primitive = false;
this.arrayIndices = -1;
this.rootComponentType = this;
}
private TypeDescriptor(TypeDescriptor rootType, int arrayIndices) {
while (rootType.isArray()) {
arrayIndices += rootType.arrayIndices;
rootType = rootType.rootComponentType;
}
this.rootComponentType = rootType;
this.name = rootType.name;
this.primitive = false;
this.arrayIndices = arrayIndices;
}
private TypeDescriptor(String name, boolean primitive) {
this.name = name;
this.primitive = primitive;
this.arrayIndices = -1;
this.rootComponentType = this;
}
/**
* @param indices The indices
* @return The array version of the basic component type
*/
public TypeDescriptor getArrayType (int indices) {
return new TypeDescriptor(rootComponentType, indices + this.arrayIndices);
}
/**
* @return The valid java class name
*/
public String getClassName () {
if (this.primitive) {
if (this == TypeDescriptor.BOOLEAN) return boolean.class.getName();
else if (this == TypeDescriptor.BYTE) return byte.class.getName();
else if (this == TypeDescriptor.CHAR) return char.class.getName();
else if (this == TypeDescriptor.DOUBLE) return double.class.getName();
else if (this == TypeDescriptor.FLOAT) return float.class.getName();
else if (this == TypeDescriptor.INTEGER) return int.class.getName();
else if (this == TypeDescriptor.LONG) return long.class.getName();
else if (this == TypeDescriptor.SHORT) return short.class.getName();
else if (this == TypeDescriptor.VOID) return void.class.getName();
}
return this.name.replace('/', '.');
}
/**
* @return The internal JVM name of this type.
*/
public String getName() { return name; }
/**
* @return True if this type represents a primitive
*/
public boolean isPrimitive() { return primitive; }
/**
* @return If {@link #isPrimitive()} is true, the boxed type, otherwise an exception is thrown
*/
public TypeDescriptor getBoxedType () {
if (this.primitive) {
if (this == INTEGER) return getFor(Integer.class);
if (this == LONG) return getFor(Long.class);
if (this == FLOAT) return getFor(Float.class);
if (this == DOUBLE) return getFor(Double.class);
if (this == SHORT) return getFor(Short.class);
if (this == BYTE) return getFor(Byte.class);
if (this == BOOLEAN) return getFor(Boolean.class);
if (this == CHAR) return getFor(Character.class);
}
throw new BytecodeException("Cannot get a boxed type for a non-primitive");
}
/**
* @return If {@link #isPrimitive()} is false and the type is a boxed type the unboxed type is returned, otherwise an exception is thrown
*/
public TypeDescriptor getUnboxedType () {
if (!this.primitive) {
if (this.name.equals("java/lang/Integer")) return INTEGER;
if (this.name.equals("java/lang/Long")) return LONG;
if (this.name.equals("java/lang/Float")) return FLOAT;
if (this.name.equals("java/lang/Double")) return DOUBLE;
if (this.name.equals("java/lang/Short")) return SHORT;
if (this.name.equals("java/lang/Byte")) return BYTE;
if (this.name.equals("java/lang/Boolean")) return BOOLEAN;
if (this.name.equals("java/lang/Character")) return CHAR;
}
throw new BytecodeException("Cannot get an unboxed type for a non primitive boxed type");
}
/**
* @return True if this is an array, otherwise false
*/
public boolean isArray() { return arrayIndices > 0; }
/**
* @return The indices of the array, or -1 if this is not an array
*/
public int getIndices() { return arrayIndices; }
/**
* @return The component type descriptor if this is an array, otherwise null
*/
public TypeDescriptor getComponentType () {
if (this.arrayIndices < 1) return null;
return this.arrayIndices == 1 ? new TypeDescriptor(name) : new TypeDescriptor(this.rootComponentType, arrayIndices - 1);
}
@Override public boolean equals(Object obj) {
return obj instanceof TypeDescriptor ? this.compareTo((TypeDescriptor)obj) == 0 : false;
}
public int compareTo(TypeDescriptor o) {
return name.compareTo(o.name);
}
/**
* @return A valid internal JVM descriptor for this type
*/
public String toDescriptorString () {
if (this.primitive) { return this.name; }
if (this.descriptor == null) {
this.descriptor = this.appendDescriptorString(new StringBuilder()).toString();
}
return descriptor;
}
/**
* This allows for efficiency when this descriptor is part of a larger
* appending operation.
*
* @param sb The builder to append this descriptor to
* @return The same builder for chaining
*/
public StringBuilder appendDescriptorString (StringBuilder sb) {
if (this.descriptor != null) { return sb.append(descriptor); }
if (this.primitive) { sb.append(name); }
else {
if (this.arrayIndices > 0) {
for (int i=0; i<this.arrayIndices; i++)
sb.append("[");
}
if (this.rootComponentType.isPrimitive()) sb.append(this.rootComponentType.name);
else sb.append("L").append(this.rootComponentType.name);
sb.append(";");
}
return sb;
}
}