]> source.dussan.org Git - aspectj.git/commitdiff
246125: promoted generic sig parsing code to util
authoraclement <aclement>
Mon, 20 Oct 2008 18:31:31 +0000 (18:31 +0000)
committeraclement <aclement>
Mon, 20 Oct 2008 18:31:31 +0000 (18:31 +0000)
util/src/org/aspectj/util/GenericSignature.java [new file with mode: 0644]
util/src/org/aspectj/util/GenericSignatureParser.java [new file with mode: 0644]
util/testsrc/org/aspectj/util/GenericSignatureParserTest.java [new file with mode: 0644]
util/testsrc/org/aspectj/util/UtilTests.java

diff --git a/util/src/org/aspectj/util/GenericSignature.java b/util/src/org/aspectj/util/GenericSignature.java
new file mode 100644 (file)
index 0000000..0e2433a
--- /dev/null
@@ -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 (file)
index 0000000..d8e5bd0
--- /dev/null
@@ -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 (file)
index 0000000..c46c97b
--- /dev/null
@@ -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("<T:Ljava/lang/String;:Ljava/util/Comparable;>");
+               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("<E:Ljava/lang/String;:Ljava/lang/Number<TE;>;>Ljava/util/List<TE;>;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<TE;>;", isig.classSignature);
+               assertEquals("Ljava/util/List<TE;>;", 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("<T:>(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("<T:>()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("<T:>(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</;");
+                       fail("Expected IllegalStateException");
+               } catch (IllegalStateException ex) {
+                       assertTrue(ex.getMessage().indexOf("Ljava/util/List</;") != -1);
+               }
+       }
+
+       public void testPr107784() {
+               parser
+                               .parseAsMethodSignature("(Lcom/cibc/responders/mapping/CommonDataBeanScenario;Ljava/lang/Object;)Lcom/cibc/responders/response/Formatter<[BLjava/lang/Object;>;");
+               parser.parseAsClassSignature("<Parent:Ljava/lang/Object;Child:Ljava/lang/Object;>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();
+       }
+}
index dfc80b220400cacd3952a86cbd7b9a5051557eed..9d459aad730dd43402a7fb00ed913b846d40f082 100644 (file)
  *     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);
+       }
 
-}  
+}