From 22c9c825e93a8ad85904462499fcd0b7ca5f7845 Mon Sep 17 00:00:00 2001 From: aclement Date: Mon, 20 Oct 2008 18:31:31 +0000 Subject: [PATCH] 246125: promoted generic sig parsing code to util --- .../org/aspectj/util/GenericSignature.java | 249 +++++++++++ .../aspectj/util/GenericSignatureParser.java | 410 ++++++++++++++++++ .../util/GenericSignatureParserTest.java | 212 +++++++++ util/testsrc/org/aspectj/util/UtilTests.java | 28 +- 4 files changed, 887 insertions(+), 12 deletions(-) create mode 100644 util/src/org/aspectj/util/GenericSignature.java create mode 100644 util/src/org/aspectj/util/GenericSignatureParser.java create mode 100644 util/testsrc/org/aspectj/util/GenericSignatureParserTest.java 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; + } + +} diff --git a/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java b/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java new file mode 100644 index 000000000..c46c97b63 --- /dev/null +++ b/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java @@ -0,0 +1,212 @@ +/* ******************************************************************* + * 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://www.eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ + +package org.aspectj.util; + +import junit.framework.TestCase; + +import org.aspectj.util.GenericSignature.ClassSignature; +import org.aspectj.util.GenericSignature.ClassTypeSignature; +import org.aspectj.util.GenericSignature.FieldTypeSignature; +import org.aspectj.util.GenericSignature.SimpleClassTypeSignature; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public class GenericSignatureParserTest extends TestCase { + + GenericSignatureParser parser; + + public void testSimpleTokenize() { + String[] tokens = parser.tokenize("Ljava/lang/String;"); + assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", ";" }, tokens); + } + + public void testTokenizeWithWildTypeArguments() { + String[] tokens = parser.tokenize("Ljava/lang/String<*>;"); + assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", "<", "*", ">", ";" }, tokens); + } + + public void testTokenizeWithExtendsTypeArguments() { + String[] tokens = parser.tokenize("Ljava/util/List<+TE>;"); + assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "+", "TE", ">", ";" }, tokens); + } + + public void testTokenizeWithSuperTypeArguments() { + String[] tokens = parser.tokenize("Ljava/util/List<-TE>;"); + assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "-", "TE", ">", ";" }, tokens); + } + + public void testTokenizeArrayType() { + String[] tokens = parser.tokenize("[Ljava/lang/String;"); + assertEquals(new String[] { "[", "Ljava", "/", "lang", "/", "String", ";" }, tokens); + } + + public void testTokenizeFormalTypeParameters() { + String[] tokens = parser.tokenize(""); + assertEquals(new String[] { "<", "T", ":", "Ljava", "/", "lang", "/", "String", ";", ":", "Ljava", "/", "util", "/", + "Comparable", ";", ">" }, tokens); + } + + public void testParseClassSignatureSimple() { + ClassSignature sig = parser.parseAsClassSignature("Ljava/lang/String;"); + assertEquals("No type parameters", 0, sig.formalTypeParameters.length); + assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length); + assertEquals("Ljava/lang/String;", sig.superclassSignature.classSignature); + SimpleClassTypeSignature outerType = sig.superclassSignature.outerType; + assertEquals("Ljava/lang/String;", outerType.identifier); + assertEquals("No type args", 0, outerType.typeArguments.length); + } + + public void testParseClassSignatureTypeArgs() { + ClassSignature sig = parser.parseAsClassSignature("Ljava/util/List<+Ljava/lang/String;>;"); + assertEquals("No type parameters", 0, sig.formalTypeParameters.length); + assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length); + assertEquals("Ljava/util/List<+Ljava/lang/String;>;", sig.superclassSignature.classSignature); + SimpleClassTypeSignature outerType = sig.superclassSignature.outerType; + assertEquals("Ljava/util/List", outerType.identifier); + assertEquals("One type arg", 1, outerType.typeArguments.length); + assertTrue(outerType.typeArguments[0].isPlus); + assertEquals("+Ljava/lang/String;", outerType.typeArguments[0].toString()); + } + + public void testParseClassSignatureTheFullMonty() { + ClassSignature sig = parser + .parseAsClassSignature(";>Ljava/util/List;Ljava/util/Comparable<-TE;>;"); + assertEquals("1 formal parameter", 1, sig.formalTypeParameters.length); + assertEquals("E", sig.formalTypeParameters[0].identifier); + ClassTypeSignature fsig = (ClassTypeSignature) sig.formalTypeParameters[0].classBound; + assertEquals("Ljava/lang/String;", fsig.classSignature); + assertEquals("1 interface bound", 1, sig.formalTypeParameters[0].interfaceBounds.length); + ClassTypeSignature isig = (ClassTypeSignature) sig.formalTypeParameters[0].interfaceBounds[0]; + assertEquals("Ljava/lang/Number;", isig.classSignature); + assertEquals("Ljava/util/List;", sig.superclassSignature.classSignature); + assertEquals("1 type argument", 1, sig.superclassSignature.outerType.typeArguments.length); + assertEquals("TE;", sig.superclassSignature.outerType.typeArguments[0].toString()); + assertEquals("1 super interface", 1, sig.superInterfaceSignatures.length); + assertEquals("Ljava/util/Comparable<-TE;>;", sig.superInterfaceSignatures[0].toString()); + } + + public void testFieldSignatureParsingClassType() { + FieldTypeSignature fsig = parser.parseAsFieldSignature("Ljava/lang/String;"); + assertTrue("ClassTypeSignature", fsig instanceof ClassTypeSignature); + assertEquals("Ljava/lang/String;", fsig.toString()); + } + + public void testFieldSignatureParsingArrayType() { + FieldTypeSignature fsig = parser.parseAsFieldSignature("[Ljava/lang/String;"); + assertTrue("ArrayTypeSignature", fsig instanceof GenericSignature.ArrayTypeSignature); + assertEquals("[Ljava/lang/String;", fsig.toString()); + } + + public void testFieldSignatureParsingTypeVariable() { + FieldTypeSignature fsig = parser.parseAsFieldSignature("TT;"); + assertTrue("TypeVariableSignature", fsig instanceof GenericSignature.TypeVariableSignature); + assertEquals("TT;", fsig.toString()); + } + + public void testSimpleMethodSignatureParsing() { + GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("()V"); + assertEquals("No type parameters", 0, mSig.formalTypeParameters.length); + assertEquals("No parameters", 0, mSig.parameters.length); + assertEquals("Void return type", "V", mSig.returnType.toString()); + assertEquals("No throws", 0, mSig.throwsSignatures.length); + } + + public void testMethodSignatureTypeParams() { + GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("(TT;)V"); + assertEquals("One type parameter", 1, mSig.formalTypeParameters.length); + assertEquals("T", mSig.formalTypeParameters[0].identifier); + assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString()); + assertEquals("One parameter", 1, mSig.parameters.length); + assertEquals("TT;", mSig.parameters[0].toString()); + assertEquals("Void return type", "V", mSig.returnType.toString()); + assertEquals("No throws", 0, mSig.throwsSignatures.length); + } + + public void testMethodSignatureGenericReturn() { + GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("()TT;"); + assertEquals("One type parameter", 1, mSig.formalTypeParameters.length); + assertEquals("T", mSig.formalTypeParameters[0].identifier); + assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString()); + assertEquals("No parameters", 0, mSig.parameters.length); + assertEquals("'T' return type", "TT;", mSig.returnType.toString()); + assertEquals("No throws", 0, mSig.throwsSignatures.length); + } + + public void testMethodSignatureThrows() { + GenericSignature.MethodTypeSignature mSig = parser + .parseAsMethodSignature("(TT;)V^Ljava/lang/Exception;^Ljava/lang/RuntimeException;"); + assertEquals("One type parameter", 1, mSig.formalTypeParameters.length); + assertEquals("T", mSig.formalTypeParameters[0].identifier); + assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString()); + assertEquals("One parameter", 1, mSig.parameters.length); + assertEquals("TT;", mSig.parameters[0].toString()); + assertEquals("Void return type", "V", mSig.returnType.toString()); + assertEquals("2 throws", 2, mSig.throwsSignatures.length); + assertEquals("Ljava/lang/Exception;", mSig.throwsSignatures[0].toString()); + assertEquals("Ljava/lang/RuntimeException;", mSig.throwsSignatures[1].toString()); + } + + public void testMethodSignaturePrimitiveParams() { + GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("(ILjava/lang/Object;)V"); + assertEquals("2 parameters", 2, mSig.parameters.length); + assertEquals("I", mSig.parameters[0].toString()); + assertEquals("Ljava/lang/Object;", mSig.parameters[1].toString()); + } + + public void testFullyQualifiedSuperclassAfterTypeParams() { + try { + GenericSignature.FieldTypeSignature cSig = parser.parseAsFieldSignature("Ljava/util/List;"); + parser.parseAsClassSignature("Ljava/lang/Object;"); + } + + private void assertEquals(String[] expected, String[] actual) { + if (actual.length != expected.length) { + int shorter = Math.min(expected.length, actual.length); + for (int i = 0; i < shorter; i++) { + if (!actual[i].equals(expected[i])) { + fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]); + } + } + fail("Expected " + expected.length + " tokens but got " + actual.length + tokensToString(actual)); + } + for (int i = 0; i < actual.length; i++) { + if (!actual[i].equals(expected[i])) { + fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]); + } + } + } + + private String tokensToString(String[] tokens) { + StringBuffer sb = new StringBuffer(); + sb.append(tokens[0]); + for (int i = 1; i < tokens.length; i++) { + sb.append(","); + sb.append(tokens[i]); + } + return sb.toString(); + } + + protected void setUp() throws Exception { + super.setUp(); + parser = new GenericSignatureParser(); + } +} diff --git a/util/testsrc/org/aspectj/util/UtilTests.java b/util/testsrc/org/aspectj/util/UtilTests.java index dfc80b220..9d459aad7 100644 --- a/util/testsrc/org/aspectj/util/UtilTests.java +++ b/util/testsrc/org/aspectj/util/UtilTests.java @@ -11,22 +11,26 @@ * Xerox/PARC initial implementation * ******************************************************************/ - package org.aspectj.util; -import junit.framework.*; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; public class UtilTests extends TestCase { - public static Test suite() { - TestSuite suite = new TestSuite(UtilTests.class.getName()); - //$JUnit-BEGIN$ - suite.addTestSuite(FileUtilTest.class); - suite.addTestSuite(LangUtilTest.class); - //$JUnit-END$ - return suite; - } + public static Test suite() { + TestSuite suite = new TestSuite(UtilTests.class.getName()); + // $JUnit-BEGIN$ + suite.addTestSuite(FileUtilTest.class); + suite.addTestSuite(LangUtilTest.class); + suite.addTestSuite(GenericSignatureParserTest.class); + // $JUnit-END$ + return suite; + } - public UtilTests(String name) { super(name); } + public UtilTests(String name) { + super(name); + } -} +} -- 2.39.5