diff options
author | aclement <aclement> | 2008-10-20 18:31:31 +0000 |
---|---|---|
committer | aclement <aclement> | 2008-10-20 18:31:31 +0000 |
commit | 22c9c825e93a8ad85904462499fcd0b7ca5f7845 (patch) | |
tree | ffec158574f784a090ece1d76d40938d31a12d4f /util/src | |
parent | 83fad50ca5effa81e73b6fa8f159d175fcaab599 (diff) | |
download | aspectj-22c9c825e93a8ad85904462499fcd0b7ca5f7845.tar.gz aspectj-22c9c825e93a8ad85904462499fcd0b7ca5f7845.zip |
246125: promoted generic sig parsing code to util
Diffstat (limited to 'util/src')
-rw-r--r-- | util/src/org/aspectj/util/GenericSignature.java | 249 | ||||
-rw-r--r-- | util/src/org/aspectj/util/GenericSignatureParser.java | 410 |
2 files changed, 659 insertions, 0 deletions
diff --git a/util/src/org/aspectj/util/GenericSignature.java b/util/src/org/aspectj/util/GenericSignature.java new file mode 100644 index 000000000..0e2433abd --- /dev/null +++ b/util/src/org/aspectj/util/GenericSignature.java @@ -0,0 +1,249 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * 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 + * + * ******************************************************************/ +package org.aspectj.util; + +/** + * Encapsulate generic signature parsing + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class GenericSignature { + + /** + * structure holding a parsed class signature + */ + public static class ClassSignature { + public FormalTypeParameter[] formalTypeParameters = new FormalTypeParameter[0]; + public ClassTypeSignature superclassSignature; + public ClassTypeSignature[] superInterfaceSignatures = new ClassTypeSignature[0]; + + public String toString() { + StringBuffer ret = new StringBuffer(); + ret.append(formalTypeParameters.toString()); + ret.append(superclassSignature.toString()); + for (int i = 0; i < superInterfaceSignatures.length; i++) { + ret.append(superInterfaceSignatures[i].toString()); + } + return ret.toString(); + } + } + + public static class MethodTypeSignature { + public FormalTypeParameter[] formalTypeParameters = new FormalTypeParameter[0]; + public TypeSignature[] parameters = new TypeSignature[0]; + public TypeSignature returnType; + public FieldTypeSignature[] throwsSignatures = new FieldTypeSignature[0]; + + public MethodTypeSignature(FormalTypeParameter[] aFormalParameterList, TypeSignature[] aParameterList, + TypeSignature aReturnType, FieldTypeSignature[] aThrowsSignatureList) { + this.formalTypeParameters = aFormalParameterList; + this.parameters = aParameterList; + this.returnType = aReturnType; + this.throwsSignatures = aThrowsSignatureList; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + if (formalTypeParameters.length > 0) { + sb.append("<"); + for (int i = 0; i < formalTypeParameters.length; i++) { + sb.append(formalTypeParameters[i].toString()); + } + sb.append(">"); + } + sb.append("("); + for (int i = 0; i < parameters.length; i++) { + sb.append(parameters[i].toString()); + } + sb.append(")"); + sb.append(returnType.toString()); + for (int i = 0; i < throwsSignatures.length; i++) { + sb.append("^"); + sb.append(throwsSignatures[i].toString()); + } + return sb.toString(); + } + } + + /** + * structure capturing a FormalTypeParameter from the Signature grammar + */ + public static class FormalTypeParameter { + public String identifier; + public FieldTypeSignature classBound; + public FieldTypeSignature[] interfaceBounds; + + public String toString() { + StringBuffer ret = new StringBuffer(); + ret.append("T"); + ret.append(identifier); + ret.append(":"); + ret.append(classBound.toString()); + for (int i = 0; i < interfaceBounds.length; i++) { + ret.append(":"); + ret.append(interfaceBounds[i].toString()); + } + return ret.toString(); + } + } + + public static abstract class TypeSignature { + public boolean isBaseType() { + return false; + } + } + + public static class BaseTypeSignature extends TypeSignature { + private final String sig; + + public BaseTypeSignature(String aPrimitiveType) { + sig = aPrimitiveType; + } + + public boolean isBaseType() { + return true; + } + + public String toString() { + return sig; + } + } + + public static abstract class FieldTypeSignature extends TypeSignature { + public boolean isClassTypeSignature() { + return false; + } + + public boolean isTypeVariableSignature() { + return false; + } + + public boolean isArrayTypeSignature() { + return false; + } + } + + public static class ClassTypeSignature extends FieldTypeSignature { + public String classSignature; + public SimpleClassTypeSignature outerType; + public SimpleClassTypeSignature[] nestedTypes; + + public ClassTypeSignature(String sig, String identifier) { + this.classSignature = sig; + this.outerType = new SimpleClassTypeSignature(identifier); + this.nestedTypes = new SimpleClassTypeSignature[0]; + } + + public ClassTypeSignature(String sig, SimpleClassTypeSignature outer, SimpleClassTypeSignature[] inners) { + this.classSignature = sig; + this.outerType = outer; + this.nestedTypes = inners; + } + + public boolean isClassTypeSignature() { + return true; + } + + public String toString() { + return classSignature; + } + } + + public static class TypeVariableSignature extends FieldTypeSignature { + public String typeVariableName; + + public TypeVariableSignature(String typeVarToken) { + this.typeVariableName = typeVarToken.substring(1); + } + + public boolean isTypeVariableSignature() { + return true; + } + + public String toString() { + return "T" + typeVariableName + ";"; + } + } + + public static class ArrayTypeSignature extends FieldTypeSignature { + public TypeSignature typeSig; + + public ArrayTypeSignature(TypeSignature aTypeSig) { + this.typeSig = aTypeSig; + } + + public boolean isArrayTypeSignature() { + return true; + } + + public String toString() { + return "[" + typeSig.toString(); + } + } + + public static class SimpleClassTypeSignature { + public String identifier; + public TypeArgument[] typeArguments; + + public SimpleClassTypeSignature(String identifier) { + this.identifier = identifier; + this.typeArguments = new TypeArgument[0]; + } + + public SimpleClassTypeSignature(String identifier, TypeArgument[] args) { + this.identifier = identifier; + this.typeArguments = args; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(identifier); + if (typeArguments.length > 0) { + sb.append("<"); + for (int i = 0; i < typeArguments.length; i++) { + sb.append(typeArguments[i].toString()); + } + sb.append(">"); + } + return sb.toString(); + } + } + + public static class TypeArgument { + public boolean isWildcard = false; + public boolean isPlus = false; + public boolean isMinus = false; + public FieldTypeSignature signature; // null if isWildcard + + public TypeArgument() { + isWildcard = true; + } + + public TypeArgument(boolean plus, boolean minus, FieldTypeSignature aSig) { + this.isPlus = plus; + this.isMinus = minus; + this.signature = aSig; + } + + public String toString() { + if (isWildcard) + return "*"; + StringBuffer sb = new StringBuffer(); + if (isPlus) + sb.append("+"); + if (isMinus) + sb.append("-"); + sb.append(signature.toString()); + return sb.toString(); + } + } +} diff --git a/util/src/org/aspectj/util/GenericSignatureParser.java b/util/src/org/aspectj/util/GenericSignatureParser.java new file mode 100644 index 000000000..d8e5bd073 --- /dev/null +++ b/util/src/org/aspectj/util/GenericSignatureParser.java @@ -0,0 +1,410 @@ +/* ******************************************************************* + * Copyright (c) 2005-2008 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.util; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.util.GenericSignature.ArrayTypeSignature; +import org.aspectj.util.GenericSignature.BaseTypeSignature; +import org.aspectj.util.GenericSignature.ClassTypeSignature; +import org.aspectj.util.GenericSignature.FieldTypeSignature; +import org.aspectj.util.GenericSignature.FormalTypeParameter; +import org.aspectj.util.GenericSignature.MethodTypeSignature; +import org.aspectj.util.GenericSignature.SimpleClassTypeSignature; +import org.aspectj.util.GenericSignature.TypeArgument; +import org.aspectj.util.GenericSignature.TypeSignature; +import org.aspectj.util.GenericSignature.TypeVariableSignature; + +/** + * Parses the generic signature attribute as defined in the JVM spec. + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class GenericSignatureParser { + + private String inputString; + private String[] tokenStream; // for parse in flight + private int tokenIndex = 0; + + /** + * AMC. Parse the signature string interpreting it as a ClassSignature according to the grammar defined in Section 4.4.4 of the + * JVM specification. + */ + public GenericSignature.ClassSignature parseAsClassSignature(String sig) { + this.inputString = sig; + tokenStream = tokenize(sig); + tokenIndex = 0; + GenericSignature.ClassSignature classSig = new GenericSignature.ClassSignature(); + // FormalTypeParameters-opt + if (maybeEat("<")) { + List formalTypeParametersList = new ArrayList(); + do { + formalTypeParametersList.add(parseFormalTypeParameter()); + } while (!maybeEat(">")); + classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()]; + formalTypeParametersList.toArray(classSig.formalTypeParameters); + } + classSig.superclassSignature = parseClassTypeSignature(); + List superIntSigs = new ArrayList(); + while (tokenIndex < tokenStream.length) { + superIntSigs.add(parseClassTypeSignature()); + } + classSig.superInterfaceSignatures = new ClassTypeSignature[superIntSigs.size()]; + superIntSigs.toArray(classSig.superInterfaceSignatures); + return classSig; + } + + /** + * AMC. Parse the signature string interpreting it as a MethodTypeSignature according to the grammar defined in Section 4.4.4 of + * the JVM specification. + */ + public MethodTypeSignature parseAsMethodSignature(String sig) { + this.inputString = sig; + tokenStream = tokenize(sig); + tokenIndex = 0; + FormalTypeParameter[] formals = new FormalTypeParameter[0]; + TypeSignature returnType = null; + // FormalTypeParameters-opt + if (maybeEat("<")) { + List formalTypeParametersList = new ArrayList(); + do { + formalTypeParametersList.add(parseFormalTypeParameter()); + } while (!maybeEat(">")); + formals = new FormalTypeParameter[formalTypeParametersList.size()]; + formalTypeParametersList.toArray(formals); + } + // Parameters + eat("("); + List paramList = new ArrayList(); + while (!maybeEat(")")) { + FieldTypeSignature fsig = parseFieldTypeSignature(true); + if (fsig != null) { + paramList.add(fsig); + } else { + paramList.add(new GenericSignature.BaseTypeSignature(eatIdentifier())); + } + } + TypeSignature[] params = new TypeSignature[paramList.size()]; + paramList.toArray(params); + // return type + returnType = parseFieldTypeSignature(true); + if (returnType == null) + returnType = new GenericSignature.BaseTypeSignature(eatIdentifier()); + // throws + List throwsList = new ArrayList(); + while (maybeEat("^")) { + FieldTypeSignature fsig = parseFieldTypeSignature(false); + throwsList.add(fsig); + } + FieldTypeSignature[] throwsSigs = new FieldTypeSignature[throwsList.size()]; + throwsList.toArray(throwsSigs); + return new GenericSignature.MethodTypeSignature(formals, params, returnType, throwsSigs); + } + + /** + * AMC. Parse the signature string interpreting it as a FieldTypeSignature according to the grammar defined in Section 4.4.4 of + * the JVM specification. + */ + public FieldTypeSignature parseAsFieldSignature(String sig) { + this.inputString = sig; + tokenStream = tokenize(sig); + tokenIndex = 0; + return parseFieldTypeSignature(false); + } + + private FormalTypeParameter parseFormalTypeParameter() { + FormalTypeParameter ftp = new FormalTypeParameter(); + // Identifier + ftp.identifier = eatIdentifier(); + // ClassBound + eat(":"); + ftp.classBound = parseFieldTypeSignature(true); + if (ftp.classBound == null) { + ftp.classBound = new ClassTypeSignature("Ljava/lang/Object;", "Ljava/lang/Object"); + } + // Optional InterfaceBounds + List optionalBounds = new ArrayList(); + while (maybeEat(":")) { + optionalBounds.add(parseFieldTypeSignature(false)); + } + ftp.interfaceBounds = new FieldTypeSignature[optionalBounds.size()]; + optionalBounds.toArray(ftp.interfaceBounds); + return ftp; + } + + private FieldTypeSignature parseFieldTypeSignature(boolean isOptional) { + if (isOptional) { + // anything other than 'L', 'T' or '[' and we're out of here + if (!tokenStream[tokenIndex].startsWith("L") && !tokenStream[tokenIndex].startsWith("T") + && !tokenStream[tokenIndex].startsWith("[")) { + return null; + } + } + if (maybeEat("[")) { + return parseArrayTypeSignature(); + } else if (tokenStream[tokenIndex].startsWith("L")) { + return parseClassTypeSignature(); + } else if (tokenStream[tokenIndex].startsWith("T")) { + return parseTypeVariableSignature(); + } else { + throw new IllegalStateException("Expecting [,L, or T, but found " + tokenStream[tokenIndex] + " while unpacking " + + inputString); + } + } + + private ArrayTypeSignature parseArrayTypeSignature() { + // opening [ already eaten + FieldTypeSignature fieldType = parseFieldTypeSignature(true); + if (fieldType != null) { + return new ArrayTypeSignature(fieldType); + } else { + // must be BaseType array + return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier())); + } + } + + // L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ; + private ClassTypeSignature parseClassTypeSignature() { + SimpleClassTypeSignature outerType = null; + SimpleClassTypeSignature[] nestedTypes = new SimpleClassTypeSignature[0]; + StringBuffer ret = new StringBuffer(); + String identifier = eatIdentifier(); + ret.append(identifier); + while (maybeEat("/")) { + ret.append("/"); // dont forget this... + ret.append(eatIdentifier()); + } + identifier = ret.toString(); + // now we have either a "." indicating the start of a nested type, + // or a "<" indication type arguments, or ";" and we are done. + while (!maybeEat(";")) { + if (maybeEat(".")) { + // outer type completed + outerType = new SimpleClassTypeSignature(identifier); + List nestedTypeList = new ArrayList(); + do { + ret.append("."); + SimpleClassTypeSignature sig = parseSimpleClassTypeSignature(); + ret.append(sig.toString()); + nestedTypeList.add(sig); + } while (maybeEat(".")); + nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()]; + nestedTypeList.toArray(nestedTypes); + } else if (tokenStream[tokenIndex].equals("<")) { + ret.append("<"); + TypeArgument[] tArgs = maybeParseTypeArguments(); + for (int i = 0; i < tArgs.length; i++) { + ret.append(tArgs[i].toString()); + } + ret.append(">"); + outerType = new SimpleClassTypeSignature(identifier, tArgs); + // now parse possible nesteds... + List nestedTypeList = new ArrayList(); + while (maybeEat(".")) { + ret.append("."); + SimpleClassTypeSignature sig = parseSimpleClassTypeSignature(); + ret.append(sig.toString()); + nestedTypeList.add(sig); + } + nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()]; + nestedTypeList.toArray(nestedTypes); + } else { + throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking " + + inputString); + } + } + ret.append(";"); + if (outerType == null) + outerType = new SimpleClassTypeSignature(ret.toString()); + return new ClassTypeSignature(ret.toString(), outerType, nestedTypes); + } + + private SimpleClassTypeSignature parseSimpleClassTypeSignature() { + String identifier = eatIdentifier(); + TypeArgument[] tArgs = maybeParseTypeArguments(); + if (tArgs != null) { + return new SimpleClassTypeSignature(identifier, tArgs); + } else { + return new SimpleClassTypeSignature(identifier); + } + } + + private TypeArgument parseTypeArgument() { + boolean isPlus = false; + boolean isMinus = false; + if (maybeEat("*")) { + return new TypeArgument(); + } else if (maybeEat("+")) { + isPlus = true; + } else if (maybeEat("-")) { + isMinus = true; + } + FieldTypeSignature sig = parseFieldTypeSignature(false); + return new TypeArgument(isPlus, isMinus, sig); + } + + private TypeArgument[] maybeParseTypeArguments() { + if (maybeEat("<")) { + List typeArgs = new ArrayList(); + do { + TypeArgument arg = parseTypeArgument(); + typeArgs.add(arg); + } while (!maybeEat(">")); + TypeArgument[] tArgs = new TypeArgument[typeArgs.size()]; + typeArgs.toArray(tArgs); + return tArgs; + } else { + return null; + } + } + + private TypeVariableSignature parseTypeVariableSignature() { + TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier()); + eat(";"); + return tv; + } + + private boolean maybeEat(String token) { + if (tokenStream.length <= tokenIndex) + return false; + if (tokenStream[tokenIndex].equals(token)) { + tokenIndex++; + return true; + } + return false; + } + + private void eat(String token) { + if (!tokenStream[tokenIndex].equals(token)) { + throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking " + + inputString); + } + tokenIndex++; + } + + private String eatIdentifier() { + return tokenStream[tokenIndex++]; + } + + /** + * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser. + */ + public String[] tokenize(String signatureString) { + char[] chars = signatureString.toCharArray(); + int index = 0; + List tokens = new ArrayList(); + StringBuffer identifier = new StringBuffer(); + boolean inParens = false; + boolean inArray = false; + boolean couldSeePrimitive = false; + do { + switch (chars[index]) { + case '<': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add("<"); + break; + case '>': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add(">"); + break; + case ':': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add(":"); + break; + case '/': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add("/"); + couldSeePrimitive = false; + break; + case ';': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add(";"); + couldSeePrimitive = true; + inArray = false; + break; + case '^': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + tokens.add("^"); + break; + case '+': + tokens.add("+"); + break; + case '-': + tokens.add("-"); + break; + case '*': + tokens.add("*"); + break; + case '.': + if (identifier.length() > 0) + tokens.add(identifier.toString()); + identifier = new StringBuffer(); + couldSeePrimitive = false; + tokens.add("."); + break; + case '(': + tokens.add("("); + inParens = true; + couldSeePrimitive = true; + break; + case ')': + tokens.add(")"); + inParens = false; + break; + case '[': + tokens.add("["); + couldSeePrimitive = true; + inArray = true; + break; + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'V': + case 'Z': + if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) { + tokens.add(new String("" + chars[index])); + } else { + identifier.append(chars[index]); + } + inArray = false; + break; + case 'L': + couldSeePrimitive = false; + // deliberate fall-through + default: + identifier.append(chars[index]); + } + } while ((++index) < chars.length); + if (identifier.length() > 0) + tokens.add(identifier.toString()); + String[] tokenArray = new String[tokens.size()]; + tokens.toArray(tokenArray); + return tokenArray; + } + +} |