123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /* *******************************************************************
- * 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<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
- do {
- formalTypeParametersList.add(parseFormalTypeParameter());
- } while (!maybeEat(">"));
- classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()];
- formalTypeParametersList.toArray(classSig.formalTypeParameters);
- }
- classSig.superclassSignature = parseClassTypeSignature();
- List<ClassTypeSignature> superIntSigs = new ArrayList<ClassTypeSignature>();
- 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<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
- do {
- formalTypeParametersList.add(parseFormalTypeParameter());
- } while (!maybeEat(">"));
- formals = new FormalTypeParameter[formalTypeParametersList.size()];
- formalTypeParametersList.toArray(formals);
- }
- // Parameters
- eat("(");
- List<TypeSignature> paramList = new ArrayList<TypeSignature>();
- 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<FieldTypeSignature> throwsList = new ArrayList<FieldTypeSignature>();
- 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<FieldTypeSignature> optionalBounds = new ArrayList<FieldTypeSignature>();
- 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<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
- 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<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
- 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<TypeArgument> typeArgs = new ArrayList<TypeArgument>();
- 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<String> tokens = new ArrayList<String>();
- 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;
- }
-
- }
|