src/share/classes/java/lang/reflect/AnnotatedElement.java

Print this page

        

@@ -25,10 +25,13 @@
 
 package java.lang.reflect;
 
 import java.lang.annotation.Annotation;
 import java.lang.annotation.AnnotationFormatError;
+import java.lang.annotation.Repeatable;
+import java.util.Objects;
+import sun.reflect.annotation.AnnotationSupport;
 
 /**
  * Represents an annotated element of the program currently running in this
  * VM.  This interface allows annotations to be read reflectively.  All
  * annotations returned by methods in this interface are immutable and

@@ -220,36 +223,77 @@
      * annotation.
      *
      * The caller of this method is free to modify the returned array; it will
      * have no effect on the arrays returned to other callers.
      *
+     * @implSpec The default implementation first calls {@link
+     * #getDeclaredAnnotationsByType(Class)} on the argument type. If
+     * the returned array has size greater than zero, the array is
+     * returned. If the returned array has size zero and this {@code
+     * AnnotatedElement} is a class and the argument type is an
+     * inheritable annotation, and the superclass of this {@code
+     * AnnoatedElement} is non-null, then the result of {@code
+     * getAnnotationsByType(annotationClass)} on the superclass is
+     * returned. Otherwise, a zero-length array is returned.
+     *
      * @param <T> the type of the annotation to query for and return if present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return all this element's annotations for the specified annotation type if
      *     associated with this element, else an array of length zero
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass);
+    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
+        T[] result = getDeclaredAnnotationsByType(annotationClass);
+        if (result.length > 0)
+            return result;
+        else {
+            Class<?> thisClass = this.getClass();
+            if (thisClass.equals(Class.class) &&
+                annotationClass.getAnnotation(java.lang.annotation.Inherited.class) != null) {
+                Class<?> superClass = thisClass.getSuperclass();
+                if (superClass != null)
+                    return superClass.getAnnotationsByType(annotationClass);
+            }
+            assert result.length == 0;
+            return result;
+        }
+    }
 
     /**
      * Returns this element's annotation for the specified type if
      * such an annotation is <em>directly present</em>, else null.
      *
      * This method ignores inherited annotations. (Returns null if no
      * annotations are directly present on this element.)
      *
+     * @implSpec The default implementation first performs a null check
+     * and then loops over the results of {@link
+     * getDeclaredAnnotations} returning the first annotation whose
+     * annotation type matches the argument type.
+     *
      * @param <T> the type of the annotation to query for and return if directly present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return this element's annotation for the specified annotation type if
      *     directly present on this element, else null
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass);
+    default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
+        Objects.requireNonNull(annotationClass);
+        Annotation[] annotations =  getDeclaredAnnotations(); // All directly-present annotations
+        for (Annotation annotation : annotations) {
+            if (annotationClass.equals(annotation.annotationType())) {
+                // More robust to do a cast at runtime instead of
+                // compile-time only.
+                return annotationClass.cast(annotation);
+            }
+        }
+        return null; 
+    }
 
     /**
      * Returns this element's annotation(s) for the specified type if
      * such annotations are either <em>directly present</em> or
      * <em>indirectly present</em>. This method ignores inherited

@@ -266,20 +310,108 @@
      * through" a container annotation if one is present.
      *
      * The caller of this method is free to modify the returned array; it will
      * have no effect on the arrays returned to other callers.
      *
+     * @implSpec The default implementation may call {@link
+     * #getDeclaredAnnotation(Class)} one or more times to find a
+     * directly present annotation and, if the annotation type is
+     * repeatable, to find a container annotation. If the annotation
+     * type is both directly and indirectly present, {@link
+     * getDeclaredAnnotations()} will get called to determine the
+     * order of the elements in the returned array. Alternatively,
+     * {@link getDeclaredAnnotations()} may be called a single time
+     * and the returned array examined for both directly and
+     * indirectly present annotations. The results of calling {@link
+     * getDeclaredAnnotations()} are assumed to be consistent with the
+     * results of calling {@code #getDeclaredAnnotation}
+     *
      * @param <T> the type of the annotation to query for and return
      * if directly or indirectly present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return all this element's annotations for the specified annotation type if
      *     directly or indirectly present on this element, else an array of length zero
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);
+    default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
+        Objects.requireNonNull(annotationClass);
+        int resultSize = 0;
+        T directlyPresent = getDeclaredAnnotation(annotationClass);
+        @SuppressWarnings("unchecked")
+        T[] indirectlyPresent =  (T[]) Array.newInstance(annotationClass, 0);
+
+        if (directlyPresent != null) {
+            resultSize++;
+        }
+
+        // If the annotation type is repeatable, look through a
+        // container if one is present
+        Repeatable repeatable = annotationClass.getAnnotation(Repeatable.class);
+        Class<? extends Annotation> containerType = null;
+        if (repeatable != null) { // T is a repeatable annotation type
+            containerType = repeatable.value();
+            Annotation container =  this.getDeclaredAnnotation(containerType);
+            if (container != null) {
+                indirectlyPresent = AnnotationSupport.getValueArray(container);
+                resultSize += indirectlyPresent.length;
+            }
+        }
+    
+        if (resultSize == indirectlyPresent.length) {
+            assert resultSize == 0 || directlyPresent == null;
+            /*
+             * If resultSize is 0, indirectlyPresent is either
+             * assigned to the result of the initial Array.newInstance
+             * call or indirectlyPresent is assigned to a zero-lenght
+             * value array from an empty container annotation. In
+             * either case, a zero-length array is immutable and does
+             * not need to reallocated before being returned.
+             *
+             * If resultSize is nonzero, then indirectlyPresent points
+             * to the result calling a method on an annotation and
+             * annotations are required to implement a no sharing
+             * policy. Therefore, it is not required to copy the
+             * elements of indirectlyPresent into a new array of the
+             * same size.
+             */
+            return indirectlyPresent;
+        } else {
+            assert resultSize > 0 && (directlyPresent != null || indirectlyPresent.length > 0);
+            @SuppressWarnings("unchecked")
+            T[] returnValue = (T[]) Array.newInstance(annotationClass, resultSize);
+
+            if (indirectlyPresent.length == 0) {
+                assert resultSize == 1;
+                returnValue[0] = directlyPresent;
+            } else {
+                assert directlyPresent != null  && indirectlyPresent.length > 0;
+                // Determine whether the directly present annotation
+                // comes before or after the indirectly present ones.
+
+                int indirectOffset = 0;
+
+                for (Annotation a : getDeclaredAnnotations()) {
+                    if (a.annotationType().equals(annotationClass)) {
+                        indirectOffset = 1;
+                        break;
+                    } else if (a.annotationType().equals(containerType)) {
+                        break;
+                    }
+                }
+
+                for (int i = 0; i < resultSize; i++) {
+                    returnValue[i + indirectOffset] = indirectlyPresent[i];
+                }
+
+                returnValue[(indirectOffset == 1) ? 0 : resultSize - 1] = directlyPresent;
+            }
+            return returnValue;
+        }
+    }
+
 
     /**
      * Returns annotations that are <em>directly present</em> on this element.
      * This method ignores inherited annotations.
      *