OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / cts / tools / signature-tools / src / signature / converter / dex / GenericSignatureParser.java
diff --git a/cts/tools/signature-tools/src/signature/converter/dex/GenericSignatureParser.java b/cts/tools/signature-tools/src/signature/converter/dex/GenericSignatureParser.java
new file mode 100644 (file)
index 0000000..773e2fc
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package signature.converter.dex;
+
+import static signature.converter.dex.DexUtil.getClassName;
+import static signature.converter.dex.DexUtil.getPackageName;
+import signature.model.IClassDefinition;
+import signature.model.IClassReference;
+import signature.model.IConstructor;
+import signature.model.IGenericDeclaration;
+import signature.model.IMethod;
+import signature.model.ITypeReference;
+import signature.model.ITypeVariableDefinition;
+import signature.model.ITypeVariableReference;
+import signature.model.impl.SigArrayType;
+import signature.model.impl.SigParameterizedType;
+import signature.model.impl.SigPrimitiveType;
+import signature.model.impl.SigTypeVariableDefinition;
+import signature.model.impl.SigWildcardType;
+import signature.model.impl.Uninitialized;
+import signature.model.util.ITypeFactory;
+
+import java.lang.reflect.GenericSignatureFormatError;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements a parser for the generics signature attribute. Uses a top-down,
+ * recursive descent parsing approach for the following grammar:
+ * 
+ * <pre>
+ * ClassSignature ::=
+ *     OptFormalTypeParams SuperclassSignature {SuperinterfaceSignature}.
+ * SuperclassSignature ::= ClassTypeSignature.
+ * SuperinterfaceSignature ::= ClassTypeSignature.
+ *
+ * OptFormalTypeParams ::=
+ *     ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+ *
+ * FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+ * ClassBound ::= ":" [FieldTypeSignature].
+ * InterfaceBound ::= ":" FieldTypeSignature.
+ *
+ * FieldTypeSignature ::=
+ *     ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
+ * ArrayTypeSignature ::= "[" TypSignature.
+ *
+ * ClassTypeSignature ::=
+ *     "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";".
+ *
+ * OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+ *
+ * TypeArgument ::= ([WildcardIndicator] FieldTypeSignature) | "*".
+ * WildcardIndicator ::= "+" | "-".
+ *
+ * TypeVariableSignature ::= "T" Ident ";".
+ *
+ * TypSignature ::= FieldTypeSignature | BaseType.
+ * BaseType ::= "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z".
+ *
+ * MethodTypeSignature ::=
+ *     OptFormalTypeParams "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
+ * ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
+ *
+ * ReturnType ::= TypSignature | VoidDescriptor.
+ * VoidDescriptor ::= "V".
+ * </pre>
+ */
+public class GenericSignatureParser {
+
+    public List<ITypeReference> exceptionTypes;
+    public List<ITypeReference> parameterTypes;
+    public List<ITypeVariableDefinition> formalTypeParameters;
+    public ITypeReference returnType;
+    public ITypeReference fieldType;
+    public List<ITypeReference> interfaceTypes;
+    public ITypeReference superclassType;
+
+    private IGenericDeclaration genericDecl;
+
+    /*
+     * Parser:
+     */
+    private char symbol; // 0: eof; else valid term symbol or first char of
+    // identifier.
+    private String identifier;
+
+
+    /*
+     * Scanner: eof is private to the scan methods and it's set only when a scan
+     * is issued at the end of the buffer.
+     */
+    private boolean eof;
+
+    private char[] buffer;
+    private int pos;
+
+    private final ITypeFactory factory;
+    private final IClassInitializer classFinder;
+    private boolean parseForField;
+
+
+    public GenericSignatureParser(ITypeFactory factory,
+            IClassInitializer classFinder) {
+        this.factory = factory;
+        this.classFinder = classFinder;
+    }
+
+    private void setInput(IGenericDeclaration genericDecl, String input) {
+        if (input != null) {
+            this.genericDecl = genericDecl;
+            this.buffer = input.toCharArray();
+            this.eof = false;
+            scanSymbol();
+        } else {
+            this.eof = true;
+        }
+    }
+
+    public ITypeReference parseNonGenericType(String typeSignature) {
+        setInput(null, typeSignature);
+        ITypeReference type = parsePrimitiveType();
+        if (type == null) {
+            type = parseFieldTypeSignature();
+        }
+        return type;
+    }
+
+    public ITypeReference parseNonGenericReturnType(String typeSignature) {
+        setInput(null, typeSignature);
+        ITypeReference returnType = parsePrimitiveType();
+        if (returnType == null) {
+            returnType = parseReturnType();
+        }
+        return returnType;
+    }
+
+    private ITypeReference parsePrimitiveType() {
+        switch (symbol) {
+        case 'B':
+            scanSymbol();
+            return SigPrimitiveType.BYTE_TYPE;
+        case 'C':
+            scanSymbol();
+            return SigPrimitiveType.CHAR_TYPE;
+        case 'D':
+            scanSymbol();
+            return SigPrimitiveType.DOUBLE_TYPE;
+        case 'F':
+            scanSymbol();
+            return SigPrimitiveType.FLOAT_TYPE;
+        case 'I':
+            scanSymbol();
+            return SigPrimitiveType.INT_TYPE;
+        case 'J':
+            scanSymbol();
+            return SigPrimitiveType.LONG_TYPE;
+        case 'S':
+            scanSymbol();
+            return SigPrimitiveType.SHORT_TYPE;
+        case 'Z':
+            scanSymbol();
+            return SigPrimitiveType.BOOLEAN_TYPE;
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * Parses the generic signature of a class and creates the data structure
+     * representing the signature.
+     * 
+     * @param classToProcess
+     *            the GenericDeclaration calling this method
+     * @param signature
+     *            the generic signature of the class
+     */
+    public void parseForClass(IClassDefinition classToProcess,
+            String signature) {
+        setInput(classToProcess, signature);
+        if (!eof) {
+            parseClassSignature();
+        } else {
+            throw new IllegalStateException("Generic signature is invalid!");
+        }
+    }
+
+    /**
+     * Parses the generic signature of a method and creates the data structure
+     * representing the signature.
+     * 
+     * @param genericDecl
+     *            the GenericDeclaration calling this method
+     * @param signature
+     *            the generic signature of the class
+     */
+    public void parseForMethod(IMethod genericDecl, String signature) {
+        setInput(genericDecl, signature);
+        if (!eof) {
+            parseMethodTypeSignature();
+        } else {
+            throw new IllegalStateException("Generic signature is invalid!");
+        }
+    }
+
+    /**
+     * Parses the generic signature of a constructor and creates the data
+     * structure representing the signature.
+     * 
+     * @param genericDecl
+     *            the GenericDeclaration calling this method
+     * @param signature
+     *            the generic signature of the class
+     */
+    public void parseForConstructor(IConstructor genericDecl,
+            String signature) {
+        setInput(genericDecl, signature);
+        if (!eof) {
+            parseMethodTypeSignature();
+        } else {
+            throw new IllegalStateException("Generic signature is invalid!");
+        }
+    }
+
+    /**
+     * Parses the generic signature of a field and creates the data structure
+     * representing the signature.
+     * 
+     * @param genericDecl
+     *            the GenericDeclaration calling this method
+     * @param signature
+     *            the generic signature of the class
+     */
+    public void parseForField(IClassDefinition genericDecl, String signature) {
+        parseForField = true;
+        setInput(genericDecl, signature);
+        try {
+            if (!eof) {
+                this.fieldType = parseFieldTypeSignature();
+            } else {
+                throw new IllegalStateException(
+                        "Generic signature is invalid!");
+            }
+        } finally {
+            parseForField = false;
+        }
+    }
+
+    private void parseClassSignature() {
+        // ClassSignature ::=
+        // OptFormalTypeParameters SuperclassSignature
+        // {SuperinterfaceSignature}.
+
+        parseOptFormalTypeParameters();
+
+        // SuperclassSignature ::= ClassTypeSignature.
+        this.superclassType = parseClassTypeSignature();
+
+        interfaceTypes = new ArrayList<ITypeReference>(16);
+        while (symbol > 0) {
+            // SuperinterfaceSignature ::= ClassTypeSignature.
+            interfaceTypes.add(parseClassTypeSignature());
+        }
+    }
+
+    private void parseOptFormalTypeParameters() {
+        // OptFormalTypeParameters ::=
+        // ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+
+        List<ITypeVariableDefinition> typeParameters =
+                new ArrayList<ITypeVariableDefinition>();
+
+        if (symbol == '<') {
+            scanSymbol();
+            typeParameters.add(parseFormalTypeParameter());
+            while ((symbol != '>') && (symbol > 0)) {
+                typeParameters.add(parseFormalTypeParameter());
+            }
+            expect('>');
+        }
+
+        formalTypeParameters = typeParameters;
+    }
+
+    private SigTypeVariableDefinition parseFormalTypeParameter() {
+        // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+
+        scanIdentifier();
+        String name = identifier.intern();
+        SigTypeVariableDefinition typeVariable = factory.getTypeVariable(name,
+                genericDecl);
+
+        List<ITypeReference> bounds = new ArrayList<ITypeReference>();
+
+        // ClassBound ::= ":" [FieldTypeSignature].
+        expect(':');
+        if (symbol == 'L' || symbol == '[' || symbol == 'T') {
+            bounds.add(parseFieldTypeSignature());
+        }
+
+        while (symbol == ':') {
+            // InterfaceBound ::= ":" FieldTypeSignature.
+            scanSymbol();
+            bounds.add(parseFieldTypeSignature());
+        }
+        typeVariable.setUpperBounds(bounds);
+        return typeVariable;
+    }
+
+    /**
+     * Returns the generic declaration for the type variable with the specified
+     * name.
+     * 
+     * @param variableName
+     *            the name of the type variable
+     * @param declaration
+     *            the declaration to start searching
+     * @return the declaration which defines the specified type variable
+     */
+    private IGenericDeclaration getDeclarationOfTypeVariable(
+            String variableName, IClassDefinition declaration) {
+        assert variableName != null;
+        assert declaration != null;
+
+        if (!Uninitialized.isInitialized(declaration.getTypeParameters())) {
+            declaration = classFinder.initializeClass(declaration
+                    .getPackageName(), declaration.getName());
+        }
+
+        for (ITypeVariableDefinition typeVariable : declaration
+                .getTypeParameters()) {
+            if (variableName.equals(typeVariable.getName())) {
+                return declaration;
+            }
+        }
+        return getDeclarationOfTypeVariable(variableName, declaration
+                .getDeclaringClass());
+    }
+
+    private ITypeReference parseFieldTypeSignature() {
+        // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature
+        // | TypeVariableSignature.
+
+        switch (symbol) {
+        case 'L':
+            return parseClassTypeSignature();
+        case '[':
+            // ArrayTypeSignature ::= "[" TypSignature.
+            scanSymbol();
+            SigArrayType arrayType = factory.getArrayType(parseTypeSignature());
+            return arrayType;
+        case 'T':
+            return parseTypeVariableSignature();
+        default:
+            throw new GenericSignatureFormatError();
+        }
+    }
+
+    private ITypeReference parseClassTypeSignature() {
+        // ClassTypeSignature ::= "L" {Ident "/"} Ident
+        // OptTypeArguments {"." Ident OptTypeArguments} ";".
+
+        expect('L');
+
+        StringBuilder qualIdent = new StringBuilder("L");
+        scanIdentifier();
+        while (symbol == '/') {
+            scanSymbol();
+            qualIdent.append(identifier).append("/");
+            scanIdentifier();
+        }
+
+        qualIdent.append(this.identifier);
+
+
+        List<ITypeReference> typeArgs = parseOptTypeArguments();
+
+        ITypeReference parentType = null;
+
+        String packageName = getPackageName(qualIdent.toString() + ";");
+        String className = getClassName(qualIdent.toString() + ";");
+
+        if (typeArgs.isEmpty()) {
+            parentType = factory.getClassReference(packageName, className);
+        } else {
+            IClassReference rawType = factory.getClassReference(packageName,
+                    className);
+            SigParameterizedType parameterizedType = factory
+                    .getParameterizedType(null, rawType, typeArgs);
+            parentType = parameterizedType;
+        }
+
+        ITypeReference typeToReturn = parentType;
+
+
+        // if owner type is a parameterized type, the types are separated by '.'
+        while (symbol == '.') {
+            // Deal with Member Classes:
+            scanSymbol();
+            scanIdentifier();
+            qualIdent.append("$").append(identifier);
+            typeArgs = parseOptTypeArguments();
+            ITypeReference memberType = null;
+
+            packageName = getPackageName(qualIdent.toString() + ";");
+            className = getClassName(qualIdent.toString() + ";");
+
+            if (typeArgs.isEmpty()) {
+                memberType = factory.getClassReference(packageName, className);
+            } else {
+                IClassReference rawType = factory.getClassReference(
+                        packageName, className);
+                SigParameterizedType parameterizedType = factory
+                        .getParameterizedType(parentType, rawType, typeArgs);
+                memberType = parameterizedType;
+            }
+            typeToReturn = memberType;
+        }
+
+        expect(';');
+
+        return typeToReturn;
+    }
+
+    private List<ITypeReference> parseOptTypeArguments() {
+        // OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+
+        List<ITypeReference> typeArgs = new ArrayList<ITypeReference>(8);
+        if (symbol == '<') {
+            scanSymbol();
+
+            typeArgs.add(parseTypeArgument());
+            while ((symbol != '>') && (symbol > 0)) {
+                typeArgs.add(parseTypeArgument());
+            }
+            expect('>');
+        }
+        return typeArgs;
+    }
+
+    private ITypeReference parseTypeArgument() {
+        // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
+        List<ITypeReference> extendsBound = new ArrayList<ITypeReference>(1);
+        ITypeReference superBound = null;
+        if (symbol == '*') {
+            scanSymbol();
+            extendsBound.add(factory.getClassReference("java.lang", "Object"));
+            SigWildcardType wildcardType = factory.getWildcardType(superBound,
+                    extendsBound);
+            return wildcardType;
+        } else if (symbol == '+') {
+            scanSymbol();
+            extendsBound.add(parseFieldTypeSignature());
+            SigWildcardType wildcardType = factory.getWildcardType(superBound,
+                    extendsBound);
+            return wildcardType;
+        } else if (symbol == '-') {
+            scanSymbol();
+            superBound = parseFieldTypeSignature();
+            extendsBound.add(factory.getClassReference("java.lang", "Object"));
+            SigWildcardType wildcardType = factory.getWildcardType(superBound,
+                    extendsBound);
+            return wildcardType;
+        } else {
+            return parseFieldTypeSignature();
+        }
+    }
+
+    private ITypeVariableReference parseTypeVariableSignature() {
+        // TypeVariableSignature ::= "T" Ident ";".
+        expect('T');
+        scanIdentifier();
+        expect(';');
+
+        IGenericDeclaration declaration = genericDecl;
+
+        if (!factory.containsTypeVariableDefinition(identifier, declaration)) {
+            // since a field is not a generic declaration, i need to treat it
+            // differently.
+            // the generic declaration
+            if (parseForField) {
+                declaration = getDeclarationOfTypeVariable(identifier,
+                        (IClassDefinition) genericDecl);
+            } else {
+                declaration = getDeclarationOfTypeVariable(identifier,
+                        genericDecl.getDeclaringClass());
+            }
+            // just create type variable
+            factory.getTypeVariable(identifier, declaration);
+        }
+
+        return factory.getTypeVariableReference(identifier, declaration);
+    }
+
+    private ITypeReference parseTypeSignature() {
+        switch (symbol) {
+        case 'B':
+            scanSymbol();
+            return SigPrimitiveType.BYTE_TYPE;
+        case 'C':
+            scanSymbol();
+            return SigPrimitiveType.CHAR_TYPE;
+        case 'D':
+            scanSymbol();
+            return SigPrimitiveType.DOUBLE_TYPE;
+        case 'F':
+            scanSymbol();
+            return SigPrimitiveType.FLOAT_TYPE;
+        case 'I':
+            scanSymbol();
+            return SigPrimitiveType.INT_TYPE;
+        case 'J':
+            scanSymbol();
+            return SigPrimitiveType.LONG_TYPE;
+        case 'S':
+            scanSymbol();
+            return SigPrimitiveType.SHORT_TYPE;
+        case 'Z':
+            scanSymbol();
+            return SigPrimitiveType.BOOLEAN_TYPE;
+        default:
+            // Not an elementary type, but a FieldTypeSignature.
+            return parseFieldTypeSignature();
+        }
+    }
+
+    private void parseMethodTypeSignature() {
+        // MethodTypeSignature ::= [FormalTypeParameters]
+        // "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
+
+        parseOptFormalTypeParameters();
+
+        parameterTypes = new ArrayList<ITypeReference>(16);
+        expect('(');
+        while (symbol != ')' && (symbol > 0)) {
+            parameterTypes.add(parseTypeSignature());
+        }
+        expect(')');
+
+        returnType = parseReturnType();
+
+        exceptionTypes = new ArrayList<ITypeReference>(8);
+        while (symbol == '^') {
+            scanSymbol();
+
+            // ThrowsSignature ::= ("^" ClassTypeSignature) |
+            // ("^" TypeVariableSignature).
+            if (symbol == 'T') {
+                exceptionTypes.add(parseTypeVariableSignature());
+            } else {
+                exceptionTypes.add(parseClassTypeSignature());
+            }
+        }
+    }
+
+    private ITypeReference parseReturnType() {
+        // ReturnType ::= TypeSignature | "V".
+        if (symbol != 'V') {
+            return parseTypeSignature();
+        } else {
+            scanSymbol();
+            return SigPrimitiveType.VOID_TYPE;
+        }
+    }
+
+
+    //
+    // Scanner:
+    //
+
+    private void scanSymbol() {
+        if (!eof) {
+            if (pos < buffer.length) {
+                symbol = buffer[pos];
+                pos++;
+            } else {
+                symbol = 0;
+                eof = true;
+            }
+        } else {
+            throw new GenericSignatureFormatError();
+        }
+    }
+
+    private void expect(char c) {
+        if (symbol == c) {
+            scanSymbol();
+        } else {
+            throw new GenericSignatureFormatError();
+        }
+    }
+
+    private boolean isStopSymbol(char ch) {
+        switch (ch) {
+        case ':':
+        case '/':
+        case ';':
+        case '<':
+        case '.':
+            return true;
+        }
+        return false;
+    }
+
+    // PRE: symbol is the first char of the identifier.
+    // POST: symbol = the next symbol AFTER the identifier.
+    private void scanIdentifier() {
+        if (!eof) {
+            StringBuilder identBuf = new StringBuilder(32);
+            if (!isStopSymbol(symbol)) {
+                identBuf.append(symbol);
+                do {
+                    char ch = buffer[pos];
+                    if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A')
+                            && (ch <= 'Z') || !isStopSymbol(ch)) {
+                        identBuf.append(buffer[pos]);
+                        pos++;
+                    } else {
+                        identifier = identBuf.toString();
+                        scanSymbol();
+                        return;
+                    }
+                } while (pos != buffer.length);
+                identifier = identBuf.toString();
+                symbol = 0;
+                eof = true;
+            } else {
+                // Ident starts with incorrect char.
+                symbol = 0;
+                eof = true;
+                throw new GenericSignatureFormatError();
+            }
+        } else {
+            throw new GenericSignatureFormatError();
+        }
+    }
+}