AnnotatedElements.java revision 9599ec54b164da29db4e3386a9839aca73caf8ee
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.reflect;
18
19import java.lang.annotation.Annotation;
20import java.lang.annotation.IncompleteAnnotationException;
21import java.lang.annotation.Repeatable;
22import java.lang.reflect.*;
23import java.util.ArrayList;
24
25/**
26 * Implementation of {@link AnnotatedElement}'s 1.8 methods.
27 *
28 * <p>This implementation is shared between all the classes implementing {@link AnnotatedElement},
29 * avoiding code duplication.</p>
30 *
31 * @hide
32 */
33public final class AnnotatedElements {
34  /**
35   * Default implementation for {@link AnnotatedElement#getDeclaredAnnotation}.
36   *
37   * @return Directly present annotation of type {@code annotationClass} for {@code element},
38   *         or {@code null} if none was found.
39   */
40  public static <T extends Annotation> T getDeclaredAnnotation(AnnotatedElement element,
41      Class<T> annotationClass) {
42    if (annotationClass == null) {
43      throw new NullPointerException("annotationClass");
44    }
45
46    Annotation[] annotations = element.getDeclaredAnnotations();
47
48    // Safeguard: getDeclaredAnnotations should never return null.
49    if (annotations == null) {
50      return null;
51    }
52
53    // The annotation might be directly present:
54    // Return the first (and only) annotation whose class matches annotationClass.
55    for (int i = 0; i < annotations.length; ++i) {
56      if (annotationClass.isInstance(annotations[i])) {
57        return (T)annotations[i];  // Safe because of above guard.
58      }
59    }
60
61    // The annotation was *not* directly present:
62    // If the array was empty, or we found no matches, return null.
63    return null;
64  }
65
66  /**
67   * Default implementation for {@link AnnotatedElement#getDeclaredAnnotationsByType}.
68   *
69   * @return Directly/indirectly present list of annotations of type {@code annotationClass} for
70   *         {@code element}, or an empty array if none were found.
71   */
72  public static <T extends Annotation> T[] getDeclaredAnnotationsByType(AnnotatedElement element,
73      Class<T> annotationClass) {
74    if (annotationClass == null) {
75      throw new NullPointerException("annotationClass");
76    }
77
78    Annotation[] annotations = element.getDeclaredAnnotations();
79
80    // Store a list of repeatable annotations that have been extracted from their container.
81    ArrayList<T> unfoldedAnnotations = new ArrayList<T>();
82
83    Class<? extends Annotation> repeatableAnnotationClass =
84        getRepeatableAnnotationContainerClassFor(annotationClass);
85
86    for (int i = 0; i < annotations.length; ++i) {
87      if (annotationClass.isInstance(annotations[i])) {
88        // Is it directly present?
89        unfoldedAnnotations.add((T)annotations[i]);  // Safe, guarded by above check.
90      } else if (repeatableAnnotationClass != null &&
91          repeatableAnnotationClass.isInstance(annotations[i])) {
92        // Is it repeatably (indirectly) present?
93        insertAnnotationValues(annotations[i], annotationClass, unfoldedAnnotations);
94      }
95    }
96
97    return unfoldedAnnotations.toArray((T[])Array.newInstance(annotationClass, 0));
98  }
99
100  /**
101   * Extracts annotations from a container annotation and inserts them into a list.
102   *
103   * <p>
104   * Given a complex annotation "annotation", it should have a "T[] value()" method on it.
105   * Call that method and add all of the nested annotations into unfoldedAnnotations list.
106   * </p>
107   */
108  private static <T extends Annotation> void insertAnnotationValues(Annotation annotation,
109      Class<T> annotationClass, ArrayList<T> unfoldedAnnotations) {
110    // annotation is a complex annotation which has elements of instance annotationClass
111    // (whose static type is T).
112    //
113    // @interface SomeName {  <--- = annotation.getClass()
114    //   ...
115    //   T[] value();        <--- T.class == annotationClass
116    // }
117    //
118    // Use reflection to access these values.
119    Class<T[]> annotationArrayClass =
120        (Class<T[]>)((T[])Array.newInstance(annotationClass, 0)).getClass();
121
122    Method valuesMethod;
123    try {
124      valuesMethod = annotation.getClass().getDeclaredMethod("value");
125      // This will always succeed unless the annotation and its repeatable annotation class were
126      // recompiled separately, then this is a binary incompatibility error.
127    } catch (NoSuchMethodException e) {
128      throw new AssertionError("annotation container = " + annotation +
129          "annotation element class = " + annotationClass + "; missing value() method");
130    } catch (SecurityException e) {
131      throw new IncompleteAnnotationException(annotation.getClass(), "value");
132    }
133
134    // Ensure that value() returns a T[]
135    if (!valuesMethod.getReturnType().isArray()) {
136      throw new AssertionError("annotation container = " + annotation +
137          "annotation element class = " + annotationClass + "; value() doesn't return array");
138    }
139
140    // Ensure that the T[] value() is actually the correct type (T==annotationClass).
141    if (!annotationClass.equals(valuesMethod.getReturnType().getComponentType())) {
142      throw new AssertionError("annotation container = " + annotation +
143          "annotation element class = " + annotationClass + "; value() returns incorrect type");
144    }
145
146    // Append those values into the existing list.
147    T[] nestedAnnotations;
148    try {
149      nestedAnnotations = (T[])valuesMethod.invoke(annotation);  // Safe because of #getMethod.
150    } catch (IllegalAccessException|InvocationTargetException e) {
151      throw new AssertionError(e);
152    }
153
154    for (int i = 0; i < nestedAnnotations.length; ++i) {
155      unfoldedAnnotations.add(nestedAnnotations[i]);
156    }
157  }
158
159  /**
160   * Find the {@code \@Repeatable} container annotation class for an annotation class, or
161   * {@code null}.
162   *
163   * <p>
164   * Given:
165   *
166   * <code>
167   *  @Repeatable(X.class)
168   *  @interface SomeName {     <--- = annotationClass
169   *  }...
170   * </code>
171   *
172   * <p>
173   * Returns {@code X.class}
174   *
175   * Otherwise if there was no {@code \@Repeatable} annotation, return {@code null}.
176   * </p>
177   */
178  private static <T extends Annotation> Class<? extends Annotation>
179      getRepeatableAnnotationContainerClassFor(Class<T> annotationClass) {
180
181    Repeatable repeatableAnnotation = annotationClass.getDeclaredAnnotation(Repeatable.class);
182    return (repeatableAnnotation == null) ? null : repeatableAnnotation.value();
183  }
184
185  /**
186   * Default implementation of {@link AnnotatedElement#getAnnotationsByType}.
187   *
188   * <p>
189   * This method does not handle inherited annotations and is
190   * intended for use for {@code Method}, {@code Field}, {@code Package}.
191   * The {@link Class#getAnnotationsByType} is implemented explicitly.
192   * </p>
193   *
194   * @return Associated annotations of type {@code annotationClass} for {@code element}.
195   */
196  public static <T extends Annotation> T[] getAnnotationsByType(AnnotatedElement element,
197      Class<T> annotationClass) {
198    if (annotationClass == null) {
199      throw new NullPointerException("annotationClass");
200    }
201
202    // Find any associated annotations [directly or repeatably (indirectly) present on this class].
203    T[] annotations = element.getDeclaredAnnotationsByType(annotationClass);
204    if (annotations == null) {
205      throw new AssertionError("annotations must not be null");  // Internal error.
206    }
207
208    // If nothing was found, we would look for associated annotations recursively up to the root
209    // class. However this can only happen if AnnotatedElement is a Class, which is handled
210    // in the Class override of this method.
211    return annotations;
212  }
213
214  private AnnotatedElements() {
215    throw new AssertionError("Instances of AnnotatedElements not allowed");
216  }
217}
218
219