1
1
/*
2
- * Copyright 2002-2013 the original author or authors.
2
+ * Copyright 2002-2014 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .test .context ;
18
18
19
19
import java .lang .annotation .Annotation ;
20
+ import java .util .HashSet ;
21
+ import java .util .Set ;
20
22
21
23
import org .springframework .core .annotation .AnnotatedElementUtils ;
22
24
import org .springframework .core .annotation .AnnotationAttributes ;
29
31
30
32
/**
31
33
* {@code MetaAnnotationUtils} is a collection of utility methods that complements
32
- * support already available in {@link AnnotationUtils}.
34
+ * the standard support already available in {@link AnnotationUtils}.
33
35
*
34
- * <p>Whereas {@code AnnotationUtils} only provides utilities for <em>getting</em>
35
- * or <em>finding</em> an annotation, {@code MetaAnnotationUtils} provides
36
- * additional support for determining the <em>root class</em> on which an
36
+ * <p>Whereas {@code AnnotationUtils} provides utilities for <em>getting</em> or
37
+ * <em>finding</em> an annotation, {@code MetaAnnotationUtils} goes a step further
38
+ * by providing support for determining the <em>root class</em> on which an
37
39
* annotation is declared, either directly or via a <em>composed annotation</em>.
38
40
* This additional information is encapsulated in an {@link AnnotationDescriptor}.
39
41
*
40
42
* <p>The additional information provided by an {@code AnnotationDescriptor} is
41
- * required in the <em>Spring TestContext Framework</em> in order to be able to
42
- * support class hierarchy traversals for <em>inherited</em> annotations such as
43
+ * required by the <em>Spring TestContext Framework</em> in order to be able to
44
+ * support class hierarchy traversals for annotations such as
43
45
* {@link ContextConfiguration @ContextConfiguration},
44
46
* {@link TestExecutionListeners @TestExecutionListeners}, and
45
- * {@link ActiveProfiles @ActiveProfiles}.
47
+ * {@link ActiveProfiles @ActiveProfiles} which offer support for merging and
48
+ * overriding various <em>inherited</em> annotation attributes (e.g., {@link
49
+ * ContextConfiguration#inheritLocations}).
46
50
*
47
51
* @author Sam Brannen
48
52
* @since 4.0
@@ -57,7 +61,7 @@ private MetaAnnotationUtils() {
57
61
58
62
/**
59
63
* Find the {@link AnnotationDescriptor} for the supplied {@code annotationType}
60
- * from the supplied {@link Class}, traversing its annotations and superclasses
64
+ * on the supplied {@link Class}, traversing its annotations and superclasses
61
65
* if no annotation can be found on the given class itself.
62
66
*
63
67
* <p>This method explicitly handles class-level annotations which are not
@@ -66,30 +70,45 @@ private MetaAnnotationUtils() {
66
70
*
67
71
* <p>The algorithm operates as follows:
68
72
* <ol>
69
- * <li>Search for a local declaration of the annotation on the given class
70
- * and return a corresponding {@code AnnotationDescriptor} if found.
71
- * <li>Search through all annotations that the given class declares,
72
- * returning an {@code AnnotationDescriptor} for the first matching
73
- * candidate, if any.
74
- * <li>Proceed with introspection of the superclass hierarchy of the given
75
- * class by returning to step #1 with the superclass as the class to look
76
- * for annotations on.
73
+ * <li>Search for the annotation on the given class and return a corresponding
74
+ * {@code AnnotationDescriptor} if found.
75
+ * <li>Recursively search through all annotations that the given class declares.
76
+ * <li>Recursively search through the superclass hierarchy of the given class.
77
77
* </ol>
78
78
*
79
+ * <p>In this context, the term <em>recursively</em> means that the search
80
+ * process continues by returning to step #1 with the current annotation or
81
+ * superclass as the class to look for annotations on.
82
+ *
79
83
* <p>If the supplied {@code clazz} is an interface, only the interface
80
84
* itself will be checked; the inheritance hierarchy for interfaces will not
81
85
* be traversed.
82
86
*
83
87
* @param clazz the class to look for annotations on
84
- * @param annotationType the annotation class to look for, both locally and
85
- * as a meta-annotation
88
+ * @param annotationType the type of annotation to look for
86
89
* @return the corresponding annotation descriptor if the annotation was found;
87
90
* otherwise {@code null}
88
91
* @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class)
89
92
* @see #findAnnotationDescriptorForTypes(Class, Class...)
90
93
*/
91
94
public static <T extends Annotation > AnnotationDescriptor <T > findAnnotationDescriptor (Class <?> clazz ,
92
95
Class <T > annotationType ) {
96
+ return findAnnotationDescriptor (clazz , new HashSet <Annotation >(), annotationType );
97
+ }
98
+
99
+ /**
100
+ * Perform the search algorithm for {@link #findAnnotationDescriptor(Class, Class)},
101
+ * avoiding endless recursion by tracking which annotations have already been
102
+ * <em>visited</em>.
103
+ *
104
+ * @param clazz the class to look for annotations on
105
+ * @param visited the set of annotations that have already been visited
106
+ * @param annotationType the type of annotation to look for
107
+ * @return the corresponding annotation descriptor if the annotation was found;
108
+ * otherwise {@code null}
109
+ */
110
+ private static <T extends Annotation > AnnotationDescriptor <T > findAnnotationDescriptor (Class <?> clazz ,
111
+ Set <Annotation > visited , Class <T > annotationType ) {
93
112
94
113
Assert .notNull (annotationType , "Annotation type must not be null" );
95
114
@@ -103,29 +122,30 @@ public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescr
103
122
}
104
123
105
124
// Declared on a composed annotation (i.e., as a meta-annotation)?
106
- if (!Annotation .class .isAssignableFrom (clazz )) {
107
- for (Annotation composedAnnotation : clazz .getAnnotations ()) {
108
- T annotation = composedAnnotation .annotationType ().getAnnotation (annotationType );
109
- if (annotation != null ) {
110
- return new AnnotationDescriptor <T >(clazz , composedAnnotation , annotation );
125
+ for (Annotation composedAnnotation : clazz .getDeclaredAnnotations ()) {
126
+ if (visited .add (composedAnnotation )) {
127
+ AnnotationDescriptor <T > descriptor = findAnnotationDescriptor (composedAnnotation .annotationType (),
128
+ visited , annotationType );
129
+ if (descriptor != null ) {
130
+ return new AnnotationDescriptor <T >(clazz , descriptor .getDeclaringClass (), composedAnnotation ,
131
+ descriptor .getAnnotation ());
111
132
}
112
133
}
113
134
}
114
135
115
136
// Declared on a superclass?
116
- return findAnnotationDescriptor (clazz .getSuperclass (), annotationType );
137
+ return findAnnotationDescriptor (clazz .getSuperclass (), visited , annotationType );
117
138
}
118
139
119
140
/**
120
141
* Find the {@link UntypedAnnotationDescriptor} for the first {@link Class}
121
142
* in the inheritance hierarchy of the specified {@code clazz} (including
122
143
* the specified {@code clazz} itself) which declares at least one of the
123
- * specified {@code annotationTypes}, or {@code null} if none of the
124
- * specified annotation types could be found.
144
+ * specified {@code annotationTypes}.
125
145
*
126
146
* <p>This method traverses the annotations and superclasses of the specified
127
147
* {@code clazz} if no annotation can be found on the given class itself.
128
- *
148
+ *
129
149
* <p>This method explicitly handles class-level annotations which are not
130
150
* declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as
131
151
* well as meta-annotations</em>.
@@ -135,21 +155,20 @@ public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescr
135
155
* <li>Search for a local declaration of one of the annotation types on
136
156
* the given class and return a corresponding {@code UntypedAnnotationDescriptor}
137
157
* if found.
138
- * <li>Search through all annotations that the given class declares,
139
- * returning an {@code UntypedAnnotationDescriptor} for the first matching
140
- * candidate, if any.
141
- * <li>Proceed with introspection of the superclass hierarchy of the given
142
- * class by returning to step #1 with the superclass as the class to look
143
- * for annotations on.
158
+ * <li>Recursively search through all annotations that the given class declares.
159
+ * <li>Recursively search through the superclass hierarchy of the given class.
144
160
* </ol>
145
161
*
162
+ * <p>In this context, the term <em>recursively</em> means that the search
163
+ * process continues by returning to step #1 with the current annotation or
164
+ * superclass as the class to look for annotations on.
165
+ *
146
166
* <p>If the supplied {@code clazz} is an interface, only the interface
147
167
* itself will be checked; the inheritance hierarchy for interfaces will not
148
168
* be traversed.
149
169
*
150
170
* @param clazz the class to look for annotations on
151
- * @param annotationTypes the types of annotations to look for, both locally
152
- * and as meta-annotations
171
+ * @param annotationTypes the types of annotations to look for
153
172
* @return the corresponding annotation descriptor if one of the annotations
154
173
* was found; otherwise {@code null}
155
174
* @see AnnotationUtils#findAnnotationDeclaringClassForTypes(java.util.List, Class)
@@ -158,6 +177,23 @@ public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescr
158
177
@ SuppressWarnings ("unchecked" )
159
178
public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes (Class <?> clazz ,
160
179
Class <? extends Annotation >... annotationTypes ) {
180
+ return findAnnotationDescriptorForTypes (clazz , new HashSet <Annotation >(), annotationTypes );
181
+ }
182
+
183
+ /**
184
+ * Perform the search algorithm for {@link #findAnnotationDescriptorForTypes(Class, Class...)},
185
+ * avoiding endless recursion by tracking which annotations have already been
186
+ * <em>visited</em>.
187
+ *
188
+ * @param clazz the class to look for annotations on
189
+ * @param visited the set of annotations that have already been visited
190
+ * @param annotationTypes the types of annotations to look for
191
+ * @return the corresponding annotation descriptor if one of the annotations
192
+ * was found; otherwise {@code null}
193
+ */
194
+ @ SuppressWarnings ("unchecked" )
195
+ private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes (Class <?> clazz ,
196
+ Set <Annotation > visited , Class <? extends Annotation >... annotationTypes ) {
161
197
162
198
assertNonEmptyAnnotationTypeArray (annotationTypes , "The list of annotation types must not be empty" );
163
199
@@ -173,19 +209,19 @@ public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class
173
209
}
174
210
175
211
// Declared on a composed annotation (i.e., as a meta-annotation)?
176
- if (! Annotation . class . isAssignableFrom ( clazz )) {
177
- for ( Annotation composedAnnotation : clazz . getAnnotations ( )) {
178
- for ( Class <? extends Annotation > annotationType : annotationTypes ) {
179
- Annotation annotation = composedAnnotation .annotationType (). getAnnotation ( annotationType );
180
- if (annotation != null ) {
181
- return new UntypedAnnotationDescriptor (clazz , composedAnnotation , annotation );
182
- }
212
+ for ( Annotation composedAnnotation : clazz . getDeclaredAnnotations ( )) {
213
+ if ( visited . add ( composedAnnotation )) {
214
+ UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes (
215
+ composedAnnotation .annotationType (), visited , annotationTypes );
216
+ if (descriptor != null ) {
217
+ return new UntypedAnnotationDescriptor (clazz , descriptor . getDeclaringClass (), composedAnnotation ,
218
+ descriptor . getAnnotation ());
183
219
}
184
220
}
185
221
}
186
222
187
223
// Declared on a superclass?
188
- return findAnnotationDescriptorForTypes (clazz .getSuperclass (), annotationTypes );
224
+ return findAnnotationDescriptorForTypes (clazz .getSuperclass (), visited , annotationTypes );
189
225
}
190
226
191
227
@@ -254,16 +290,16 @@ public static class AnnotationDescriptor<T extends Annotation> {
254
290
255
291
256
292
public AnnotationDescriptor (Class <?> rootDeclaringClass , T annotation ) {
257
- this (rootDeclaringClass , null , annotation );
293
+ this (rootDeclaringClass , rootDeclaringClass , null , annotation );
258
294
}
259
295
260
- public AnnotationDescriptor (Class <?> rootDeclaringClass , Annotation composedAnnotation , T annotation ) {
296
+ public AnnotationDescriptor (Class <?> rootDeclaringClass , Class <?> declaringClass ,
297
+ Annotation composedAnnotation , T annotation ) {
261
298
Assert .notNull (rootDeclaringClass , "rootDeclaringClass must not be null" );
262
299
Assert .notNull (annotation , "annotation must not be null" );
263
300
264
301
this .rootDeclaringClass = rootDeclaringClass ;
265
- this .declaringClass = (composedAnnotation != null ) ? composedAnnotation .annotationType ()
266
- : rootDeclaringClass ;
302
+ this .declaringClass = declaringClass ;
267
303
this .composedAnnotation = composedAnnotation ;
268
304
this .annotation = annotation ;
269
305
this .annotationAttributes = AnnotatedElementUtils .getAnnotationAttributes (rootDeclaringClass ,
@@ -322,12 +358,13 @@ public String toString() {
322
358
*/
323
359
public static class UntypedAnnotationDescriptor extends AnnotationDescriptor <Annotation > {
324
360
325
- public UntypedAnnotationDescriptor (Class <?> declaringClass , Annotation annotation ) {
326
- super ( declaringClass , annotation );
361
+ public UntypedAnnotationDescriptor (Class <?> rootDeclaringClass , Annotation annotation ) {
362
+ this ( rootDeclaringClass , rootDeclaringClass , null , annotation );
327
363
}
328
364
329
- public UntypedAnnotationDescriptor (Class <?> declaringClass , Annotation composedAnnotation , Annotation annotation ) {
330
- super (declaringClass , composedAnnotation , annotation );
365
+ public UntypedAnnotationDescriptor (Class <?> rootDeclaringClass , Class <?> declaringClass ,
366
+ Annotation composedAnnotation , Annotation annotation ) {
367
+ super (rootDeclaringClass , declaringClass , composedAnnotation , annotation );
331
368
}
332
369
}
333
370
0 commit comments