blob: 0be8e390066ca5102d7511b3d46c43c118bdb06f [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 2002,2005 Contributors
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* PARC initial implementation
* Andy Clement start of generics upgrade...
* Adrian Colyer - overhaul
* ******************************************************************/
package org.aspectj.weaver;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;
import org.aspectj.util.GenericSignature;
import org.aspectj.util.GenericSignature.ClassSignature;
import org.aspectj.util.GenericSignatureParser;
import org.aspectj.weaver.tools.Traceable;
/**
* A UnresolvedType represents a type to the weaver. UnresolvedTypes are resolved in some World (a type repository). When a
* UnresolvedType is resolved it turns into a ResolvedType which may be a primitive type, or a ReferenceType. ReferenceTypes may
* refer to simple, generic, parameterized or type-variable based reference types. A ReferenceType is backed by a delegate that
* provides information about the type based on some repository (for example an Eclipse based delegate, a bytecode based delegate or
* a reflection based delegate).
* <p>
* Every UnresolvedType has a signature, the unique key for the type in the world.
*/
public class UnresolvedType implements Traceable, TypeVariableDeclaringElement {
// common type structures
public static final UnresolvedType[] NONE = new UnresolvedType[0];
public static final UnresolvedType OBJECT = forSignature("Ljava/lang/Object;");
public static final UnresolvedType OBJECTARRAY = forSignature("[Ljava/lang/Object;");
public static final UnresolvedType CLONEABLE = forSignature("Ljava/lang/Cloneable;");
public static final UnresolvedType SERIALIZABLE = forSignature("Ljava/io/Serializable;");
public static final UnresolvedType THROWABLE = forSignature("Ljava/lang/Throwable;");
public static final UnresolvedType RUNTIME_EXCEPTION = forSignature("Ljava/lang/RuntimeException;");
public static final UnresolvedType ERROR = forSignature("Ljava/lang/Error;");
public static final UnresolvedType AT_INHERITED = forSignature("Ljava/lang/annotation/Inherited;");
public static final UnresolvedType AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;");
public static final UnresolvedType ENUM = forSignature("Ljava/lang/Enum;");
public static final UnresolvedType ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;");
public static final UnresolvedType JL_CLASS = forSignature("Ljava/lang/Class;");
public static final UnresolvedType JAVA_LANG_CLASS_ARRAY = forSignature("[Ljava/lang/Class;");
public static final UnresolvedType JL_STRING = forSignature("Ljava/lang/String;");
public static final UnresolvedType JL_EXCEPTION = forSignature("Ljava/lang/Exception;");
public static final UnresolvedType JAVA_LANG_REFLECT_METHOD = forSignature("Ljava/lang/reflect/Method;");
public static final UnresolvedType JAVA_LANG_REFLECT_FIELD = forSignature("Ljava/lang/reflect/Field;");
public static final UnresolvedType JAVA_LANG_REFLECT_CONSTRUCTOR = forSignature("Ljava/lang/reflect/Constructor;");
public static final UnresolvedType JAVA_LANG_ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;");
public static final UnresolvedType SUPPRESS_AJ_WARNINGS = forSignature("Lorg/aspectj/lang/annotation/SuppressAjWarnings;");
public static final UnresolvedType AT_TARGET = forSignature("Ljava/lang/annotation/Target;");
public static final UnresolvedType SOMETHING = new UnresolvedType("?");
public static final UnresolvedType[] ARRAY_WITH_JUST_OBJECT = new UnresolvedType[] { OBJECT };
public static final UnresolvedType JOINPOINT_STATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;");
public static final UnresolvedType JOINPOINT_ENCLOSINGSTATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;");
public static final UnresolvedType AJC_PRIVILEGED = forSignature("Lorg/aspectj/internal/lang/annotation/ajcPrivileged;");
public static final UnresolvedType PROCEEDING_JOINPOINT = forSignature("Lorg/aspectj/lang/ProceedingJoinPoint;");
public static final UnresolvedType BOOLEAN = forPrimitiveType("Z");
public static final UnresolvedType BYTE = forPrimitiveType("B");
public static final UnresolvedType CHAR = forPrimitiveType("C");
public static final UnresolvedType DOUBLE = forPrimitiveType("D");
public static final UnresolvedType FLOAT = forPrimitiveType("F");
public static final UnresolvedType INT = forPrimitiveType("I");
public static final UnresolvedType LONG = forPrimitiveType("J");
public static final UnresolvedType SHORT = forPrimitiveType("S");
public static final UnresolvedType VOID = forPrimitiveType("V");
// A type is considered missing if we have a signature for it but cannot find the delegate
public static final String MISSING_NAME = "@missing@";
// OPTIMIZE I dont think you can ask something unresolved what kind of type it is, how can it always know? Push down into
// resolvedtype that will force references to resolvedtypes to be correct rather than relying on unresolvedtypes to answer
// questions
protected TypeKind typeKind = TypeKind.SIMPLE; // what kind of type am I?
protected String signature;
/**
* The erasure of the signature. Contains only the Java signature of the type with all supertype, superinterface, type variable,
* and parameter information removed.
*/
protected String signatureErasure;
/**
* Calculated on first request - the package name (java.lang for type java.lang.String)
*/
private String packageName;
/**
* Calculated on first request - the class name (String for type java.lang.String)
*/
private String className;
/**
* Iff isParameterized(), then these are the type parameters
*/
protected UnresolvedType[] typeParameters;
/**
* Iff isGeneric(), then these are the type variables declared on the type Iff isParameterized(), then these are the type
* variables bound as parameters in the type
*/
// OPTIMIZE should be no state in here that will damage whether equals() is correct...
protected TypeVariable[] typeVariables;
public boolean isPrimitiveType() {
return typeKind == TypeKind.PRIMITIVE;
}
public boolean isVoid() {
// OPTIMIZE promote to bitflag?
return signature.equals("V");
}
public boolean isSimpleType() {
return typeKind == TypeKind.SIMPLE;
}
public boolean isRawType() {
return typeKind == TypeKind.RAW;
}
public boolean isGenericType() {
return typeKind == TypeKind.GENERIC;
}
public boolean isParameterizedType() {
return typeKind == TypeKind.PARAMETERIZED;
}
public boolean isParameterizedOrGenericType() {
return typeKind == TypeKind.GENERIC || typeKind == TypeKind.PARAMETERIZED;
}
public boolean isParameterizedOrRawType() {
return typeKind == TypeKind.PARAMETERIZED || typeKind == TypeKind.RAW;
}
public boolean isTypeVariableReference() {
return typeKind == TypeKind.TYPE_VARIABLE;
}
public boolean isGenericWildcard() {
return typeKind == TypeKind.WILDCARD;
}
public TypeKind getTypekind() {
return typeKind;
}
// for any reference type, we can get some extra information...
public final boolean isArray() {
return signature.length() > 0 && signature.charAt(0) == '[';
}
/**
* Equality is checked based on the underlying signature.
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof UnresolvedType)) {
return false;
}
return signature.equals(((UnresolvedType) other).signature);
}
/**
* Equality is checked based on the underlying signature, so the hash code of a particular type is the hash code of its
* signature string.
*/
@Override
public int hashCode() {
return signature.hashCode();
}
protected UnresolvedType(String signature) {
this.signature = signature;
this.signatureErasure = signature;
}
protected UnresolvedType(String signature, String signatureErasure) {
this.signature = signature;
this.signatureErasure = signatureErasure;
}
// called from TypeFactory
public UnresolvedType(String signature, String signatureErasure, UnresolvedType[] typeParams) {
this.signature = signature;
this.signatureErasure = signatureErasure;
this.typeParameters = typeParams;
if (typeParams != null) {
this.typeKind = TypeKind.PARAMETERIZED;
}
}
// The operations supported by an UnresolvedType are those that do not require a world
/**
* This is the size of this type as used in JVM.
*/
public int getSize() {
return size;
}
private int size = 1;
/**
* NOTE: Use forSignature() if you can, it'll be cheaper ! Constructs a UnresolvedType for a java language type name. For
* example:
*
* <blockquote>
*
* <pre>
* UnresolvedType.forName(&quot;java.lang.Thread[]&quot;)
* UnresolvedType.forName(&quot;int&quot;)
* </pre>
*
* </blockquote>
*
* Types may equivalently be produced by this or by {@link #forSignature(String)}.
*
* <blockquote>
*
* <pre>
* UnresolvedType.forName(&quot;java.lang.Thread[]&quot;).equals(Type.forSignature(&quot;[Ljava/lang/Thread;&quot;)
* UnresolvedType.forName(&quot;int&quot;).equals(Type.forSignature(&quot;I&quot;))
* </pre>
*
* </blockquote>
*
* @param name the java language type name in question.
* @return a type object representing that java language type.
*/
// OPTIMIZE change users of this to use forSignature, especially for simple cases
public static UnresolvedType forName(String name) {
return forSignature(nameToSignature(name));
}
/**
* Constructs a UnresolvedType for each java language type name in an incoming array.
*
* @param names an array of java language type names.
* @return an array of UnresolvedType objects.
* @see #forName(String)
*/
public static UnresolvedType[] forNames(String[] names) {
UnresolvedType[] ret = new UnresolvedType[names.length];
for (int i = 0, len = names.length; i < len; i++) {
ret[i] = UnresolvedType.forName(names[i]);
}
return ret;
}
public static UnresolvedType forGenericType(String name, TypeVariable[] tvbs, String genericSig) {
String sig = nameToSignature(name);
UnresolvedType ret = UnresolvedType.forSignature(sig);
ret.typeKind = TypeKind.GENERIC;
ret.typeVariables = tvbs;
ret.signatureErasure = sig;
return ret;
}
public static UnresolvedType forGenericTypeSignature(String sig, String declaredGenericSig) {
UnresolvedType ret = UnresolvedType.forSignature(sig);
ret.typeKind = TypeKind.GENERIC;
ClassSignature csig = new GenericSignatureParser().parseAsClassSignature(declaredGenericSig);
GenericSignature.FormalTypeParameter[] ftps = csig.formalTypeParameters;
ret.typeVariables = new TypeVariable[ftps.length];
for (int i = 0; i < ftps.length; i++) {
GenericSignature.FormalTypeParameter parameter = ftps[i];
if (parameter.classBound instanceof GenericSignature.ClassTypeSignature) {
GenericSignature.ClassTypeSignature cts = (GenericSignature.ClassTypeSignature) parameter.classBound;
ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, UnresolvedType.forSignature(cts.outerType.identifier
+ ";"));
} else if (parameter.classBound instanceof GenericSignature.TypeVariableSignature) {
GenericSignature.TypeVariableSignature tvs = (GenericSignature.TypeVariableSignature) parameter.classBound;
UnresolvedTypeVariableReferenceType utvrt = new UnresolvedTypeVariableReferenceType(new TypeVariable(
tvs.typeVariableName));
ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, utvrt);
} else {
throw new BCException(
"UnresolvedType.forGenericTypeSignature(): Do not know how to process type variable bound of type '"
+ parameter.classBound.getClass() + "'. Full signature is '" + sig + "'");
}
}
ret.signatureErasure = sig;
ret.signature = ret.signatureErasure;
return ret;
}
public static UnresolvedType forGenericTypeVariables(String sig, TypeVariable[] tVars) {
UnresolvedType ret = UnresolvedType.forSignature(sig);
ret.typeKind = TypeKind.GENERIC;
ret.typeVariables = tVars;
ret.signatureErasure = sig;
ret.signature = ret.signatureErasure;
return ret;
}
public static UnresolvedType forRawTypeName(String name) {
UnresolvedType ret = UnresolvedType.forName(name);
ret.typeKind = TypeKind.RAW;
return ret;
}
public static UnresolvedType forPrimitiveType(String signature) {
UnresolvedType ret = new UnresolvedType(signature);
ret.typeKind = TypeKind.PRIMITIVE;
if (signature.equals("J") || signature.equals("D")) {
ret.size = 2;
} else if (signature.equals("V")) {
ret.size = 0;
}
return ret;
}
/**
* Creates a new type array with a fresh type appended to the end.
*
* @param types the left hand side of the new array
* @param end the right hand side of the new array
*/
public static UnresolvedType[] add(UnresolvedType[] types, UnresolvedType end) {
int len = types.length;
UnresolvedType[] ret = new UnresolvedType[len + 1];
System.arraycopy(types, 0, ret, 0, len);
ret[len] = end;
return ret;
}
/**
* Creates a new type array with a fresh type inserted at the beginning.
*
*
* @param start the left hand side of the new array
* @param types the right hand side of the new array
*/
public static UnresolvedType[] insert(UnresolvedType start, UnresolvedType[] types) {
int len = types.length;
UnresolvedType[] ret = new UnresolvedType[len + 1];
ret[0] = start;
System.arraycopy(types, 0, ret, 1, len);
return ret;
}
/**
* Constructs a Type for a JVM bytecode signature string. For example:
*
* <blockquote>
*
* <pre>
* UnresolvedType.forSignature(&quot;[Ljava/lang/Thread;&quot;)
* UnresolvedType.forSignature(&quot;I&quot;);
* </pre>
*
* </blockquote>
*
* Types may equivalently be produced by this or by {@link #forName(String)}. This method should not be passed P signatures.
*
* <blockquote>
*
* <pre>
* UnresolvedType.forName(&quot;java.lang.Thread[]&quot;).equals(Type.forSignature(&quot;[Ljava/lang/Thread;&quot;)
* UnresolvedType.forName(&quot;int&quot;).equals(Type.forSignature(&quot;I&quot;))
* </pre>
*
* </blockquote>
*
* @param signature the JVM bytecode signature string for the desired type.
* @return a type object represnting that JVM bytecode signature.
*/
public static UnresolvedType forSignature(String signature) {
assert !(signature.startsWith("L") && signature.indexOf("<") != -1);
switch (signature.charAt(0)) {
case 'B':
return UnresolvedType.BYTE;
case 'C':
return UnresolvedType.CHAR;
case 'D':
return UnresolvedType.DOUBLE;
case 'F':
return UnresolvedType.FLOAT;
case 'I':
return UnresolvedType.INT;
case 'J':
return UnresolvedType.LONG;
case 'L':
return TypeFactory.createTypeFromSignature(signature);
case 'P':
return TypeFactory.createTypeFromSignature(signature);
case 'S':
return UnresolvedType.SHORT;
case 'V':
return UnresolvedType.VOID;
case 'Z':
return UnresolvedType.BOOLEAN;
case '[':
return TypeFactory.createTypeFromSignature(signature);
case '+':
return TypeFactory.createTypeFromSignature(signature);
case '-':
return TypeFactory.createTypeFromSignature(signature);
case '?':
return TypeFactory.createTypeFromSignature(signature);
case 'T':
return TypeFactory.createTypeFromSignature(signature);
default:
throw new BCException("Bad type signature " + signature);
}
}
/**
* Constructs a UnresolvedType for each JVM bytecode type signature in an incoming array.
*
* @param names an array of JVM bytecode type signatures
* @return an array of UnresolvedType objects.
* @see #forSignature(String)
*/
public static UnresolvedType[] forSignatures(String[] sigs) {
UnresolvedType[] ret = new UnresolvedType[sigs.length];
for (int i = 0, len = sigs.length; i < len; i++) {
ret[i] = UnresolvedType.forSignature(sigs[i]);
}
return ret;
}
/**
* Returns the name of this type in java language form (e.g. java.lang.Thread or boolean[]). This produces a more aesthetically
* pleasing string than {@link java.lang.Class#getName()}.
*
* @return the java language name of this type.
*/
public String getName() {
return signatureToName(signature);
}
public String getSimpleName() {
String name = getRawName();
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
name = name.substring(lastDot + 1);
}
if (isParameterizedType()) {
StringBuffer sb = new StringBuffer(name);
sb.append("<");
for (int i = 0; i < (typeParameters.length - 1); i++) {
sb.append(typeParameters[i].getSimpleName());
sb.append(",");
}
sb.append(typeParameters[typeParameters.length - 1].getSimpleName());
sb.append(">");
name = sb.toString();
}
return name;
}
public String getRawName() {
return signatureToName((signatureErasure == null ? signature : signatureErasure));
}
public String getBaseName() {
String name = getName();
if (isParameterizedType() || isGenericType()) {
if (typeParameters == null) {
return name;
} else {
return name.substring(0, name.indexOf("<"));
}
} else {
return name;
}
}
public String getSimpleBaseName() {
String name = getBaseName();
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
name = name.substring(lastDot + 1);
}
return name;
}
/**
* Returns an array of strings representing the java langauge names of an array of types.
*
* @param types an array of UnresolvedType objects
* @return an array of Strings fo the java language names of types.
* @see #getName()
*/
public static String[] getNames(UnresolvedType[] types) {
String[] ret = new String[types.length];
for (int i = 0, len = types.length; i < len; i++) {
ret[i] = types[i].getName();
}
return ret;
}
/**
* Returns the name of this type in JVM signature form. For all UnresolvedType t:
*
* <blockquote>
*
* <pre>
* UnresolvedType.forSignature(t.getSignature()).equals(t)
* </pre>
*
* </blockquote>
*
* and for all String s where s is a lexically valid JVM type signature string:
*
* <blockquote>
*
* <pre>
* UnresolvedType.forSignature(s).getSignature().equals(s)
* </pre>
*
* </blockquote>
*
* @return the java JVM signature string for this type.
*/
public String getSignature() {
return signature;
}
/**
* For parameterized types, return the signature for the raw type
*/
public String getErasureSignature() {
if (signatureErasure == null) {
return signature;
}
return signatureErasure;
}
private boolean needsModifiableDelegate = false;
public boolean needsModifiableDelegate() {
return needsModifiableDelegate;
}
public void setNeedsModifiableDelegate(boolean b) {
this.needsModifiableDelegate = b;
}
public UnresolvedType getRawType() {
return UnresolvedType.forSignature(getErasureSignature());
}
/**
* Returns a UnresolvedType object representing the effective outermost enclosing type for a name type. For all other types,
* this will return the type itself.
*
* The only guarantee is given in JLS 13.1 where code generated according to those rules will have type names that can be split
* apart in this way.
*
* @return the outermost enclosing UnresolvedType object or this.
*/
public UnresolvedType getOutermostType() {
if (isArray() || isPrimitiveType()) {
return this;
}
String sig = getErasureSignature();
int dollar = sig.indexOf('$');
if (dollar != -1) {
return UnresolvedType.forSignature(sig.substring(0, dollar) + ';');
} else {
return this;
}
}
/**
* Returns a UnresolvedType object representing the component type of this array, or null if this type does not represent an
* array type.
*
* @return the component UnresolvedType object, or null.
*/
public UnresolvedType getComponentType() {
if (isArray()) {
return forSignature(signature.substring(1));
} else {
return null;
}
}
/**
* Returns a java language string representation of this type.
*/
@Override
public String toString() {
return getName(); // + " - " + getKind();
}
public String toDebugString() {
return getName();
}
// ---- requires worlds
/**
* Returns a resolved version of this type according to a particular world.
*
* @param world the {@link World} within which to resolve.
* @return a resolved type representing this type in the appropriate world.
*/
public ResolvedType resolve(World world) {
return world.resolve(this);
}
// ---- helpers
private static String signatureToName(String signature) {
switch (signature.charAt(0)) {
case 'B':
return "byte";
case 'C':
return "char";
case 'D':
return "double";
case 'F':
return "float";
case 'I':
return "int";
case 'J':
return "long";
case 'L':
String name = signature.substring(1, signature.length() - 1).replace('/', '.');
return name;
case 'T':
StringBuffer nameBuff2 = new StringBuffer();
int colon = signature.indexOf(";");
String tvarName = signature.substring(1, colon);
nameBuff2.append(tvarName);
return nameBuff2.toString();
case 'P': // it's one of our parameterized type sigs
StringBuffer nameBuff = new StringBuffer();
// signature for parameterized types is e.g.
// List<String> -> Ljava/util/List<Ljava/lang/String;>;
// Map<String,List<Integer>> -> Ljava/util/Map<java/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;
int paramNestLevel = 0;
for (int i = 1; i < signature.length(); i++) {
char c = signature.charAt(i);
switch (c) {
case '/':
nameBuff.append('.');
break;
case '<':
nameBuff.append("<");
paramNestLevel++;
StringBuffer innerBuff = new StringBuffer();
while (paramNestLevel > 0) {
c = signature.charAt(++i);
if (c == '<') {
paramNestLevel++;
}
if (c == '>') {
paramNestLevel--;
}
if (paramNestLevel > 0) {
innerBuff.append(c);
}
if (c == ';' && paramNestLevel == 1) {
nameBuff.append(signatureToName(innerBuff.toString()));
if (signature.charAt(i + 1) != '>') {
nameBuff.append(',');
}
innerBuff = new StringBuffer();
}
}
nameBuff.append(">");
break;
case ';':
break;
default:
nameBuff.append(c);
}
}
return nameBuff.toString();
case 'S':
return "short";
case 'V':
return "void";
case 'Z':
return "boolean";
case '[':
return signatureToName(signature.substring(1, signature.length())) + "[]";
// case '<':
// // its a generic!
// if (signature.charAt(1)=='>') return signatureToName(signature.substring(2));
case '+':
return "? extends " + signatureToName(signature.substring(1, signature.length()));
case '-':
return "? super " + signatureToName(signature.substring(1, signature.length()));
case '*':
return "?";
default:
throw new BCException("Bad type signature: " + signature);
}
}
private static String nameToSignature(String name) {
int len = name.length();
if (len < 8) {
if (name.equals("int")) {
return "I";
}
if (name.equals("void")) {
return "V";
}
if (name.equals("long")) {
return "J";
}
if (name.equals("boolean")) {
return "Z";
}
if (name.equals("double")) {
return "D";
}
if (name.equals("float")) {
return "F";
}
if (name.equals("byte")) {
return "B";
}
if (name.equals("short")) {
return "S";
}
if (name.equals("char")) {
return "C";
}
if (name.equals("?")) {
return name;
}
}
if (len == 0) {
throw new BCException("Bad type name: " + name);
}
if (name.endsWith("[]")) {
return "[" + nameToSignature(name.substring(0, name.length() - 2));
}
// Sometimes the 'name' for an array is of the form: [Ljava.lang.String;
if (name.charAt(0)=='[') {
return name.replace('.','/');
}
if (name.indexOf("<") == -1) {
// not parameterized
return new StringBuilder("L").append(name.replace('.', '/')).append(';').toString();
} else {
StringBuffer nameBuff = new StringBuffer();
int nestLevel = 0;
nameBuff.append("P");
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
switch (c) {
case '.':
nameBuff.append('/');
break;
case '<':
nameBuff.append("<");
nestLevel++;
StringBuffer innerBuff = new StringBuffer();
while (nestLevel > 0) {
c = name.charAt(++i);
if (c == '<') {
nestLevel++;
} else if (c == '>') {
nestLevel--;
}
if (c == ',' && nestLevel == 1) {
nameBuff.append(nameToSignature(innerBuff.toString()));
innerBuff = new StringBuffer();
} else {
if (nestLevel > 0) {
innerBuff.append(c);
}
}
}
nameBuff.append(nameToSignature(innerBuff.toString()));
nameBuff.append('>');
break;
// case '>':
// throw new IllegalStateException("Should by matched by <");
// case ',':
// throw new IllegalStateException("Should only happen inside <...>");
default:
nameBuff.append(c);
}
}
nameBuff.append(";");
return nameBuff.toString();
}
}
/**
* Write out an UnresolvedType - the signature should be enough.
*/
public final void write(CompressingDataOutputStream s) throws IOException {
s.writeUTF(getSignature());
}
/**
* Read in an UnresolvedType - just read the signature and rebuild the UnresolvedType.
*/
public static UnresolvedType read(DataInputStream s) throws IOException {
String sig = s.readUTF();
if (sig.equals(MISSING_NAME)) {
return ResolvedType.MISSING;
} else {
// TODO isn't it a shame to build these (this method is expensive) and then chuck them away on resolution?
// TODO review callers and see if they are immediately resolving it, maybe we can do something more optimal if they are
return UnresolvedType.forSignature(sig);
}
}
public String getNameAsIdentifier() {
return getName().replace('.', '_');
}
public String getPackageNameAsIdentifier() {
String name = getName();
int index = name.lastIndexOf('.');
if (index == -1) {
return "";
} else {
return name.substring(0, index).replace('.', '_');
}
}
public UnresolvedType[] getTypeParameters() {
return typeParameters == null ? UnresolvedType.NONE : typeParameters;
}
public TypeVariable[] getTypeVariables() {
return typeVariables;
}
public static class TypeKind {
// Note: It is not sufficient to say that a parameterized type with no type parameters in fact
// represents a raw type - a parameterized type with no type parameters can represent
// an inner type of a parameterized type that specifies no type parameters of its own.
public final static TypeKind PRIMITIVE = new TypeKind("primitive");
public final static TypeKind SIMPLE = new TypeKind("simple"); // a type with NO type parameters/vars
public final static TypeKind RAW = new TypeKind("raw"); // the erasure of a generic type
public final static TypeKind GENERIC = new TypeKind("generic"); // a generic type
public final static TypeKind PARAMETERIZED = new TypeKind("parameterized"); // a parameterized type
public final static TypeKind TYPE_VARIABLE = new TypeKind("type_variable"); // a type variable
public final static TypeKind WILDCARD = new TypeKind("wildcard"); // a generic wildcard type
@Override
public String toString() {
return type;
}
private TypeKind(String type) {
this.type = type;
}
private final String type;
}
@Override
public TypeVariable getTypeVariableNamed(String name) {
TypeVariable[] vars = getTypeVariables();
if (vars == null || vars.length == 0) {
return null;
}
for (int i = 0; i < vars.length; i++) {
TypeVariable aVar = vars[i];
if (aVar.getName().equals(name)) {
return aVar;
}
}
return null;
}
@Override
public String toTraceString() {
return getClass().getName() + "[" + getName() + "]";
}
/**
* Return a version of this parameterized type in which any type parameters that are type variable references are replaced by
* their matching type variable binding.
*/
// OPTIMIZE methods like this just allow callers to be lazy and not ensure they are working with the right (resolved) subtype
public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) {
throw new UnsupportedOperationException("unable to parameterize unresolved type: " + signature);
}
/**
* @return the class name (does not include the package name)
*/
public String getClassName() {
if (className == null) {
String name = getName();
if (name.indexOf("<") != -1) {
name = name.substring(0, name.indexOf("<"));
}
int index = name.lastIndexOf('.');
if (index == -1) {
className = name;
} else {
className = name.substring(index + 1);
}
}
return className;
}
/**
* @return the package name (no class name included)
*/
public String getPackageName() {
if (packageName == null) {
String name = getName();
int angly = name.indexOf('<');
if (angly != -1) {
name = name.substring(0, angly);
}
int index = name.lastIndexOf('.');
if (index == -1) {
packageName = "";
} else {
packageName = name.substring(0, index);
}
}
return packageName;
}
public static void writeArray(UnresolvedType[] types, CompressingDataOutputStream stream) throws IOException {
int len = types.length;
stream.writeShort(len);
for (UnresolvedType type : types) {
type.write(stream);
}
}
public static UnresolvedType[] readArray(DataInputStream s) throws IOException {
int len = s.readShort();
if (len == 0) {
return UnresolvedType.NONE;
}
UnresolvedType[] types = new UnresolvedType[len];
for (int i = 0; i < len; i++) {
types[i] = UnresolvedType.read(s);
}
return types;
}
public static UnresolvedType makeArray(UnresolvedType base, int dims) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < dims; i++) {
sig.append("[");
}
sig.append(base.getSignature());
return UnresolvedType.forSignature(sig.toString());
}
}