/* *******************************************************************
 * 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, Andy Clement
/** * @author Adrian Colyer * @author Andy Clement */
public class TypeFactory {
Create a parameterized version of a generic type.
Params:
  • aGenericType –
  • someTypeParameters – note, in the case of an inner type of a parameterized type, this parameter may legitimately be null
  • inAWorld –
Returns:
/** * 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.
/** * 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; bound = toString=IGuard sig=PIGuard; sigErasure=LIGuard; kind=parameterized
/** * 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.
/** * 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;" signature created = "PSomeType;>;"
/** * 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); } }