src/share/classes/java/lang/reflect/AnnotatedElement.java
Print this page
@@ -25,10 +25,14 @@
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;
+import sun.reflect.annotation.AnnotationType;
/**
* 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 +224,86 @@
* 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)} passing {@code
+ * annotationClass} as the argument. 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 type,
+ * and the superclass of this {@code AnnoatedElement} is non-null,
+ * then the returned result is the result of calling {@code
+ * getAnnotationsByType} on the superclass with {@code
+ * annotationClass} as the argument. 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) {
+ /*
+ * Definition of associated: directly or indirectly present OR
+ * neither directly nor indirectly present AND the element is
+ * a Class, the annotation type is inheritable, and the
+ * annotation type is associated with the superclass of the
+ * element.
+ */
+ T[] result = getDeclaredAnnotationsByType(annotationClass);
+
+ if (result.length == 0 && // Neither directly nor indirectly present
+ this instanceof Class && // the element is a class
+ AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
+ Class<?> superClass = ((Class<?>) this).getSuperclass();
+ if (superClass != null) {
+ // Determine if the annotation is associated with the
+ // superclass
+ result = superClass.getAnnotationsByType(annotationClass);
+ }
+ }
+
+ 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);
+ // Loop over all directly-present annotations looking for a matching one
+ for (Annotation annotation : getDeclaredAnnotations()) {
+ if (annotationClass.equals(annotation.annotationType())) {
+ // More robust to do a dynamic 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 +320,126 @@
* 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 annotations of
+ * the annotation type {@code annotationClass} are found to be both
+ * directly and indirectly present, then {@link
+ * #getDeclaredAnnotations()} will get called to determine the
+ * order of the elements in the returned array.
+ *
+ * <p>Alternatively, the default implementation may call {@link
+ * #getDeclaredAnnotations()} 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 {@link #getDeclaredAnnotation(Class)}.
+ *
* @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);
+
+ if (directlyPresent != null) {
+ resultSize++;
+ }
+
+ Repeatable repeatable = annotationClass.getAnnotation(Repeatable.class);
+ if (repeatable == null) {
+ @SuppressWarnings("unchecked")
+ T[] returnValue = (T[]) Array.newInstance(annotationClass, resultSize);
+ if (directlyPresent != null)
+ returnValue[0] = directlyPresent;
+ return returnValue;
+ } else {
+ // directlyPresent may or may not be null
+
+ // Look through a container to see if an annotation is
+ // indirectly present
+ Class<? extends Annotation> containerType = repeatable.value();
+ Annotation container = getDeclaredAnnotation(containerType);
+ T[] indirectlyPresent = null;
+
+ if (container != null) {
+ indirectlyPresent = AnnotationSupport.getValueArray(container);
+ resultSize += indirectlyPresent.length;
+ }
+
+ // Final result size is known
+
+ // todo: review comment for accuracy...
+ /*
+ * If resultSize is 0, indirectlyPresent is either
+ * assigned to the result of the initial Array.newInstance
+ * call or indirectlyPresent is assigned to a zero-length
+ * 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.
+ */
+ if (indirectlyPresent == null || indirectlyPresent.length == 0) {
+ @SuppressWarnings("unchecked")
+ T[] returnValue = (T[]) Array.newInstance(annotationClass, resultSize);
+ if (resultSize == 1)
+ returnValue[0] = directlyPresent;
+ return returnValue;
+ } else {
+ // assert indirectlyPresent != null && indirectlyPresent.length > 0;
+
+ if (resultSize == indirectlyPresent.length){
+ // assert directlyPresent == null;
+ return indirectlyPresent;
+ } else {
+ // assert directlyPresent != null && indirectlyPresent.length > 0;
+ @SuppressWarnings("unchecked")
+ T[] returnValue = (T[]) Array.newInstance(annotationClass, resultSize);
+
+ // Determine whether the directly present annotation
+ // comes before or after the indirectly present ones.
+
+ int indirectOffset = 0;
+
+ for (Annotation a : getDeclaredAnnotations()) {
+ Class<? extends Annotation> annotationType = a.annotationType();
+ if (annotationType.equals(annotationClass)) {
+ indirectOffset = 1;
+ break;
+ } else if (annotationType.equals(containerType)) {
+ break;
+ }
+ }
+
+ for (int i = 0; i < indirectlyPresent.length; 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.
*