• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package libcore.reflect;
18 
19 import java.lang.annotation.Annotation;
20 import java.lang.annotation.IncompleteAnnotationException;
21 import java.lang.annotation.Repeatable;
22 import java.lang.reflect.*;
23 import 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  */
33 public 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    */
getDeclaredAnnotation(AnnotatedElement element, Class<T> annotationClass)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    */
getDeclaredAnnotationsByType(AnnotatedElement element, Class<T> annotationClass)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    */
insertAnnotationValues(Annotation annotation, Class<T> annotationClass, ArrayList<T> unfoldedAnnotations)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>
getRepeatableAnnotationContainerClassFor(Class<T> annotationClass)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    */
getAnnotationsByType(AnnotatedElement element, Class<T> annotationClass)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 
AnnotatedElements()214   private AnnotatedElements() {
215     throw new AssertionError("Instances of AnnotatedElements not allowed");
216   }
217 }
218 
219