7193719: Support repeating annotations in javax.lang.model
authorjfranck
Mon, 14 Jan 2013 19:52:36 +0100
changeset 15355 a4757c33cae9
parent 15354 52a04c670c05
child 15356 cf312dc54c60
7193719: Support repeating annotations in javax.lang.model Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java
langtools/src/share/classes/javax/lang/model/element/Element.java
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jan 10 19:38:57 2013 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Jan 14 19:52:36 2013 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -450,7 +450,7 @@
      * This is the implementation for {@code
      * javax.lang.model.element.Element.getAnnotationMirrors()}.
      */
-    public final List<Attribute.Compound> getAnnotationMirrors() {
+    public final List<? extends AnnotationMirror> getAnnotationMirrors() {
         return getRawAttributes();
     }
 
@@ -462,6 +462,11 @@
         return JavacElements.getAnnotation(this, annoType);
     }
 
+    // This method is part of the javax.lang.model API, do not use this in javac code.
+    public <A extends java.lang.annotation.Annotation> A[] getAnnotations(Class<A> annoType) {
+        return JavacElements.getAnnotations(this, annoType);
+    }
+
     // TODO: getEnclosedElements should return a javac List, fix in FilteredMemberList
     public java.util.List<Symbol> getEnclosedElements() {
         return List.nil();
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java	Thu Jan 10 19:38:57 2013 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java	Mon Jan 14 19:52:36 2013 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -27,6 +27,8 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Inherited;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Map;
 
 import javax.lang.model.SourceVersion;
@@ -96,32 +98,43 @@
         enter = Enter.instance(context);
     }
 
-
     /**
-     * An internal-use utility that creates a reified annotation.
+     * An internal-use utility that creates a runtime view of an
+     * annotation. This is the implementation of
+     * Element.getAnnotation(Class).
      */
     public static <A extends Annotation> A getAnnotation(Symbol annotated,
                                                          Class<A> annoType) {
         if (!annoType.isAnnotation())
             throw new IllegalArgumentException("Not an annotation type: "
                                                + annoType);
-        String name = annoType.getName();
-        for (Attribute.Compound anno : annotated.getAnnotationMirrors())
-            if (name.equals(anno.type.tsym.flatName().toString()))
-                return AnnotationProxyMaker.generateAnnotation(anno, annoType);
-        return null;
+        Attribute.Compound c;
+        if (annotated.kind == Kinds.TYP && annotated instanceof ClassSymbol) {
+            c = getAttributeOnClass((ClassSymbol)annotated, annoType);
+        } else {
+            c = getAttribute(annotated, annoType);
+        }
+        return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
     }
 
-    /**
-     * An internal-use utility that creates a reified annotation.
-     * This overloaded version take annotation inheritance into account.
-     */
-    public static <A extends Annotation> A getAnnotation(ClassSymbol annotated,
-                                                         Class<A> annoType) {
+    // Helper to getAnnotation[s]
+    private static <A extends Annotation> Attribute.Compound getAttribute(Symbol annotated,
+                                                                          Class<A> annoType) {
+        String name = annoType.getName();
+
+        for (Attribute.Compound anno : annotated.getRawAttributes())
+            if (name.equals(anno.type.tsym.flatName().toString()))
+                return anno;
+
+        return null;
+    }
+    // Helper to getAnnotation[s]
+    private static <A extends Annotation> Attribute.Compound getAttributeOnClass(ClassSymbol annotated,
+                                                                Class<A> annoType) {
         boolean inherited = annoType.isAnnotationPresent(Inherited.class);
-        A result = null;
+        Attribute.Compound result = null;
         while (annotated.name != annotated.name.table.names.java_lang_Object) {
-            result = getAnnotation((Symbol)annotated, annoType);
+            result = getAttribute(annotated, annoType);
             if (result != null || !inherited)
                 break;
             Type sup = annotated.getSuperclass();
@@ -132,6 +145,188 @@
         return result;
     }
 
+    /**
+     * An internal-use utility that creates a runtime view of
+     * annotations. This is the implementation of
+     * Element.getAnnotations(Class).
+     */
+    public static <A extends Annotation> A[] getAnnotations(Symbol annotated,
+                                                            Class<A> annoType) {
+        if (!annoType.isAnnotation())
+            throw new IllegalArgumentException("Not an annotation type: "
+                                               + annoType);
+        // If annoType does not declare a container this is equivalent to wrapping
+        // getAnnotation(...) in an array.
+        Class <? extends Annotation> containerType = getContainer(annoType);
+        if (containerType == null) {
+            A res = getAnnotation(annotated, annoType);
+            int size;
+            if (res == null) {
+                size = 0;
+            } else {
+                size = 1;
+            }
+            @SuppressWarnings("unchecked") // annoType is the Class for A
+            A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
+            if (res != null)
+                arr[0] = res;
+            return arr;
+        }
+
+        // So we have a containing type
+        String name = annoType.getName();
+        String annoTypeName = annoType.getSimpleName();
+        String containerTypeName = containerType.getSimpleName();
+        int directIndex = -1, containerIndex = -1;
+        Attribute.Compound direct = null, container = null;
+        Attribute.Compound[] rawAttributes = annotated.getRawAttributes().toArray(new Attribute.Compound[0]);
+
+        // Find directly present annotations
+        for (int i = 0; i < rawAttributes.length; i++) {
+            if (annoTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
+                directIndex = i;
+                direct = rawAttributes[i];
+            } else if(containerTypeName != null &&
+                      containerTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
+                containerIndex = i;
+                container = rawAttributes[i];
+            }
+        }
+        // Deal with inherited annotations
+        if (annotated.kind == Kinds.TYP &&
+                (annotated instanceof ClassSymbol)) {
+            ClassSymbol s = (ClassSymbol)annotated;
+            if (direct == null && container == null) {
+                direct = getAttributeOnClass(s, annoType);
+                container = getAttributeOnClass(s, containerType);
+
+                // both are inherited and found, put container last
+                if (direct != null && container != null) {
+                    directIndex = 0;
+                    containerIndex = 1;
+                } else if (direct != null) {
+                    directIndex = 0;
+                } else {
+                    containerIndex = 0;
+                }
+            } else if (direct == null) {
+                direct = getAttributeOnClass(s, annoType);
+                if (direct != null)
+                    directIndex = containerIndex + 1;
+            } else if (container == null) {
+                container = getAttributeOnClass(s, containerType);
+                if (container != null)
+                    containerIndex = directIndex + 1;
+            }
+        }
+
+        // Pack them in an array
+        Attribute[] contained0 = new Attribute[0];
+        if (container != null)
+            contained0 = unpackAttributes(container);
+        ListBuffer<Attribute.Compound> compounds = ListBuffer.lb();
+        for (Attribute a : contained0)
+            if (a instanceof Attribute.Compound)
+                compounds = compounds.append((Attribute.Compound)a);
+        Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[0]);
+
+        int size = (direct == null ? 0 : 1) + contained.length;
+        @SuppressWarnings("unchecked") // annoType is the Class for A
+        A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
+
+        // if direct && container, which is first?
+        int insert = -1;
+        int length = arr.length;
+        if (directIndex >= 0 && containerIndex >= 0) {
+            if (directIndex < containerIndex) {
+                arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
+                insert = 1;
+            } else {
+                arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
+                insert = 0;
+                length--;
+            }
+        } else if (directIndex >= 0) {
+            arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
+            return arr;
+        } else {
+            // Only container
+            insert = 0;
+        }
+
+        for (int i = 0; i + insert < length; i++)
+            arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
+
+        return arr;
+    }
+
+    // Needed to unpack the runtime view of containing annotations
+    private static final Class<? extends Annotation> CONTAINED_BY_CLASS = initContainedBy();
+    private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod();
+
+    private static Class<? extends Annotation> initContainedBy() {
+        try {
+            @SuppressWarnings("unchecked") // java.lang.annotation.ContainedBy extends Annotation by being an annotation type
+            Class<? extends Annotation> c = (Class)Class.forName("java.lang.annotation.ContainedBy");
+            return c;
+        } catch (ClassNotFoundException e) {
+            return null;
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+    private static Method initValueElementMethod() {
+        if (CONTAINED_BY_CLASS == null)
+            return null;
+
+        Method m = null;
+        try {
+            m = CONTAINED_BY_CLASS.getMethod("value");
+            if (m != null)
+                m.setAccessible(true);
+            return m;
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    // Helper to getAnnotations
+    private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
+        // Since we can not refer to java.lang.annotation.ContainedBy until we are
+        // bootstrapping with java 8 we need to get the ContainedBy annotation using
+        // reflective invocations instead of just using its type and element method.
+        if (CONTAINED_BY_CLASS != null &&
+            VALUE_ELEMENT_METHOD != null) {
+            // Get the ContainedBy instance on the annotations declaration
+            Annotation containedBy = (Annotation)annoType.getAnnotation(CONTAINED_BY_CLASS);
+            if (containedBy != null) {
+                try {
+                    // Get the value element, it should be a class
+                    // indicating the containing annotation type
+                    @SuppressWarnings("unchecked")
+                    Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(containedBy);
+                    if (containerType == null)
+                        return null;
+
+                    return containerType;
+                } catch (ClassCastException e) {
+                    return null;
+                } catch (IllegalAccessException e) {
+                    return null;
+                } catch (InvocationTargetException e ) {
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+    // Helper to getAnnotations
+    private static Attribute[] unpackAttributes(Attribute.Compound container) {
+        // We now have an instance of the container,
+        // unpack it returning an instance of the
+        // contained type or null
+        return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
+    }
 
     public PackageSymbol getPackageElement(CharSequence name) {
         String strName = name.toString();
@@ -238,8 +433,10 @@
         tree.accept(vis);
         if (vis.result == null)
             return null;
+
+        List<Attribute.Compound> annos = sym.getRawAttributes();
         return matchAnnoToTree(cast(Attribute.Compound.class, findme),
-                               sym.getAnnotationMirrors(),
+                               annos,
                                vis.result);
     }
 
@@ -442,7 +639,7 @@
      */
     public List<Attribute.Compound> getAllAnnotationMirrors(Element e) {
         Symbol sym = cast(Symbol.class, e);
-        List<Attribute.Compound> annos = sym.getAnnotationMirrors();
+        List<Attribute.Compound> annos = sym.getRawAttributes();
         while (sym.getKind() == ElementKind.CLASS) {
             Type sup = ((ClassSymbol) sym).getSuperclass();
             if (!sup.hasTag(CLASS) || sup.isErroneous() ||
@@ -451,7 +648,8 @@
             }
             sym = sup.tsym;
             List<Attribute.Compound> oldAnnos = annos;
-            for (Attribute.Compound anno : sym.getAnnotationMirrors()) {
+            List<Attribute.Compound> newAnnos = sym.getRawAttributes();
+            for (Attribute.Compound anno : newAnnos) {
                 if (isInherited(anno.type) &&
                         !containsAnnoOfType(oldAnnos, anno.type)) {
                     annos = annos.prepend(anno);
@@ -465,11 +663,7 @@
      * Tests whether an annotation type is @Inherited.
      */
     private boolean isInherited(Type annotype) {
-        for (Attribute.Compound anno : annotype.tsym.getAnnotationMirrors()) {
-            if (anno.type.tsym == syms.inheritedType.tsym)
-                return true;
-        }
-        return false;
+        return annotype.tsym.attribute(syms.inheritedType.tsym) != null;
     }
 
     /**
--- a/langtools/src/share/classes/javax/lang/model/element/Element.java	Thu Jan 10 19:38:57 2013 -0800
+++ b/langtools/src/share/classes/javax/lang/model/element/Element.java	Mon Jan 14 19:52:36 2013 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -149,6 +149,56 @@
     <A extends Annotation> A getAnnotation(Class<A> annotationType);
 
     /**
+     * Returns an array of all of this element's annotation for the
+     * specified type if such annotations are present, else an empty
+     * array.  The annotation may be either inherited or directly
+     * present on this element. This method will look through a container
+     * annotation (if present) if the supplied annotation type is
+     * repeatable.
+     *
+     * <p> The annotations returned by this method could contain an element
+     * whose value is of type {@code Class}.
+     * This value cannot be returned directly:  information necessary to
+     * locate and load a class (such as the class loader to use) is
+     * not available, and the class might not be loadable at all.
+     * Attempting to read a {@code Class} object by invoking the relevant
+     * method on the returned annotation
+     * will result in a {@link MirroredTypeException},
+     * from which the corresponding {@link TypeMirror} may be extracted.
+     * Similarly, attempting to read a {@code Class[]}-valued element
+     * will result in a {@link MirroredTypesException}.
+     *
+     * <blockquote>
+     * <i>Note:</i> This method is unlike others in this and related
+     * interfaces.  It operates on runtime reflective information &mdash;
+     * representations of annotation types currently loaded into the
+     * VM &mdash; rather than on the representations defined by and used
+     * throughout these interfaces.  Consequently, calling methods on
+     * the returned annotation object can throw many of the exceptions
+     * that can be thrown when calling methods on an annotation object
+     * returned by core reflection.  This method is intended for
+     * callers that are written to operate on a known, fixed set of
+     * annotation types.
+     * </blockquote>
+     *
+     * @param <A>  the annotation type
+     * @param annotationType  the {@code Class} object corresponding to
+     *          the annotation type
+     * @return this element's annotations for the specified annotation
+     *         type if present on this element, else an empty array
+     *
+     * @see #getAnnotationMirrors()
+     * @see #getAnnotation()
+     * @see java.lang.reflect.AnnotatedElement#getAnnotations
+     * @see EnumConstantNotPresentException
+     * @see AnnotationTypeMismatchException
+     * @see IncompleteAnnotationException
+     * @see MirroredTypeException
+     * @see MirroredTypesException
+     */
+    <A extends Annotation> A[] getAnnotations(Class<A> annotationType);
+
+    /**
      * Returns the modifiers of this element, excluding annotations.
      * Implicit modifiers, such as the {@code public} and {@code static}
      * modifiers of interface members, are included.