blob: e565e5bc85a6020d1c9bbee04eb59c031b4f0fa2 [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 2005-2010 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://eclipse.org/legal/epl-v10.html
* ******************************************************************/
package org.aspectj.weaver;
import java.util.ArrayList;
import java.util.List;
/**
* @author Adrian Colyer
* @author Andy Clement
*/
public class TypeFactory {
/**
* Create a parameterized version of a generic type.
*
* @param aGenericType
* @param someTypeParameters note, in the case of an inner type of a parameterized type, this parameter may legitimately be null
* @param inAWorld
* @return
*/
public static ReferenceType createParameterizedType(ResolvedType aBaseType, UnresolvedType[] someTypeParameters, World inAWorld) {
ResolvedType baseType = aBaseType;
if (!aBaseType.isGenericType()) {
if (someTypeParameters != null && someTypeParameters.length > 0) {
if (!aBaseType.isRawType()) {
throw new IllegalStateException("Expecting raw type, but " + aBaseType+" is of type "+aBaseType.getTypekind());
}
baseType = baseType.getGenericType();
if (baseType == null) {
throw new IllegalStateException("Raw type does not have generic type set");
}
} // else if someTypeParameters is null, then the base type is allowed to be non-generic, it's an inner
}
ResolvedType[] resolvedParameters = inAWorld.resolve(someTypeParameters);
ReferenceType existingType = ((ReferenceType)baseType).findDerivativeType(resolvedParameters);
ReferenceType pType = null;
if (existingType!=null) {
pType = existingType;
} else {
pType =new ReferenceType(baseType, resolvedParameters, inAWorld);
}
// pType.setSourceContext(aBaseType.getSourceContext());
return (ReferenceType) pType.resolve(inAWorld);
}
/**
* Create an *unresolved* parameterized version of a generic type.
*/
public static UnresolvedType createUnresolvedParameterizedType(String sig, String erasuresig, UnresolvedType[] arguments) {
return new UnresolvedType(sig, erasuresig, arguments);
}
// public static ReferenceType createRawType(
// ResolvedType aBaseType,
// World inAWorld
// ) {
// if (aBaseType.isRawType()) return (ReferenceType) aBaseType;
// if (!aBaseType.isGenericType()) {
// if (!aBaseType.isRawType()) throw new IllegalStateException("Expecting generic type");
// }
// ReferenceType rType = new ReferenceType(aBaseType,inAWorld);
// //rType.setSourceContext(aBaseType.getSourceContext());
// return (ReferenceType) rType.resolve(inAWorld);
// }
/**
* Creates a sensible unresolvedtype from some signature, for example: signature = LIGuard<TT;>; bound = toString=IGuard<T>
* sig=PIGuard<TT;>; sigErasure=LIGuard; kind=parameterized
*/
static UnresolvedType convertSigToType(String aSignature) {
UnresolvedType bound = null;
int startOfParams = aSignature.indexOf('<');
if (startOfParams == -1) {
bound = UnresolvedType.forSignature(aSignature);
} else {
int endOfParams = aSignature.lastIndexOf('>');
String signatureErasure = "L" + aSignature.substring(1, startOfParams) + ";";
UnresolvedType[] typeParams = createTypeParams(aSignature.substring(startOfParams + 1, endOfParams));
bound = new UnresolvedType("P" + aSignature.substring(1), signatureErasure, typeParams);
}
return bound;
}
/**
* Used by UnresolvedType.read, creates a type from a full signature.
*/
public static UnresolvedType createTypeFromSignature(String signature) {
// if (signature.equals(ResolvedType.MISSING_NAME)) {
// return ResolvedType.MISSING;
// }
char firstChar = signature.charAt(0);
if (firstChar == 'P') {
// parameterized type, calculate signature erasure and type parameters
// (see pr122458) It is possible for a parameterized type to have *no* type parameters visible in its signature.
// This happens for an inner type of a parameterized type which simply inherits the type parameters
// of its parent. In this case it is parameterized but theres no < in the signature.
int startOfParams = signature.indexOf('<');
if (startOfParams == -1) {
// Should be an inner type of a parameterized type - could assert there is a '$' in the signature....
String signatureErasure = "L" + signature.substring(1);
return new UnresolvedType(signature, signatureErasure, UnresolvedType.NONE);
} else {
int endOfParams = locateMatchingEndAngleBracket(signature, startOfParams);
StringBuffer erasureSig = new StringBuffer(signature);
erasureSig.setCharAt(0, 'L');
while (startOfParams != -1) {
erasureSig.delete(startOfParams, endOfParams + 1);
startOfParams = locateFirstBracket(erasureSig);
if (startOfParams != -1) {
endOfParams = locateMatchingEndAngleBracket(erasureSig, startOfParams);
}
}
String signatureErasure = erasureSig.toString();// "L" + erasureSig.substring(1);
// the type parameters of interest are only those that apply to the 'last type' in the signature
// if the signature is 'PMyInterface<String>$MyOtherType;' then there are none...
String lastType = null;
int nestedTypePosition = signature.indexOf("$", endOfParams); // don't look for $ INSIDE the parameters
if (nestedTypePosition != -1) {
lastType = signature.substring(nestedTypePosition + 1);
} else {
lastType = new String(signature);
}
startOfParams = lastType.indexOf("<");
UnresolvedType[] typeParams = UnresolvedType.NONE;
if (startOfParams != -1) {
endOfParams = locateMatchingEndAngleBracket(lastType, startOfParams);
typeParams = createTypeParams(lastType.substring(startOfParams + 1, endOfParams));
}
StringBuilder s = new StringBuilder();
int firstAngleBracket = signature.indexOf('<');
s.append("P").append(signature.substring(1, firstAngleBracket));
s.append('<');
for (UnresolvedType typeParameter : typeParams) {
s.append(typeParameter.getSignature());
}
s.append(">;");
signature = s.toString();// 'P' + signature.substring(1);
return new UnresolvedType(signature, signatureErasure, typeParams);
}
// can't replace above with convertSigToType - leads to stackoverflow
} else if ((firstChar == '?' || firstChar == '*') && signature.length()==1) {
return WildcardedUnresolvedType.QUESTIONMARK;
} else if (firstChar == '+') {
// ? extends ...
UnresolvedType upperBound = convertSigToType(signature.substring(1));
WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, upperBound, null);
return wildcardedUT;
} else if (firstChar == '-') {
// ? super ...
UnresolvedType lowerBound = convertSigToType(signature.substring(1));
WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, null, lowerBound);
return wildcardedUT;
} else if (firstChar == 'T') {
String typeVariableName = signature.substring(1);
if (typeVariableName.endsWith(";")) {
typeVariableName = typeVariableName.substring(0, typeVariableName.length() - 1);
}
return new UnresolvedTypeVariableReferenceType(new TypeVariable(typeVariableName));
} else if (firstChar == '[') {
int dims = 0;
while (signature.charAt(dims) == '[') {
dims++;
}
UnresolvedType componentType = createTypeFromSignature(signature.substring(dims));
return new UnresolvedType(signature, signature.substring(0, dims) + componentType.getErasureSignature());
} else if (signature.length() == 1) { // could be a primitive
switch (firstChar) {
case 'V':
return UnresolvedType.VOID;
case 'Z':
return UnresolvedType.BOOLEAN;
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 'S':
return UnresolvedType.SHORT;
}
} else if (firstChar == '@') {
// missing type
return ResolvedType.MISSING;
} else if (firstChar == 'L') {
// only an issue if there is also an angle bracket
int leftAngleBracket = signature.indexOf('<');
if (leftAngleBracket == -1) {
return new UnresolvedType(signature);
} else {
int endOfParams = locateMatchingEndAngleBracket(signature, leftAngleBracket);
StringBuffer erasureSig = new StringBuffer(signature);
erasureSig.setCharAt(0, 'L');
while (leftAngleBracket != -1) {
erasureSig.delete(leftAngleBracket, endOfParams + 1);
leftAngleBracket = locateFirstBracket(erasureSig);
if (leftAngleBracket != -1) {
endOfParams = locateMatchingEndAngleBracket(erasureSig, leftAngleBracket);
}
}
String signatureErasure = erasureSig.toString();
// TODO should consider all the intermediate parameterizations as well!
// the type parameters of interest are only those that apply to the 'last type' in the signature
// if the signature is 'PMyInterface<String>$MyOtherType;' then there are none...
String lastType = null;
int nestedTypePosition = signature.indexOf("$", endOfParams); // don't look for $ INSIDE the parameters
if (nestedTypePosition != -1) {
lastType = signature.substring(nestedTypePosition + 1);
} else {
lastType = new String(signature);
}
leftAngleBracket = lastType.indexOf("<");
UnresolvedType[] typeParams = UnresolvedType.NONE;
if (leftAngleBracket != -1) {
endOfParams = locateMatchingEndAngleBracket(lastType, leftAngleBracket);
typeParams = createTypeParams(lastType.substring(leftAngleBracket + 1, endOfParams));
}
StringBuilder s = new StringBuilder();
int firstAngleBracket = signature.indexOf('<');
s.append("P").append(signature.substring(1, firstAngleBracket));
s.append('<');
for (UnresolvedType typeParameter : typeParams) {
s.append(typeParameter.getSignature());
}
s.append(">;");
signature = s.toString();// 'P' + signature.substring(1);
return new UnresolvedType(signature, signatureErasure, typeParams);
}
}
return new UnresolvedType(signature);
}
private static int locateMatchingEndAngleBracket(CharSequence signature, int startOfParams) {
if (startOfParams == -1) {
return -1;
}
int count = 1;
int idx = startOfParams;
int max = signature.length();
while (idx < max) {
char ch = signature.charAt(++idx);
if (ch == '<') {
count++;
} else if (ch == '>') {
if (count == 1) {
break;
}
count--;
}
}
return idx;
}
private static int locateFirstBracket(StringBuffer signature) {
int idx = 0;
int max = signature.length();
while (idx < max) {
if (signature.charAt(idx) == '<') {
return idx;
}
idx++;
}
return -1;
}
private static UnresolvedType[] createTypeParams(String typeParameterSpecification) {
String remainingToProcess = typeParameterSpecification;
List<UnresolvedType> types = new ArrayList<UnresolvedType>();
while (remainingToProcess.length() != 0) {
int endOfSig = 0;
int anglies = 0;
boolean hadAnglies = false;
boolean sigFound = false; // OPTIMIZE can this be done better?
for (endOfSig = 0; (endOfSig < remainingToProcess.length()) && !sigFound; endOfSig++) {
char thisChar = remainingToProcess.charAt(endOfSig);
switch (thisChar) {
case '<':
anglies++;
hadAnglies = true;
break;
case '>':
anglies--;
break;
case '*':
if (anglies==0) {
int nextCharPos = endOfSig+1;
if (nextCharPos>=remainingToProcess.length()) {
sigFound=true;
} else {
char nextChar = remainingToProcess.charAt(nextCharPos);
if (!(nextChar=='+' || nextChar=='-')) {
// dont need to set endOfSig as the loop will increment
// it to the right place before it exits
sigFound=true;
}
}
}
break;
case '[':
if (anglies == 0) {
// the next char might be a [ or a primitive type ref (BCDFIJSZ)
int nextChar = endOfSig + 1;
while (remainingToProcess.charAt(nextChar) == '[') {
nextChar++;
}
if ("BCDFIJSZ".indexOf(remainingToProcess.charAt(nextChar)) != -1) {
// it is something like [I or [[S
sigFound = true;
endOfSig = nextChar;
break;
}
}
break;
case ';':
if (anglies == 0) {
sigFound = true;
break;
}
}
}
String forProcessing = remainingToProcess.substring(0, endOfSig);
if (hadAnglies && forProcessing.charAt(0) == 'L') {
forProcessing = "P" + forProcessing.substring(1);
}
types.add(createTypeFromSignature(forProcessing));
remainingToProcess = remainingToProcess.substring(endOfSig);
}
UnresolvedType[] typeParams = new UnresolvedType[types.size()];
types.toArray(typeParams);
return typeParams;
}
// OPTIMIZE improve all this signature processing stuff, use char arrays, etc
/**
* Create a signature then delegate to the other factory method. Same input/output: baseTypeSignature="LSomeType;" arguments[0]=
* something with sig "Pcom/Foo<Ljava/lang/String;>;" signature created = "PSomeType<Pcom/Foo<Ljava/lang/String;>;>;"
*/
public static UnresolvedType createUnresolvedParameterizedType(String baseTypeSignature, UnresolvedType[] arguments) {
StringBuffer parameterizedSig = new StringBuffer();
parameterizedSig.append(ResolvedType.PARAMETERIZED_TYPE_IDENTIFIER);
parameterizedSig.append(baseTypeSignature.substring(1, baseTypeSignature.length() - 1));
if (arguments.length > 0) {
parameterizedSig.append("<");
for (int i = 0; i < arguments.length; i++) {
parameterizedSig.append(arguments[i].getSignature());
}
parameterizedSig.append(">");
}
parameterizedSig.append(";");
return createUnresolvedParameterizedType(parameterizedSig.toString(), baseTypeSignature, arguments);
}
}