jdk/src/share/classes/sun/reflect/annotation/TypeAnnotationParser.java
changeset 15510 898d924a7efd
child 17450 0f704861915e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/reflect/annotation/TypeAnnotationParser.java	Tue Jan 29 10:32:49 2013 +0100
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.reflect.annotation;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import sun.misc.JavaLangAccess;
+import sun.reflect.ConstantPool;
+import static sun.reflect.annotation.TypeAnnotation.*;
+
+/**
+ * TypeAnnotationParser implements the logic needed to parse
+ * TypeAnnotations from an array of bytes.
+ */
+public class TypeAnnotationParser {
+    private static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0];
+
+    /**
+     * Build an AnnotatedType from the parameters supplied.
+     *
+     * This method and {@code buildAnnotatedTypes} are probably
+     * the entry points you are looking for.
+     *
+     * @param rawAnnotations the byte[] encoding of all type annotations on this declaration
+     * @param cp the ConstantPool needed to parse the embedded Annotation
+     * @param decl the dclaration this type annotation is on
+     * @param container the Class this type annotation is on (may be the same as decl)
+     * @param type the type the AnnotatedType corresponds to
+     * @param filter the type annotation targets included in this AnnotatedType
+     */
+    public static AnnotatedType buildAnnotatedType(byte[] rawAnnotations,
+            ConstantPool cp,
+            AnnotatedElement decl,
+            Class<?> container,
+            Type type,
+            TypeAnnotationTarget filter) {
+        TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations,
+                                                    cp,
+                                                    decl,
+                                                    container);
+        List<TypeAnnotation> l = new ArrayList<>(tas.length);
+        for (TypeAnnotation t : tas) {
+            TypeAnnotationTargetInfo ti = t.getTargetInfo();
+            if (ti.getTarget() == filter)
+                l.add(t);
+        }
+        TypeAnnotation[] typeAnnotations = l.toArray(new TypeAnnotation[0]);
+        return AnnotatedTypeFactory.buildAnnotatedType(type,
+                                                       LocationInfo.BASE_LOCATION,
+                                                       typeAnnotations,
+                                                       typeAnnotations,
+                                                       decl);
+    }
+
+    /**
+     * Build an array of AnnotatedTypes from the parameters supplied.
+     *
+     * This method and {@code buildAnnotatedType} are probably
+     * the entry points you are looking for.
+     *
+     * @param rawAnnotations the byte[] encoding of all type annotations on this declaration
+     * @param cp the ConstantPool needed to parse the embedded Annotation
+     * @param decl the declaration this type annotation is on
+     * @param container the Class this type annotation is on (may be the same as decl)
+     * @param types the Types the AnnotatedTypes corresponds to
+     * @param filter the type annotation targets that included in this AnnotatedType
+     */
+    public static AnnotatedType[] buildAnnotatedTypes(byte[] rawAnnotations,
+            ConstantPool cp,
+            AnnotatedElement decl,
+            Class<?> container,
+            Type[] types,
+            TypeAnnotationTarget filter) {
+        int size = types.length;
+        AnnotatedType[] result = new AnnotatedType[size];
+        Arrays.fill(result, AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE);
+        @SuppressWarnings("rawtypes")
+        ArrayList[] l = new ArrayList[size]; // array of ArrayList<TypeAnnotation>
+
+        TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations,
+                                                    cp,
+                                                    decl,
+                                                    container);
+        for (TypeAnnotation t : tas) {
+            TypeAnnotationTargetInfo ti = t.getTargetInfo();
+            if (ti.getTarget() == filter) {
+                int pos = ti.getCount();
+                if (l[pos] == null) {
+                    ArrayList<TypeAnnotation> tmp = new ArrayList<>(tas.length);
+                    l[pos] = tmp;
+                }
+                @SuppressWarnings("unchecked")
+                ArrayList<TypeAnnotation> tmp = l[pos];
+                tmp.add(t);
+            }
+        }
+        for (int i = 0; i < size; i++) {
+            @SuppressWarnings("unchecked")
+            ArrayList<TypeAnnotation> list = l[i];
+            if (list != null) {
+                TypeAnnotation[] typeAnnotations = list.toArray(new TypeAnnotation[0]);
+                result[i] = AnnotatedTypeFactory.buildAnnotatedType(types[i],
+                                                                    LocationInfo.BASE_LOCATION,
+                                                                    typeAnnotations,
+                                                                    typeAnnotations,
+                                                                    decl);
+            }
+        }
+        return result;
+    }
+
+    // Class helpers
+
+    /**
+     * Build an AnnotatedType for the class decl's supertype.
+     *
+     * @param rawAnnotations the byte[] encoding of all type annotations on this declaration
+     * @param cp the ConstantPool needed to parse the embedded Annotation
+     * @param decl the Class which annotated supertype is being built
+     */
+    public static AnnotatedType buildAnnotatedSuperclass(byte[] rawAnnotations,
+            ConstantPool cp,
+            Class<?> decl) {
+        Type supertype = decl.getGenericSuperclass();
+        if (supertype == null)
+            return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE;
+        return buildAnnotatedType(rawAnnotations,
+                                  cp,
+                                  decl,
+                                  decl,
+                                  supertype,
+                                  TypeAnnotationTarget.CLASS_EXTENDS);
+    }
+
+    /**
+     * Build an array of AnnotatedTypes for the class decl's implemented
+     * interfaces.
+     *
+     * @param rawAnnotations the byte[] encoding of all type annotations on this declaration
+     * @param cp the ConstantPool needed to parse the embedded Annotation
+     * @param decl the Class whose annotated implemented interfaces is being built
+     */
+    public static AnnotatedType[] buildAnnotatedInterfaces(byte[] rawAnnotations,
+            ConstantPool cp,
+            Class<?> decl) {
+        return buildAnnotatedTypes(rawAnnotations,
+                                   cp,
+                                   decl,
+                                   decl,
+                                   decl.getGenericInterfaces(),
+                                   TypeAnnotationTarget.CLASS_IMPLEMENTS);
+    }
+
+    // TypeVariable helpers
+
+    /**
+     * Parse regular annotations on a TypeVariable declared on genericDecl.
+     *
+     * Regular Annotations on TypeVariables are stored in the type
+     * annotation byte[] in the class file.
+     *
+     * @param genericsDecl the declaration declaring the type variable
+     * @param typeVarIndex the 0-based index of this type variable in the declaration
+     */
+    public static <D extends GenericDeclaration> Annotation[] parseTypeVariableAnnotations(D genericDecl,
+            int typeVarIndex) {
+        AnnotatedElement decl;
+        TypeAnnotationTarget predicate;
+        if (genericDecl instanceof Class) {
+            decl = (Class<?>)genericDecl;
+            predicate = TypeAnnotationTarget.CLASS_TYPE_PARAMETER;
+        } else if (genericDecl instanceof Executable) {
+            decl = (Executable)genericDecl;
+            predicate = TypeAnnotationTarget.METHOD_TYPE_PARAMETER;
+        } else {
+            throw new AssertionError("Unknown GenericDeclaration " + genericDecl + "\nthis should not happen.");
+        }
+        List<TypeAnnotation> typeVarAnnos = TypeAnnotation.filter(parseAllTypeAnnotations(decl),
+                                                                  predicate);
+        List<Annotation> res = new ArrayList<>(typeVarAnnos.size());
+        for (TypeAnnotation t : typeVarAnnos)
+            if (t.getTargetInfo().getCount() == typeVarIndex)
+                res.add(t.getAnnotation());
+        return res.toArray(new Annotation[0]);
+    }
+
+    /**
+     * Build an array of AnnotatedTypes for the declaration decl's bounds.
+     *
+     * @param bounds the bounds corresponding to the annotated bounds
+     * @param decl the declaration whose annotated bounds is being built
+     * @param typeVarIndex the index of this type variable on the decl
+     */
+    public static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBounds(Type[] bounds,
+            D decl,
+            int typeVarIndex) {
+        return parseAnnotatedBounds(bounds, decl, typeVarIndex, LocationInfo.BASE_LOCATION);
+    }
+    //helper for above
+    static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBounds(Type[] bounds,
+            D decl,
+            int typeVarIndex,
+            LocationInfo loc) {
+        List<TypeAnnotation> candidates = fetchBounds(decl);
+        if (bounds != null) {
+            int startIndex = 0;
+            AnnotatedType[] res = new AnnotatedType[bounds.length];
+            Arrays.fill(res, AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE);
+
+            // Adjust bounds index
+            //
+            // Figure out if the type annotations for this bound starts with 0
+            // or 1. The spec says within a bound the 0:th type annotation will
+            // always be on an bound of a Class type (not Interface type). So
+            // if the programmer starts with an Interface type for the first
+            // (and following) bound(s) the implicit Object bound is considered
+            // the first (that is 0:th) bound and type annotations start on
+            // index 1.
+            if (bounds.length > 0) {
+                Type b0 = bounds[0];
+                if (!(b0 instanceof Class<?>)) {
+                    startIndex = 1;
+                } else {
+                    Class<?> c = (Class<?>)b0;
+                    if (c.isInterface()) {
+                        startIndex = 1;
+                    }
+                }
+            }
+
+            for (int i = 0; i < bounds.length; i++) {
+                List<TypeAnnotation> l = new ArrayList<>(candidates.size());
+                for (TypeAnnotation t : candidates) {
+                    TypeAnnotationTargetInfo tInfo = t.getTargetInfo();
+                    if (tInfo.getSecondaryIndex() == i + startIndex &&
+                            tInfo.getCount() == typeVarIndex) {
+                        l.add(t);
+                    }
+                    res[i] = AnnotatedTypeFactory.buildAnnotatedType(bounds[i],
+                                                                     loc,
+                                                                     l.toArray(new TypeAnnotation[0]),
+                                                                     candidates.toArray(new TypeAnnotation[0]),
+                                                                     (AnnotatedElement)decl);
+                }
+            }
+            return res;
+        }
+        return new AnnotatedType[0];
+    }
+    private static <D extends GenericDeclaration> List<TypeAnnotation> fetchBounds(D decl) {
+        AnnotatedElement boundsDecl;
+        TypeAnnotationTarget target;
+        if (decl instanceof Class) {
+            target = TypeAnnotationTarget.CLASS_PARAMETER_BOUND;
+            boundsDecl = (Class)decl;
+        } else {
+            target = TypeAnnotationTarget.METHOD_PARAMETER_BOUND;
+            boundsDecl = (Executable)decl;
+        }
+        return TypeAnnotation.filter(TypeAnnotationParser.parseAllTypeAnnotations(boundsDecl), target);
+    }
+
+    /*
+     * Parse all type annotations on the declaration supplied. This is needed
+     * when you go from for example an annotated return type on a method that
+     * is a type variable declared on the class. In this case you need to
+     * 'jump' to the decl of the class and parse all type annotations there to
+     * find the ones that are applicable to the type variable.
+     */
+    static TypeAnnotation[] parseAllTypeAnnotations(AnnotatedElement decl) {
+        Class<?> container;
+        byte[] rawBytes;
+        JavaLangAccess javaLangAccess = sun.misc.SharedSecrets.getJavaLangAccess();
+        if (decl instanceof Class) {
+            container = (Class<?>)decl;
+            rawBytes = javaLangAccess.getRawClassTypeAnnotations(container);
+        } else if (decl instanceof Executable) {
+            container = ((Executable)decl).getDeclaringClass();
+            rawBytes = javaLangAccess.getRawExecutableTypeAnnotations((Executable)decl);
+        } else {
+            // Should not reach here. Assert?
+            return EMPTY_TYPE_ANNOTATION_ARRAY;
+        }
+        return parseTypeAnnotations(rawBytes, javaLangAccess.getConstantPool(container),
+                                    decl, container);
+    }
+
+    /* Parse type annotations encoded as an array of bytes */
+    private static TypeAnnotation[] parseTypeAnnotations(byte[] rawAnnotations,
+            ConstantPool cp,
+            AnnotatedElement baseDecl,
+            Class<?> container) {
+        if (rawAnnotations == null)
+            return EMPTY_TYPE_ANNOTATION_ARRAY;
+
+        ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
+        int annotationCount = buf.getShort() & 0xFFFF;
+        List<TypeAnnotation> typeAnnotations = new ArrayList<>(annotationCount);
+
+        // Parse each TypeAnnotation
+        for (int i = 0; i < annotationCount; i++) {
+             TypeAnnotation ta = parseTypeAnnotation(buf, cp, baseDecl, container);
+             if (ta != null)
+                 typeAnnotations.add(ta);
+        }
+
+        return typeAnnotations.toArray(EMPTY_TYPE_ANNOTATION_ARRAY);
+    }
+
+
+    // Helper
+    static Map<Class<? extends Annotation>, Annotation> mapTypeAnnotations(TypeAnnotation[] typeAnnos) {
+        Map<Class<? extends Annotation>, Annotation> result =
+            new LinkedHashMap<>();
+        for (TypeAnnotation t : typeAnnos) {
+            Annotation a = t.getAnnotation();
+            Class<? extends Annotation> klass = a.annotationType();
+            AnnotationType type = AnnotationType.getInstance(klass);
+            if (type.retention() == RetentionPolicy.RUNTIME)
+                if (result.put(klass, a) != null)
+                    throw new AnnotationFormatError("Duplicate annotation for class: "+klass+": " + a);
+        }
+        return result;
+    }
+
+    // Position codes
+    // Regular type parameter annotations
+    private static final byte CLASS_TYPE_PARAMETER = 0x00;
+    private static final byte METHOD_TYPE_PARAMETER = 0x01;
+    // Type Annotations outside method bodies
+    private static final byte CLASS_EXTENDS = 0x10;
+    private static final byte CLASS_TYPE_PARAMETER_BOUND = 0x11;
+    private static final byte METHOD_TYPE_PARAMETER_BOUND = 0x12;
+    private static final byte FIELD = 0x13;
+    private static final byte METHOD_RETURN = 0x14;
+    private static final byte METHOD_RECEIVER = 0x15;
+    private static final byte METHOD_FORMAL_PARAMETER = 0x16;
+    private static final byte THROWS = 0x17;
+    // Type Annotations inside method bodies
+    private static final byte LOCAL_VARIABLE = (byte)0x40;
+    private static final byte RESOURCE_VARIABLE = (byte)0x41;
+    private static final byte EXCEPTION_PARAMETER = (byte)0x42;
+    private static final byte CAST = (byte)0x43;
+    private static final byte INSTANCEOF = (byte)0x44;
+    private static final byte NEW = (byte)0x45;
+    private static final byte CONSTRUCTOR_REFERENCE_RECEIVER = (byte)0x46;
+    private static final byte METHOD_REFERENCE_RECEIVER = (byte)0x47;
+    private static final byte LAMBDA_FORMAL_PARAMETER = (byte)0x48;
+    private static final byte METHOD_REFERENCE = (byte)0x49;
+    private static final byte METHOD_REFERENCE_TYPE_ARGUMENT = (byte)0x50;
+
+    private static TypeAnnotation parseTypeAnnotation(ByteBuffer buf,
+            ConstantPool cp,
+            AnnotatedElement baseDecl,
+            Class<?> container) {
+        TypeAnnotationTargetInfo ti = parseTargetInfo(buf);
+        LocationInfo locationInfo = LocationInfo.parseLocationInfo(buf);
+        Annotation a = AnnotationParser.parseAnnotation(buf, cp, container, false);
+        if (ti == null) // Inside a method for example
+            return null;
+        return new TypeAnnotation(ti, locationInfo, a, baseDecl);
+    }
+
+    private static TypeAnnotationTargetInfo parseTargetInfo(ByteBuffer buf) {
+        byte posCode = buf.get();
+        switch(posCode) {
+        case CLASS_TYPE_PARAMETER:
+        case METHOD_TYPE_PARAMETER: {
+            byte index = buf.get();
+            TypeAnnotationTargetInfo res;
+            if (posCode == CLASS_TYPE_PARAMETER)
+                res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_TYPE_PARAMETER,
+                        index);
+            else
+                res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_TYPE_PARAMETER,
+                        index);
+            return res;
+            } // unreachable break;
+        case CLASS_EXTENDS: {
+            short index = buf.getShort();
+            if (index == -1) {
+                return new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_EXTENDS);
+            } else if (index >= 0) {
+                TypeAnnotationTargetInfo res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_IMPLEMENTS,
+                        index);
+                return res;
+            }} break;
+        case CLASS_TYPE_PARAMETER_BOUND:
+            return parse2ByteTarget(TypeAnnotationTarget.CLASS_PARAMETER_BOUND, buf);
+        case METHOD_TYPE_PARAMETER_BOUND:
+            return parse2ByteTarget(TypeAnnotationTarget.METHOD_PARAMETER_BOUND, buf);
+        case FIELD:
+            return new TypeAnnotationTargetInfo(TypeAnnotationTarget.FIELD_TYPE);
+        case METHOD_RETURN:
+            return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RETURN_TYPE);
+        case METHOD_RECEIVER:
+            return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RECEIVER_TYPE);
+        case METHOD_FORMAL_PARAMETER: {
+            // Todo
+            byte index = buf.get();
+            } break;
+        case THROWS:
+            return parseShortTarget(TypeAnnotationTarget.THROWS, buf);
+
+        /*
+         * The ones below are inside method bodies, we don't care about them for core reflection
+         * other than adjusting for them in the byte stream.
+         */
+        case LOCAL_VARIABLE:
+        case RESOURCE_VARIABLE:
+            short length = buf.getShort();
+            for (int i = 0; i < length; ++i) {
+                short offset = buf.getShort();
+                short varLength = buf.getShort();
+                short index = buf.getShort();
+            }
+            break;
+        case EXCEPTION_PARAMETER: {
+            byte index = buf.get();
+            } break;
+        case CAST:
+        case INSTANCEOF:
+        case NEW: {
+            short offset = buf.getShort();
+            } break;
+        case CONSTRUCTOR_REFERENCE_RECEIVER:
+        case METHOD_REFERENCE_RECEIVER: {
+            short offset = buf.getShort();
+            byte index = buf.get();
+            } break;
+        case LAMBDA_FORMAL_PARAMETER: {
+            byte index = buf.get();
+            } break;
+        case METHOD_REFERENCE:
+            // This one isn't in the spec yet
+            break;
+        case METHOD_REFERENCE_TYPE_ARGUMENT: {
+            short offset = buf.getShort();
+            byte index = buf.get();
+            } break;
+
+        default:
+            // will throw error below
+            break;
+        }
+        throw new AnnotationFormatError("Could not parse bytes for type annotations");
+    }
+
+    private static TypeAnnotationTargetInfo parseShortTarget(TypeAnnotationTarget target, ByteBuffer buf) {
+        short index = buf.getShort();
+        return new TypeAnnotationTargetInfo(target, index);
+    }
+    private static TypeAnnotationTargetInfo parse2ByteTarget(TypeAnnotationTarget target, ByteBuffer buf) {
+        byte count = buf.get();
+        byte secondaryIndex = buf.get();
+        return new TypeAnnotationTargetInfo(target,
+                                            count,
+                                            secondaryIndex);
+    }
+}