26
26
import org .springframework .util .LinkedMultiValueMap ;
27
27
import org .springframework .util .MultiValueMap ;
28
28
29
+ import static org .springframework .core .annotation .AnnotationUtils .*;
30
+
29
31
/**
30
32
* Utility class used to collect all annotation values including those declared on
31
33
* meta-annotations.
@@ -39,14 +41,16 @@ public class AnnotatedElementUtils {
39
41
40
42
public static Set <String > getMetaAnnotationTypes (AnnotatedElement element , String annotationType ) {
41
43
final Set <String > types = new LinkedHashSet <String >();
42
- process (element , annotationType , new Processor <Object >() {
44
+ process (element , annotationType , true , new Processor <Object >() {
45
+
43
46
@ Override
44
- public Object process (Annotation annotation , int depth ) {
45
- if (depth > 0 ) {
47
+ public Object process (Annotation annotation , int metaDepth ) {
48
+ if (metaDepth > 0 ) {
46
49
types .add (annotation .annotationType ().getName ());
47
50
}
48
51
return null ;
49
52
}
53
+
50
54
@ Override
51
55
public void postProcess (Annotation annotation , Object result ) {
52
56
}
@@ -55,26 +59,30 @@ public void postProcess(Annotation annotation, Object result) {
55
59
}
56
60
57
61
public static boolean hasMetaAnnotationTypes (AnnotatedElement element , String annotationType ) {
58
- return Boolean .TRUE .equals (process (element , annotationType , new Processor <Boolean >() {
62
+ return Boolean .TRUE .equals (process (element , annotationType , true , new Processor <Boolean >() {
63
+
59
64
@ Override
60
- public Boolean process (Annotation annotation , int depth ) {
61
- if (depth > 0 ) {
62
- return true ;
65
+ public Boolean process (Annotation annotation , int metaDepth ) {
66
+ if (metaDepth > 0 ) {
67
+ return Boolean . TRUE ;
63
68
}
64
69
return null ;
65
70
}
71
+
66
72
@ Override
67
73
public void postProcess (Annotation annotation , Boolean result ) {
68
74
}
69
75
}));
70
76
}
71
77
72
78
public static boolean isAnnotated (AnnotatedElement element , String annotationType ) {
73
- return Boolean .TRUE .equals (process (element , annotationType , new Processor <Boolean >() {
79
+ return Boolean .TRUE .equals (process (element , annotationType , true , new Processor <Boolean >() {
80
+
74
81
@ Override
75
- public Boolean process (Annotation annotation , int depth ) {
76
- return true ;
82
+ public Boolean process (Annotation annotation , int metaDepth ) {
83
+ return Boolean . TRUE ;
77
84
}
85
+
78
86
@ Override
79
87
public void postProcess (Annotation annotation , Boolean result ) {
80
88
}
@@ -85,20 +93,21 @@ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement elem
85
93
return getAnnotationAttributes (element , annotationType , false , false );
86
94
}
87
95
88
- public static AnnotationAttributes getAnnotationAttributes (
89
- AnnotatedElement element , String annotationType ,
96
+ public static AnnotationAttributes getAnnotationAttributes (AnnotatedElement element , String annotationType ,
90
97
final boolean classValuesAsString , final boolean nestedAnnotationsAsMap ) {
91
98
92
- return process (element , annotationType , new Processor <AnnotationAttributes >() {
99
+ return process (element , annotationType , true , new Processor <AnnotationAttributes >() {
100
+
93
101
@ Override
94
- public AnnotationAttributes process (Annotation annotation , int depth ) {
102
+ public AnnotationAttributes process (Annotation annotation , int metaDepth ) {
95
103
return AnnotationUtils .getAnnotationAttributes (annotation , classValuesAsString , nestedAnnotationsAsMap );
96
104
}
105
+
97
106
@ Override
98
107
public void postProcess (Annotation annotation , AnnotationAttributes result ) {
99
108
for (String key : result .keySet ()) {
100
- if (!"value" .equals (key )) {
101
- Object value = AnnotationUtils . getValue (annotation , key );
109
+ if (!VALUE .equals (key )) {
110
+ Object value = getValue (annotation , key );
102
111
if (value != null ) {
103
112
result .put (key , value );
104
113
}
@@ -108,28 +117,33 @@ public void postProcess(Annotation annotation, AnnotationAttributes result) {
108
117
});
109
118
}
110
119
111
- public static MultiValueMap <String , Object > getAllAnnotationAttributes (
112
- AnnotatedElement element , final String annotationType ,
113
- final boolean classValuesAsString , final boolean nestedAnnotationsAsMap ) {
120
+ public static MultiValueMap <String , Object > getAllAnnotationAttributes (AnnotatedElement element ,
121
+ final String annotationType ) {
122
+ return getAllAnnotationAttributes (element , annotationType , false , false );
123
+ }
124
+
125
+ public static MultiValueMap <String , Object > getAllAnnotationAttributes (AnnotatedElement element ,
126
+ final String annotationType , final boolean classValuesAsString , final boolean nestedAnnotationsAsMap ) {
114
127
115
128
final MultiValueMap <String , Object > attributes = new LinkedMultiValueMap <String , Object >();
116
- process (element , annotationType , new Processor <Void >() {
129
+ process (element , annotationType , false , new Processor <Void >() {
130
+
117
131
@ Override
118
- public Void process (Annotation annotation , int depth ) {
132
+ public Void process (Annotation annotation , int metaDepth ) {
119
133
if (annotation .annotationType ().getName ().equals (annotationType )) {
120
- for (Map .Entry <String , Object > entry :
121
- AnnotationUtils .getAnnotationAttributes (
122
- annotation , classValuesAsString , nestedAnnotationsAsMap ).entrySet ()) {
134
+ for (Map .Entry <String , Object > entry : AnnotationUtils .getAnnotationAttributes (annotation ,
135
+ classValuesAsString , nestedAnnotationsAsMap ).entrySet ()) {
123
136
attributes .add (entry .getKey (), entry .getValue ());
124
137
}
125
138
}
126
139
return null ;
127
140
}
141
+
128
142
@ Override
129
143
public void postProcess (Annotation annotation , Void result ) {
130
144
for (String key : attributes .keySet ()) {
131
- if (!"value" .equals (key )) {
132
- Object value = AnnotationUtils . getValue (annotation , key );
145
+ if (!VALUE .equals (key )) {
146
+ Object value = getValue (annotation , key );
133
147
if (value != null ) {
134
148
attributes .add (key , value );
135
149
}
@@ -141,45 +155,91 @@ public void postProcess(Annotation annotation, Void result) {
141
155
}
142
156
143
157
/**
144
- * Process all annotations of the specified annotation type and recursively all
145
- * meta-annotations on the specified type.
158
+ * Process all annotations of the specified {@code annotationType} and
159
+ * recursively all meta-annotations on the specified {@code element}.
160
+ *
161
+ * <p>If the {@code traverseClassHierarchy} flag is {@code true} and the sought
162
+ * annotation is neither <em>directly present</em> on the given element nor
163
+ * present on the given element as a meta-annotation, then the algorithm will
164
+ * recursively search through the class hierarchy of the given element.
165
+ *
146
166
* @param element the annotated element
147
- * @param annotationType the annotation type to find. Only items of the specified
148
- * type or meta-annotations of the specified type will be processed.
149
- * @param processor the processor
167
+ * @param annotationType the annotation type to find
168
+ * @param traverseClassHierarchy whether or not to traverse up the class
169
+ * hierarchy recursively
170
+ * @param processor the processor to delegate to
150
171
* @return the result of the processor
151
172
*/
152
- private static <T > T process (AnnotatedElement element , String annotationType , Processor <T > processor ) {
153
- return doProcess (element , annotationType , processor , new HashSet <AnnotatedElement >(), 0 );
173
+ private static <T > T process (AnnotatedElement element , String annotationType , boolean traverseClassHierarchy ,
174
+ Processor <T > processor ) {
175
+ return doProcess (element , annotationType , traverseClassHierarchy , processor , new HashSet <AnnotatedElement >(), 0 );
154
176
}
155
177
156
- private static <T > T doProcess (AnnotatedElement element , String annotationType ,
157
- Processor <T > processor , Set <AnnotatedElement > visited , int depth ) {
178
+ /**
179
+ * Perform the search algorithm for the {@link #process} method, avoiding
180
+ * endless recursion by tracking which annotated elements have already been
181
+ * <em>visited</em>.
182
+ *
183
+ * <p>The {@code metaDepth} parameter represents the depth of the annotation
184
+ * relative to the initial element. For example, an annotation that is
185
+ * <em>present</em> on the element will have a depth of 0; a meta-annotation
186
+ * will have a depth of 1; and a meta-meta-annotation will have a depth of 2.
187
+ *
188
+ * @param element the annotated element
189
+ * @param annotationType the annotation type to find
190
+ * @param traverseClassHierarchy whether or not to traverse up the class
191
+ * hierarchy recursively
192
+ * @param processor the processor to delegate to
193
+ * @param visited the set of annotated elements that have already been visited
194
+ * @param metaDepth the depth of the annotation relative to the initial element
195
+ * @return the result of the processor
196
+ */
197
+ private static <T > T doProcess (AnnotatedElement element , String annotationType , boolean traverseClassHierarchy ,
198
+ Processor <T > processor , Set <AnnotatedElement > visited , int metaDepth ) {
158
199
159
200
if (visited .add (element )) {
160
- for (Annotation annotation : element .getAnnotations ()) {
161
- if (annotation .annotationType ().getName ().equals (annotationType ) || depth > 0 ) {
162
- T result = processor .process (annotation , depth );
201
+
202
+ Annotation [] annotations = traverseClassHierarchy ? element .getDeclaredAnnotations ()
203
+ : element .getAnnotations ();
204
+
205
+ for (Annotation annotation : annotations ) {
206
+ if (annotation .annotationType ().getName ().equals (annotationType ) || metaDepth > 0 ) {
207
+ T result = processor .process (annotation , metaDepth );
163
208
if (result != null ) {
164
209
return result ;
165
210
}
166
- result = doProcess (annotation .annotationType (), annotationType , processor , visited , depth + 1 );
211
+ result = doProcess (annotation .annotationType (), annotationType , traverseClassHierarchy , processor ,
212
+ visited , metaDepth + 1 );
167
213
if (result != null ) {
168
214
processor .postProcess (annotation , result );
169
215
return result ;
170
216
}
171
217
}
172
218
}
173
- for (Annotation annotation : element .getAnnotations ()) {
174
- if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )) {
175
- T result = doProcess (annotation .annotationType (), annotationType , processor , visited , depth );
219
+
220
+ for (Annotation annotation : annotations ) {
221
+ if (!isInJavaLangAnnotationPackage (annotation )) {
222
+ T result = doProcess (annotation .annotationType (), annotationType , traverseClassHierarchy ,
223
+ processor , visited , metaDepth );
176
224
if (result != null ) {
177
225
processor .postProcess (annotation , result );
178
226
return result ;
179
227
}
180
228
}
181
229
}
230
+
231
+ if (traverseClassHierarchy && element instanceof Class ) {
232
+ Class <?> superclass = ((Class <?>) element ).getSuperclass ();
233
+ if (superclass != null && !superclass .equals (Object .class )) {
234
+ T result = doProcess (superclass , annotationType , traverseClassHierarchy , processor , visited ,
235
+ metaDepth );
236
+ if (result != null ) {
237
+ return result ;
238
+ }
239
+ }
240
+ }
182
241
}
242
+
183
243
return null ;
184
244
}
185
245
@@ -192,13 +252,18 @@ private static interface Processor<T> {
192
252
193
253
/**
194
254
* Called to process the annotation.
255
+ *
256
+ * <p>The {@code metaDepth} parameter represents the depth of the
257
+ * annotation relative to the initial element. For example, an annotation
258
+ * that is <em>present</em> on the element will have a depth of 0; a
259
+ * meta-annotation will have a depth of 1; and a meta-meta-annotation
260
+ * will have a depth of 2.
261
+ *
195
262
* @param annotation the annotation to process
196
- * @param depth the depth of the annotation relative to the initial match.
197
- * For example, a matched annotation will have a depth of 0, a meta-annotation
198
- * 1 and a meta-meta-annotation 2
263
+ * @param metaDepth the depth of the annotation relative to the initial element
199
264
* @return the result of the processing or {@code null} to continue
200
265
*/
201
- T process (Annotation annotation , int depth );
266
+ T process (Annotation annotation , int metaDepth );
202
267
203
268
void postProcess (Annotation annotation , T result );
204
269
}
0 commit comments