001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.annotation;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.AnnotatedElement;
021import java.lang.reflect.Method;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.HashSet;
025import java.util.LinkedHashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.springframework.core.BridgeMethodResolver;
031import org.springframework.util.Assert;
032import org.springframework.util.CollectionUtils;
033import org.springframework.util.LinkedMultiValueMap;
034import org.springframework.util.MultiValueMap;
035
036/**
037 * General utility methods for finding annotations, meta-annotations, and
038 * repeatable annotations on {@link AnnotatedElement AnnotatedElements}.
039 *
040 * <p>{@code AnnotatedElementUtils} defines the public API for Spring's
041 * meta-annotation programming model with support for <em>annotation attribute
042 * overrides</em>. If you do not need support for annotation attribute
043 * overrides, consider using {@link AnnotationUtils} instead.
044 *
045 * <p>Note that the features of this class are not provided by the JDK's
046 * introspection facilities themselves.
047 *
048 * <h3>Annotation Attribute Overrides</h3>
049 * <p>Support for meta-annotations with <em>attribute overrides</em> in
050 * <em>composed annotations</em> is provided by all variants of the
051 * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()},
052 * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()},
053 * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()},
054 * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()}
055 * methods.
056 *
057 * <h3>Find vs. Get Semantics</h3>
058 * <p>The search algorithms used by methods in this class follow either
059 * <em>find</em> or <em>get</em> semantics. Consult the javadocs for each
060 * individual method for details on which search algorithm is used.
061 *
062 * <p><strong>Get semantics</strong> are limited to searching for annotations
063 * that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared
064 * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared
065 * within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}.
066 *
067 * <p><strong>Find semantics</strong> are much more exhaustive, providing
068 * <em>get semantics</em> plus support for the following:
069 *
070 * <ul>
071 * <li>Searching on interfaces, if the annotated element is a class
072 * <li>Searching on superclasses, if the annotated element is a class
073 * <li>Resolving bridged methods, if the annotated element is a method
074 * <li>Searching on methods in interfaces, if the annotated element is a method
075 * <li>Searching on methods in superclasses, if the annotated element is a method
076 * </ul>
077 *
078 * <h3>Support for {@code @Inherited}</h3>
079 * <p>Methods following <em>get semantics</em> will honor the contract of Java's
080 * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally
081 * declared annotations (including custom composed annotations) will be favored over
082 * inherited annotations. In contrast, methods following <em>find semantics</em>
083 * will completely ignore the presence of {@code @Inherited} since the <em>find</em>
084 * search algorithm manually traverses type and method hierarchies and thereby
085 * implicitly supports annotation inheritance without a need for {@code @Inherited}.
086 *
087 * @author Phillip Webb
088 * @author Juergen Hoeller
089 * @author Sam Brannen
090 * @since 4.0
091 * @see AliasFor
092 * @see AnnotationAttributes
093 * @see AnnotationUtils
094 * @see BridgeMethodResolver
095 */
096public class AnnotatedElementUtils {
097
098        /**
099         * {@code null} constant used to denote that the search algorithm should continue.
100         */
101        private static final Boolean CONTINUE = null;
102
103        private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
104
105        private static final Processor<Boolean> alwaysTrueAnnotationProcessor = new AlwaysTrueBooleanAnnotationProcessor();
106
107
108        /**
109         * Build an adapted {@link AnnotatedElement} for the given annotations,
110         * typically for use with other methods on {@link AnnotatedElementUtils}.
111         * @param annotations the annotations to expose through the {@code AnnotatedElement}
112         * @since 4.3
113         */
114        public static AnnotatedElement forAnnotations(final Annotation... annotations) {
115                return new AnnotatedElement() {
116                        @Override
117                        @SuppressWarnings("unchecked")
118                        public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
119                                for (Annotation ann : annotations) {
120                                        if (ann.annotationType() == annotationClass) {
121                                                return (T) ann;
122                                        }
123                                }
124                                return null;
125                        }
126                        @Override
127                        public Annotation[] getAnnotations() {
128                                return annotations;
129                        }
130                        @Override
131                        public Annotation[] getDeclaredAnnotations() {
132                                return annotations;
133                        }
134                };
135        }
136
137        /**
138         * Get the fully qualified class names of all meta-annotation types
139         * <em>present</em> on the annotation (of the specified {@code annotationType})
140         * on the supplied {@link AnnotatedElement}.
141         * <p>This method follows <em>get semantics</em> as described in the
142         * {@linkplain AnnotatedElementUtils class-level javadoc}.
143         * @param element the annotated element
144         * @param annotationType the annotation type on which to find meta-annotations
145         * @return the names of all meta-annotations present on the annotation,
146         * or {@code null} if not found
147         * @since 4.2
148         * @see #getMetaAnnotationTypes(AnnotatedElement, String)
149         * @see #hasMetaAnnotationTypes
150         */
151        public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
152                Assert.notNull(element, "AnnotatedElement must not be null");
153                Assert.notNull(annotationType, "'annotationType' must not be null");
154
155                return getMetaAnnotationTypes(element, element.getAnnotation(annotationType));
156        }
157
158        /**
159         * Get the fully qualified class names of all meta-annotation
160         * types <em>present</em> on the annotation (of the specified
161         * {@code annotationName}) on the supplied {@link AnnotatedElement}.
162         * <p>This method follows <em>get semantics</em> as described in the
163         * {@linkplain AnnotatedElementUtils class-level javadoc}.
164         * @param element the annotated element
165         * @param annotationName the fully qualified class name of the annotation
166         * type on which to find meta-annotations
167         * @return the names of all meta-annotations present on the annotation,
168         * or {@code null} if not found
169         * @see #getMetaAnnotationTypes(AnnotatedElement, Class)
170         * @see #hasMetaAnnotationTypes
171         */
172        public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
173                Assert.notNull(element, "AnnotatedElement must not be null");
174                Assert.hasLength(annotationName, "'annotationName' must not be null or empty");
175
176                return getMetaAnnotationTypes(element, AnnotationUtils.getAnnotation(element, annotationName));
177        }
178
179        private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Annotation composed) {
180                if (composed == null) {
181                        return null;
182                }
183
184                try {
185                        final Set<String> types = new LinkedHashSet<String>();
186                        searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor<Object>(true) {
187                                        @Override
188                                        public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
189                                                types.add(annotation.annotationType().getName());
190                                                return CONTINUE;
191                                        }
192                                }, new HashSet<AnnotatedElement>(), 1);
193                        return (!types.isEmpty() ? types : null);
194                }
195                catch (Throwable ex) {
196                        AnnotationUtils.rethrowAnnotationConfigurationException(ex);
197                        throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
198                }
199        }
200
201        /**
202         * Determine if the supplied {@link AnnotatedElement} is annotated with
203         * a <em>composed annotation</em> that is meta-annotated with an
204         * annotation of the specified {@code annotationType}.
205         * <p>This method follows <em>get semantics</em> as described in the
206         * {@linkplain AnnotatedElementUtils class-level javadoc}.
207         * @param element the annotated element
208         * @param annotationType the meta-annotation type to find
209         * @return {@code true} if a matching meta-annotation is present
210         * @since 4.2.3
211         * @see #getMetaAnnotationTypes
212         */
213        public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
214                Assert.notNull(element, "AnnotatedElement must not be null");
215                Assert.notNull(annotationType, "'annotationType' must not be null");
216
217                return hasMetaAnnotationTypes(element, annotationType, null);
218        }
219
220        /**
221         * Determine if the supplied {@link AnnotatedElement} is annotated with a
222         * <em>composed annotation</em> that is meta-annotated with an annotation
223         * of the specified {@code annotationName}.
224         * <p>This method follows <em>get semantics</em> as described in the
225         * {@linkplain AnnotatedElementUtils class-level javadoc}.
226         * @param element the annotated element
227         * @param annotationName the fully qualified class name of the
228         * meta-annotation type to find
229         * @return {@code true} if a matching meta-annotation is present
230         * @see #getMetaAnnotationTypes
231         */
232        public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
233                Assert.notNull(element, "AnnotatedElement must not be null");
234                Assert.hasLength(annotationName, "'annotationName' must not be null or empty");
235
236                return hasMetaAnnotationTypes(element, null, annotationName);
237        }
238
239        private static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType,
240                        String annotationName) {
241
242                return Boolean.TRUE.equals(
243                        searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
244                                @Override
245                                public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
246                                        return (metaDepth > 0 ? Boolean.TRUE : CONTINUE);
247                                }
248                        }));
249        }
250
251        /**
252         * Determine if an annotation of the specified {@code annotationType}
253         * is <em>present</em> on the supplied {@link AnnotatedElement} or
254         * within the annotation hierarchy <em>above</em> the specified element.
255         * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
256         * will return a non-null value.
257         * <p>This method follows <em>get semantics</em> as described in the
258         * {@linkplain AnnotatedElementUtils class-level javadoc}.
259         * @param element the annotated element
260         * @param annotationType the annotation type to find
261         * @return {@code true} if a matching annotation is present
262         * @since 4.2.3
263         * @see #hasAnnotation(AnnotatedElement, Class)
264         */
265        public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) {
266                Assert.notNull(element, "AnnotatedElement must not be null");
267                Assert.notNull(annotationType, "'annotationType' must not be null");
268
269                // Shortcut: directly present on the element, with no processing needed?
270                if (element.isAnnotationPresent(annotationType)) {
271                        return true;
272                }
273                return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
274        }
275
276        /**
277         * Determine if an annotation of the specified {@code annotationName} is
278         * <em>present</em> on the supplied {@link AnnotatedElement} or within the
279         * annotation hierarchy <em>above</em> the specified element.
280         * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
281         * will return a non-null value.
282         * <p>This method follows <em>get semantics</em> as described in the
283         * {@linkplain AnnotatedElementUtils class-level javadoc}.
284         * @param element the annotated element
285         * @param annotationName the fully qualified class name of the annotation type to find
286         * @return {@code true} if a matching annotation is present
287         */
288        public static boolean isAnnotated(AnnotatedElement element, String annotationName) {
289                Assert.notNull(element, "AnnotatedElement must not be null");
290                Assert.hasLength(annotationName, "'annotationName' must not be null or empty");
291
292                return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, alwaysTrueAnnotationProcessor));
293        }
294
295        /**
296         * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String)} instead.
297         */
298        @Deprecated
299        public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName) {
300                return getMergedAnnotationAttributes(element, annotationName);
301        }
302
303        /**
304         * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} instead.
305         */
306        @Deprecated
307        public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName,
308                        boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
309
310                return getMergedAnnotationAttributes(element, annotationName, classValuesAsString, nestedAnnotationsAsMap);
311        }
312
313        /**
314         * Get the first annotation of the specified {@code annotationType} within
315         * the annotation hierarchy <em>above</em> the supplied {@code element} and
316         * merge that annotation's attributes with <em>matching</em> attributes from
317         * annotations in lower levels of the annotation hierarchy.
318         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
319         * within a single annotation and within the annotation hierarchy.
320         * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}.
321         * @param element the annotated element
322         * @param annotationType the annotation type to find
323         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
324         * @since 4.2
325         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
326         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
327         * @see #getMergedAnnotation(AnnotatedElement, Class)
328         * @see #findMergedAnnotation(AnnotatedElement, Class)
329         */
330        public static AnnotationAttributes getMergedAnnotationAttributes(
331                        AnnotatedElement element, Class<? extends Annotation> annotationType) {
332
333                Assert.notNull(annotationType, "'annotationType' must not be null");
334                AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null,
335                                new MergedAnnotationAttributesProcessor());
336                AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
337                return attributes;
338        }
339
340        /**
341         * Get the first annotation of the specified {@code annotationName} within
342         * the annotation hierarchy <em>above</em> the supplied {@code element} and
343         * merge that annotation's attributes with <em>matching</em> attributes from
344         * annotations in lower levels of the annotation hierarchy.
345         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
346         * within a single annotation and within the annotation hierarchy.
347         * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
348         * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}.
349         * @param element the annotated element
350         * @param annotationName the fully qualified class name of the annotation type to find
351         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
352         * @since 4.2
353         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
354         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
355         * @see #findMergedAnnotation(AnnotatedElement, Class)
356         * @see #getAllAnnotationAttributes(AnnotatedElement, String)
357         */
358        public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName) {
359                return getMergedAnnotationAttributes(element, annotationName, false, false);
360        }
361
362        /**
363         * Get the first annotation of the specified {@code annotationName} within
364         * the annotation hierarchy <em>above</em> the supplied {@code element} and
365         * merge that annotation's attributes with <em>matching</em> attributes from
366         * annotations in lower levels of the annotation hierarchy.
367         * <p>Attributes from lower levels in the annotation hierarchy override attributes
368         * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are
369         * fully supported, both within a single annotation and within the annotation hierarchy.
370         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by
371         * this method will stop searching the annotation hierarchy once the first annotation
372         * of the specified {@code annotationName} has been found. As a consequence,
373         * additional annotations of the specified {@code annotationName} will be ignored.
374         * <p>This method follows <em>get semantics</em> as described in the
375         * {@linkplain AnnotatedElementUtils class-level javadoc}.
376         * @param element the annotated element
377         * @param annotationName the fully qualified class name of the annotation type to find
378         * @param classValuesAsString whether to convert Class references into Strings or to
379         * preserve them as Class references
380         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances
381         * into {@code AnnotationAttributes} maps or to preserve them as Annotation instances
382         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
383         * @since 4.2
384         * @see #findMergedAnnotation(AnnotatedElement, Class)
385         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
386         * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
387         */
388        public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
389                        String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
390
391                Assert.hasLength(annotationName, "'annotationName' must not be null or empty");
392                AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName,
393                                new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
394                AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
395                return attributes;
396        }
397
398        /**
399         * Get the first annotation of the specified {@code annotationType} within
400         * the annotation hierarchy <em>above</em> the supplied {@code element},
401         * merge that annotation's attributes with <em>matching</em> attributes from
402         * annotations in lower levels of the annotation hierarchy, and synthesize
403         * the result back into an annotation of the specified {@code annotationType}.
404         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
405         * within a single annotation and within the annotation hierarchy.
406         * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)}
407         * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}.
408         * @param element the annotated element
409         * @param annotationType the annotation type to find
410         * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
411         * @since 4.2
412         * @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
413         * @see #findMergedAnnotation(AnnotatedElement, Class)
414         * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)
415         */
416        public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
417                Assert.notNull(annotationType, "'annotationType' must not be null");
418
419                // Shortcut: directly present on the element, with no merging needed?
420                if (!(element instanceof Class)) {
421                        // Do not use this shortcut against a Class: Inherited annotations
422                        // would get preferred over locally declared composed annotations.
423                        A annotation = element.getAnnotation(annotationType);
424                        if (annotation != null) {
425                                return AnnotationUtils.synthesizeAnnotation(annotation, element);
426                        }
427                }
428
429                // Exhaustive retrieval of merged annotation attributes...
430                AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType);
431                return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element);
432        }
433
434        /**
435         * Get <strong>all</strong> annotations of the specified {@code annotationType}
436         * within the annotation hierarchy <em>above</em> the supplied {@code element};
437         * and for each annotation found, merge that annotation's attributes with
438         * <em>matching</em> attributes from annotations in lower levels of the annotation
439         * hierarchy and synthesize the results back into an annotation of the specified
440         * {@code annotationType}.
441         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
442         * single annotation and within annotation hierarchies.
443         * <p>This method follows <em>get semantics</em> as described in the
444         * {@linkplain AnnotatedElementUtils class-level javadoc}.
445         * @param element the annotated element (never {@code null})
446         * @param annotationType the annotation type to find (never {@code null})
447         * @return the set of all merged, synthesized {@code Annotations} found,
448         * or an empty set if none were found
449         * @since 4.3
450         * @see #getMergedAnnotation(AnnotatedElement, Class)
451         * @see #getAllAnnotationAttributes(AnnotatedElement, String)
452         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
453         */
454        public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element,
455                        Class<A> annotationType) {
456
457                Assert.notNull(element, "AnnotatedElement must not be null");
458                Assert.notNull(annotationType, "'annotationType' must not be null");
459
460                MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
461                searchWithGetSemantics(element, annotationType, null, processor);
462                return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
463        }
464
465        /**
466         * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
467         * within the annotation hierarchy <em>above</em> the supplied {@code element};
468         * and for each annotation found, merge that annotation's attributes with
469         * <em>matching</em> attributes from annotations in lower levels of the annotation
470         * hierarchy and synthesize the results back into an annotation of the specified
471         * {@code annotationType}.
472         * <p>The container type that holds the repeatable annotations will be looked up
473         * via {@link java.lang.annotation.Repeatable}.
474         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
475         * single annotation and within annotation hierarchies.
476         * <p>This method follows <em>get semantics</em> as described in the
477         * {@linkplain AnnotatedElementUtils class-level javadoc}.
478         * @param element the annotated element (never {@code null})
479         * @param annotationType the annotation type to find (never {@code null})
480         * @return the set of all merged repeatable {@code Annotations} found,
481         * or an empty set if none were found
482         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
483         * is {@code null}, or if the container type cannot be resolved
484         * @since 4.3
485         * @see #getMergedAnnotation(AnnotatedElement, Class)
486         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
487         * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
488         */
489        public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element,
490                        Class<A> annotationType) {
491
492                return getMergedRepeatableAnnotations(element, annotationType, null);
493        }
494
495        /**
496         * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
497         * within the annotation hierarchy <em>above</em> the supplied {@code element};
498         * and for each annotation found, merge that annotation's attributes with
499         * <em>matching</em> attributes from annotations in lower levels of the annotation
500         * hierarchy and synthesize the results back into an annotation of the specified
501         * {@code annotationType}.
502         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
503         * single annotation and within annotation hierarchies.
504         * <p>This method follows <em>get semantics</em> as described in the
505         * {@linkplain AnnotatedElementUtils class-level javadoc}.
506         * @param element the annotated element (never {@code null})
507         * @param annotationType the annotation type to find (never {@code null})
508         * @param containerType the type of the container that holds the annotations;
509         * may be {@code null} if the container type should be looked up via
510         * {@link java.lang.annotation.Repeatable}
511         * @return the set of all merged repeatable {@code Annotations} found,
512         * or an empty set if none were found
513         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
514         * is {@code null}, or if the container type cannot be resolved
515         * @throws AnnotationConfigurationException if the supplied {@code containerType}
516         * is not a valid container annotation for the supplied {@code annotationType}
517         * @since 4.3
518         * @see #getMergedAnnotation(AnnotatedElement, Class)
519         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
520         */
521        public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element,
522                        Class<A> annotationType, Class<? extends Annotation> containerType) {
523
524                Assert.notNull(element, "AnnotatedElement must not be null");
525                Assert.notNull(annotationType, "'annotationType' must not be null");
526
527                if (containerType == null) {
528                        containerType = resolveContainerType(annotationType);
529                }
530                else {
531                        validateContainerType(annotationType, containerType);
532                }
533
534                MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
535                searchWithGetSemantics(element, annotationType, null, containerType, processor);
536                return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
537        }
538
539        /**
540         * Get the annotation attributes of <strong>all</strong> annotations of the specified
541         * {@code annotationName} in the annotation hierarchy above the supplied
542         * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}.
543         * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
544         * this method does <em>not</em> support attribute overrides.
545         * <p>This method follows <em>get semantics</em> as described in the
546         * {@linkplain AnnotatedElementUtils class-level javadoc}.
547         * @param element the annotated element
548         * @param annotationName the fully qualified class name of the annotation type to find
549         * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
550         * attributes from all annotations found, or {@code null} if not found
551         * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
552         */
553        public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName) {
554                return getAllAnnotationAttributes(element, annotationName, false, false);
555        }
556
557        /**
558         * Get the annotation attributes of <strong>all</strong> annotations of
559         * the specified {@code annotationName} in the annotation hierarchy above
560         * the supplied {@link AnnotatedElement} and store the results in a
561         * {@link MultiValueMap}.
562         * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
563         * this method does <em>not</em> support attribute overrides.
564         * <p>This method follows <em>get semantics</em> as described in the
565         * {@linkplain AnnotatedElementUtils class-level javadoc}.
566         * @param element the annotated element
567         * @param annotationName the fully qualified class name of the annotation type to find
568         * @param classValuesAsString whether to convert Class references into Strings or to
569         * preserve them as Class references
570         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
571         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
572         * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
573         * attributes from all annotations found, or {@code null} if not found
574         */
575        public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
576                        String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
577
578                final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<String, Object>();
579
580                searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Object>() {
581                        @Override
582                        public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
583                                AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(
584                                                annotation, classValuesAsString, nestedAnnotationsAsMap);
585                                for (Map.Entry<String, Object> entry : annotationAttributes.entrySet()) {
586                                        attributesMap.add(entry.getKey(), entry.getValue());
587                                }
588                                return CONTINUE;
589                        }
590                });
591
592                return (!attributesMap.isEmpty() ? attributesMap : null);
593        }
594
595        /**
596         * Determine if an annotation of the specified {@code annotationType}
597         * is <em>available</em> on the supplied {@link AnnotatedElement} or
598         * within the annotation hierarchy <em>above</em> the specified element.
599         * <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes}
600         * will return a non-null value.
601         * <p>This method follows <em>find semantics</em> as described in the
602         * {@linkplain AnnotatedElementUtils class-level javadoc}.
603         * @param element the annotated element
604         * @param annotationType the annotation type to find
605         * @return {@code true} if a matching annotation is present
606         * @since 4.3
607         * @see #isAnnotated(AnnotatedElement, Class)
608         */
609        public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
610                Assert.notNull(element, "AnnotatedElement must not be null");
611                Assert.notNull(annotationType, "'annotationType' must not be null");
612
613                // Shortcut: directly present on the element, with no processing needed?
614                if (element.isAnnotationPresent(annotationType)) {
615                        return true;
616                }
617                return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
618        }
619
620        /**
621         * Find the first annotation of the specified {@code annotationType} within
622         * the annotation hierarchy <em>above</em> the supplied {@code element} and
623         * merge that annotation's attributes with <em>matching</em> attributes from
624         * annotations in lower levels of the annotation hierarchy.
625         * <p>Attributes from lower levels in the annotation hierarchy override
626         * attributes of the same name from higher levels, and
627         * {@link AliasFor @AliasFor} semantics are fully supported, both
628         * within a single annotation and within the annotation hierarchy.
629         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm
630         * used by this method will stop searching the annotation hierarchy once the
631         * first annotation of the specified {@code annotationType} has been found.
632         * As a consequence, additional annotations of the specified
633         * {@code annotationType} will be ignored.
634         * <p>This method follows <em>find semantics</em> as described in the
635         * {@linkplain AnnotatedElementUtils class-level javadoc}.
636         * @param element the annotated element
637         * @param annotationType the annotation type to find
638         * @param classValuesAsString whether to convert Class references into
639         * Strings or to preserve them as Class references
640         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
641         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
642         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
643         * @since 4.2
644         * @see #findMergedAnnotation(AnnotatedElement, Class)
645         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
646         */
647        public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
648                        Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
649
650                AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null,
651                                new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
652                AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
653                return attributes;
654        }
655
656        /**
657         * Find the first annotation of the specified {@code annotationName} within
658         * the annotation hierarchy <em>above</em> the supplied {@code element} and
659         * merge that annotation's attributes with <em>matching</em> attributes from
660         * annotations in lower levels of the annotation hierarchy.
661         * <p>Attributes from lower levels in the annotation hierarchy override
662         * attributes of the same name from higher levels, and
663         * {@link AliasFor @AliasFor} semantics are fully supported, both
664         * within a single annotation and within the annotation hierarchy.
665         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search
666         * algorithm used by this method will stop searching the annotation
667         * hierarchy once the first annotation of the specified
668         * {@code annotationName} has been found. As a consequence, additional
669         * annotations of the specified {@code annotationName} will be ignored.
670         * <p>This method follows <em>find semantics</em> as described in the
671         * {@linkplain AnnotatedElementUtils class-level javadoc}.
672         * @param element the annotated element
673         * @param annotationName the fully qualified class name of the annotation type to find
674         * @param classValuesAsString whether to convert Class references into Strings or to
675         * preserve them as Class references
676         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
677         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
678         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
679         * @since 4.2
680         * @see #findMergedAnnotation(AnnotatedElement, Class)
681         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
682         */
683        public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
684                        String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
685
686                AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName,
687                                new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
688                AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
689                return attributes;
690        }
691
692        /**
693         * Find the first annotation of the specified {@code annotationType} within
694         * the annotation hierarchy <em>above</em> the supplied {@code element},
695         * merge that annotation's attributes with <em>matching</em> attributes from
696         * annotations in lower levels of the annotation hierarchy, and synthesize
697         * the result back into an annotation of the specified {@code annotationType}.
698         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
699         * within a single annotation and within the annotation hierarchy.
700         * <p>This method follows <em>find semantics</em> as described in the
701         * {@linkplain AnnotatedElementUtils class-level javadoc}.
702         * @param element the annotated element
703         * @param annotationType the annotation type to find
704         * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
705         * @since 4.2
706         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
707         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
708         * @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
709         */
710        public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
711                Assert.notNull(annotationType, "'annotationType' must not be null");
712
713                // Shortcut: directly present on the element, with no merging needed?
714                if (!(element instanceof Class)) {
715                        // Do not use this shortcut against a Class: Inherited annotations
716                        // would get preferred over locally declared composed annotations.
717                        A annotation = element.getAnnotation(annotationType);
718                        if (annotation != null) {
719                                return AnnotationUtils.synthesizeAnnotation(annotation, element);
720                        }
721                }
722
723                // Exhaustive retrieval of merged annotation attributes...
724                AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false);
725                return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element);
726        }
727
728        /**
729         * Find the first annotation of the specified {@code annotationName} within
730         * the annotation hierarchy <em>above</em> the supplied {@code element},
731         * merge that annotation's attributes with <em>matching</em> attributes from
732         * annotations in lower levels of the annotation hierarchy, and synthesize
733         * the result back into an annotation of the specified {@code annotationName}.
734         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
735         * within a single annotation and within the annotation hierarchy.
736         * <p>This method delegates to {@link #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}
737         * (supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap})
738         * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}.
739         * <p>This method follows <em>find semantics</em> as described in the
740         * {@linkplain AnnotatedElementUtils class-level javadoc}.
741         * @param element the annotated element
742         * @param annotationName the fully qualified class name of the annotation type to find
743         * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
744         * @since 4.2
745         * @see #findMergedAnnotation(AnnotatedElement, Class)
746         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
747         * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)
748         * @deprecated As of Spring Framework 4.2.3, use {@link #findMergedAnnotation(AnnotatedElement, Class)} instead.
749         */
750        @Deprecated
751        @SuppressWarnings("unchecked")
752        public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, String annotationName) {
753                AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationName, false, false);
754                return AnnotationUtils.synthesizeAnnotation(attributes, (Class<A>) attributes.annotationType(), element);
755        }
756
757        /**
758         * Find <strong>all</strong> annotations of the specified {@code annotationType}
759         * within the annotation hierarchy <em>above</em> the supplied {@code element};
760         * and for each annotation found, merge that annotation's attributes with
761         * <em>matching</em> attributes from annotations in lower levels of the annotation
762         * hierarchy and synthesize the results back into an annotation of the specified
763         * {@code annotationType}.
764         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
765         * single annotation and within annotation hierarchies.
766         * <p>This method follows <em>find semantics</em> as described in the
767         * {@linkplain AnnotatedElementUtils class-level javadoc}.
768         * @param element the annotated element (never {@code null})
769         * @param annotationType the annotation type to find (never {@code null})
770         * @return the set of all merged, synthesized {@code Annotations} found,
771         * or an empty set if none were found
772         * @since 4.3
773         * @see #findMergedAnnotation(AnnotatedElement, Class)
774         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
775         */
776        public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element,
777                        Class<A> annotationType) {
778
779                Assert.notNull(element, "AnnotatedElement must not be null");
780                Assert.notNull(annotationType, "'annotationType' must not be null");
781
782                MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
783                searchWithFindSemantics(element, annotationType, null, processor);
784                return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
785        }
786
787        /**
788         * Find all <em>repeatable annotations</em> of the specified {@code annotationType}
789         * within the annotation hierarchy <em>above</em> the supplied {@code element};
790         * and for each annotation found, merge that annotation's attributes with
791         * <em>matching</em> attributes from annotations in lower levels of the annotation
792         * hierarchy and synthesize the results back into an annotation of the specified
793         * {@code annotationType}.
794         * <p>The container type that holds the repeatable annotations will be looked up
795         * via {@link java.lang.annotation.Repeatable}.
796         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
797         * single annotation and within annotation hierarchies.
798         * <p>This method follows <em>find semantics</em> as described in the
799         * {@linkplain AnnotatedElementUtils class-level javadoc}.
800         * @param element the annotated element (never {@code null})
801         * @param annotationType the annotation type to find (never {@code null})
802         * @return the set of all merged repeatable {@code Annotations} found,
803         * or an empty set if none were found
804         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
805         * is {@code null}, or if the container type cannot be resolved
806         * @since 4.3
807         * @see #findMergedAnnotation(AnnotatedElement, Class)
808         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
809         * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
810         */
811        public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
812                        Class<A> annotationType) {
813
814                return findMergedRepeatableAnnotations(element, annotationType, null);
815        }
816
817        /**
818         * Find all <em>repeatable annotations</em> of the specified {@code annotationType}
819         * within the annotation hierarchy <em>above</em> the supplied {@code element};
820         * and for each annotation found, merge that annotation's attributes with
821         * <em>matching</em> attributes from annotations in lower levels of the annotation
822         * hierarchy and synthesize the results back into an annotation of the specified
823         * {@code annotationType}.
824         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
825         * single annotation and within annotation hierarchies.
826         * <p>This method follows <em>find semantics</em> as described in the
827         * {@linkplain AnnotatedElementUtils class-level javadoc}.
828         * @param element the annotated element (never {@code null})
829         * @param annotationType the annotation type to find (never {@code null})
830         * @param containerType the type of the container that holds the annotations;
831         * may be {@code null} if the container type should be looked up via
832         * {@link java.lang.annotation.Repeatable}
833         * @return the set of all merged repeatable {@code Annotations} found,
834         * or an empty set if none were found
835         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
836         * is {@code null}, or if the container type cannot be resolved
837         * @throws AnnotationConfigurationException if the supplied {@code containerType}
838         * is not a valid container annotation for the supplied {@code annotationType}
839         * @since 4.3
840         * @see #findMergedAnnotation(AnnotatedElement, Class)
841         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
842         */
843        public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
844                        Class<A> annotationType, Class<? extends Annotation> containerType) {
845
846                Assert.notNull(element, "AnnotatedElement must not be null");
847                Assert.notNull(annotationType, "'annotationType' must not be null");
848
849                if (containerType == null) {
850                        containerType = resolveContainerType(annotationType);
851                }
852                else {
853                        validateContainerType(annotationType, containerType);
854                }
855
856                MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
857                searchWithFindSemantics(element, annotationType, null, containerType, processor);
858                return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
859        }
860
861        /**
862         * Search for annotations of the specified {@code annotationName} or
863         * {@code annotationType} on the specified {@code element}, following
864         * <em>get semantics</em>.
865         * @param element the annotated element
866         * @param annotationType the annotation type to find
867         * @param annotationName the fully qualified class name of the annotation
868         * type to find (as an alternative to {@code annotationType})
869         * @param processor the processor to delegate to
870         * @return the result of the processor (potentially {@code null})
871         */
872        private static <T> T searchWithGetSemantics(AnnotatedElement element,
873                        Class<? extends Annotation> annotationType, String annotationName, Processor<T> processor) {
874
875                return searchWithGetSemantics(element, annotationType, annotationName, null, processor);
876        }
877
878        /**
879         * Search for annotations of the specified {@code annotationName} or
880         * {@code annotationType} on the specified {@code element}, following
881         * <em>get semantics</em>.
882         * @param element the annotated element
883         * @param annotationType the annotation type to find
884         * @param annotationName the fully qualified class name of the annotation
885         * type to find (as an alternative to {@code annotationType})
886         * @param containerType the type of the container that holds repeatable
887         * annotations, or {@code null} if the annotation is not repeatable
888         * @param processor the processor to delegate to
889         * @return the result of the processor (potentially {@code null})
890         * @since 4.3
891         */
892        private static <T> T searchWithGetSemantics(AnnotatedElement element,
893                        Class<? extends Annotation> annotationType, String annotationName,
894                        Class<? extends Annotation> containerType, Processor<T> processor) {
895
896                try {
897                        return searchWithGetSemantics(element, annotationType, annotationName,
898                                        containerType, processor, new HashSet<AnnotatedElement>(), 0);
899                }
900                catch (Throwable ex) {
901                        AnnotationUtils.rethrowAnnotationConfigurationException(ex);
902                        throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
903                }
904        }
905
906        /**
907         * Perform the search algorithm for the {@link #searchWithGetSemantics}
908         * method, avoiding endless recursion by tracking which annotated elements
909         * have already been <em>visited</em>.
910         * <p>The {@code metaDepth} parameter is explained in the
911         * {@link Processor#process process()} method of the {@link Processor} API.
912         * @param element the annotated element
913         * @param annotationType the annotation type to find
914         * @param annotationName the fully qualified class name of the annotation
915         * type to find (as an alternative to {@code annotationType})
916         * @param containerType the type of the container that holds repeatable
917         * annotations, or {@code null} if the annotation is not repeatable
918         * @param processor the processor to delegate to
919         * @param visited the set of annotated elements that have already been visited
920         * @param metaDepth the meta-depth of the annotation
921         * @return the result of the processor (potentially {@code null})
922         */
923        private static <T> T searchWithGetSemantics(AnnotatedElement element,
924                        Class<? extends Annotation> annotationType, String annotationName,
925                        Class<? extends Annotation> containerType, Processor<T> processor,
926                        Set<AnnotatedElement> visited, int metaDepth) {
927
928                Assert.notNull(element, "AnnotatedElement must not be null");
929
930                if (visited.add(element)) {
931                        try {
932                                // Start searching within locally declared annotations
933                                List<Annotation> declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations());
934                                T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
935                                                annotationType, annotationName, containerType, processor, visited, metaDepth);
936                                if (result != null) {
937                                        return result;
938                                }
939
940                                if (element instanceof Class) {  // otherwise getAnnotations does not return anything new
941                                        List<Annotation> inheritedAnnotations = new ArrayList<Annotation>();
942                                        for (Annotation annotation : element.getAnnotations()) {
943                                                if (!declaredAnnotations.contains(annotation)) {
944                                                        inheritedAnnotations.add(annotation);
945                                                }
946                                        }
947
948                                        // Continue searching within inherited annotations
949                                        result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations,
950                                                        annotationType, annotationName, containerType, processor, visited, metaDepth);
951                                        if (result != null) {
952                                                return result;
953                                        }
954                                }
955                        }
956                        catch (Throwable ex) {
957                                AnnotationUtils.handleIntrospectionFailure(element, ex);
958                        }
959                }
960
961                return null;
962        }
963
964        /**
965         * This method is invoked by {@link #searchWithGetSemantics} to perform
966         * the actual search within the supplied list of annotations.
967         * <p>This method should be invoked first with locally declared annotations
968         * and then subsequently with inherited annotations, thereby allowing
969         * local annotations to take precedence over inherited annotations.
970         * <p>The {@code metaDepth} parameter is explained in the
971         * {@link Processor#process process()} method of the {@link Processor} API.
972         * @param element the element that is annotated with the supplied
973         * annotations, used for contextual logging; may be {@code null} if unknown
974         * @param annotations the annotations to search in
975         * @param annotationType the annotation type to find
976         * @param annotationName the fully qualified class name of the annotation
977         * type to find (as an alternative to {@code annotationType})
978         * @param containerType the type of the container that holds repeatable
979         * annotations, or {@code null} if the annotation is not repeatable
980         * @param processor the processor to delegate to
981         * @param visited the set of annotated elements that have already been visited
982         * @param metaDepth the meta-depth of the annotation
983         * @return the result of the processor (potentially {@code null})
984         * @since 4.2
985         */
986        private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element,
987                        List<Annotation> annotations, Class<? extends Annotation> annotationType,
988                        String annotationName, Class<? extends Annotation> containerType,
989                        Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
990
991                // Search in annotations
992                for (Annotation annotation : annotations) {
993                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
994                        if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
995                                if (currentAnnotationType == annotationType ||
996                                                currentAnnotationType.getName().equals(annotationName) ||
997                                                processor.alwaysProcesses()) {
998                                        T result = processor.process(element, annotation, metaDepth);
999                                        if (result != null) {
1000                                                if (processor.aggregates() && metaDepth == 0) {
1001                                                        processor.getAggregatedResults().add(result);
1002                                                }
1003                                                else {
1004                                                        return result;
1005                                                }
1006                                        }
1007                                }
1008                                // Repeatable annotations in container?
1009                                else if (currentAnnotationType == containerType) {
1010                                        for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
1011                                                T result = processor.process(element, contained, metaDepth);
1012                                                if (result != null) {
1013                                                        // No need to post-process since repeatable annotations within a
1014                                                        // container cannot be composed annotations.
1015                                                        processor.getAggregatedResults().add(result);
1016                                                }
1017                                        }
1018                                }
1019                        }
1020                }
1021
1022                // Recursively search in meta-annotations
1023                for (Annotation annotation : annotations) {
1024                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
1025                        if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
1026                                T result = searchWithGetSemantics(currentAnnotationType, annotationType,
1027                                                annotationName, containerType, processor, visited, metaDepth + 1);
1028                                if (result != null) {
1029                                        processor.postProcess(element, annotation, result);
1030                                        if (processor.aggregates() && metaDepth == 0) {
1031                                                processor.getAggregatedResults().add(result);
1032                                        }
1033                                        else {
1034                                                return result;
1035                                        }
1036                                }
1037                        }
1038                }
1039
1040                return null;
1041        }
1042
1043        /**
1044         * Search for annotations of the specified {@code annotationName} or
1045         * {@code annotationType} on the specified {@code element}, following
1046         * <em>find semantics</em>.
1047         * @param element the annotated element
1048         * @param annotationType the annotation type to find
1049         * @param annotationName the fully qualified class name of the annotation
1050         * type to find (as an alternative to {@code annotationType})
1051         * @param processor the processor to delegate to
1052         * @return the result of the processor (potentially {@code null})
1053         * @since 4.2
1054         */
1055        private static <T> T searchWithFindSemantics(AnnotatedElement element,
1056                        Class<? extends Annotation> annotationType,
1057                        String annotationName, Processor<T> processor) {
1058
1059                return searchWithFindSemantics(element, annotationType, annotationName, null, processor);
1060        }
1061
1062        /**
1063         * Search for annotations of the specified {@code annotationName} or
1064         * {@code annotationType} on the specified {@code element}, following
1065         * <em>find semantics</em>.
1066         * @param element the annotated element
1067         * @param annotationType the annotation type to find
1068         * @param annotationName the fully qualified class name of the annotation
1069         * type to find (as an alternative to {@code annotationType})
1070         * @param containerType the type of the container that holds repeatable
1071         * annotations, or {@code null} if the annotation is not repeatable
1072         * @param processor the processor to delegate to
1073         * @return the result of the processor (potentially {@code null})
1074         * @since 4.3
1075         */
1076        private static <T> T searchWithFindSemantics(AnnotatedElement element,
1077                        Class<? extends Annotation> annotationType, String annotationName,
1078                        Class<? extends Annotation> containerType, Processor<T> processor) {
1079
1080                if (containerType != null && !processor.aggregates()) {
1081                        throw new IllegalArgumentException(
1082                                        "Searches for repeatable annotations must supply an aggregating Processor");
1083                }
1084
1085                try {
1086                        return searchWithFindSemantics(element, annotationType, annotationName,
1087                                        containerType, processor, new HashSet<AnnotatedElement>(), 0);
1088                }
1089                catch (Throwable ex) {
1090                        AnnotationUtils.rethrowAnnotationConfigurationException(ex);
1091                        throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
1092                }
1093        }
1094
1095        /**
1096         * Perform the search algorithm for the {@link #searchWithFindSemantics}
1097         * method, avoiding endless recursion by tracking which annotated elements
1098         * have already been <em>visited</em>.
1099         * <p>The {@code metaDepth} parameter is explained in the
1100         * {@link Processor#process process()} method of the {@link Processor} API.
1101         * @param element the annotated element (never {@code null})
1102         * @param annotationType the annotation type to find
1103         * @param annotationName the fully qualified class name of the annotation
1104         * type to find (as an alternative to {@code annotationType})
1105         * @param containerType the type of the container that holds repeatable
1106         * annotations, or {@code null} if the annotation is not repeatable
1107         * @param processor the processor to delegate to
1108         * @param visited the set of annotated elements that have already been visited
1109         * @param metaDepth the meta-depth of the annotation
1110         * @return the result of the processor (potentially {@code null})
1111         * @since 4.2
1112         */
1113        private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
1114                        String annotationName, Class<? extends Annotation> containerType, Processor<T> processor,
1115                        Set<AnnotatedElement> visited, int metaDepth) {
1116
1117                Assert.notNull(element, "AnnotatedElement must not be null");
1118
1119                if (visited.add(element)) {
1120                        try {
1121                                // Locally declared annotations (ignoring @Inherited)
1122                                Annotation[] annotations = element.getDeclaredAnnotations();
1123                                if (annotations.length > 0) {
1124                                        List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null);
1125
1126                                        // Search in local annotations
1127                                        for (Annotation annotation : annotations) {
1128                                                Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
1129                                                if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
1130                                                        if (currentAnnotationType == annotationType ||
1131                                                                        currentAnnotationType.getName().equals(annotationName) ||
1132                                                                        processor.alwaysProcesses()) {
1133                                                                T result = processor.process(element, annotation, metaDepth);
1134                                                                if (result != null) {
1135                                                                        if (aggregatedResults != null && metaDepth == 0) {
1136                                                                                aggregatedResults.add(result);
1137                                                                        }
1138                                                                        else {
1139                                                                                return result;
1140                                                                        }
1141                                                                }
1142                                                        }
1143                                                        // Repeatable annotations in container?
1144                                                        else if (currentAnnotationType == containerType) {
1145                                                                for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
1146                                                                        T result = processor.process(element, contained, metaDepth);
1147                                                                        if (aggregatedResults != null && result != null) {
1148                                                                                // No need to post-process since repeatable annotations within a
1149                                                                                // container cannot be composed annotations.
1150                                                                                aggregatedResults.add(result);
1151                                                                        }
1152                                                                }
1153                                                        }
1154                                                }
1155                                        }
1156
1157                                        // Recursively search in meta-annotations
1158                                        for (Annotation annotation : annotations) {
1159                                                Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
1160                                                if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
1161                                                        T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
1162                                                                        containerType, processor, visited, metaDepth + 1);
1163                                                        if (result != null) {
1164                                                                processor.postProcess(currentAnnotationType, annotation, result);
1165                                                                if (aggregatedResults != null && metaDepth == 0) {
1166                                                                        aggregatedResults.add(result);
1167                                                                }
1168                                                                else {
1169                                                                        return result;
1170                                                                }
1171                                                        }
1172                                                }
1173                                        }
1174
1175                                        if (!CollectionUtils.isEmpty(aggregatedResults)) {
1176                                                // Prepend to support top-down ordering within class hierarchies
1177                                                processor.getAggregatedResults().addAll(0, aggregatedResults);
1178                                        }
1179                                }
1180
1181                                if (element instanceof Method) {
1182                                        Method method = (Method) element;
1183                                        T result;
1184
1185                                        // Search on possibly bridged method
1186                                        Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
1187                                        if (resolvedMethod != method) {
1188                                                result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName,
1189                                                                containerType, processor, visited, metaDepth);
1190                                                if (result != null) {
1191                                                        return result;
1192                                                }
1193                                        }
1194
1195                                        // Search on methods in interfaces declared locally
1196                                        Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
1197                                        if (ifcs.length > 0) {
1198                                                result = searchOnInterfaces(method, annotationType, annotationName,
1199                                                                containerType, processor, visited, metaDepth, ifcs);
1200                                                if (result != null) {
1201                                                        return result;
1202                                                }
1203                                        }
1204
1205                                        // Search on methods in class hierarchy and interface hierarchy
1206                                        Class<?> clazz = method.getDeclaringClass();
1207                                        while (true) {
1208                                                clazz = clazz.getSuperclass();
1209                                                if (clazz == null || Object.class == clazz) {
1210                                                        break;
1211                                                }
1212                                                try {
1213                                                        Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
1214                                                        Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
1215                                                        result = searchWithFindSemantics(resolvedEquivalentMethod, annotationType, annotationName,
1216                                                                        containerType, processor, visited, metaDepth);
1217                                                        if (result != null) {
1218                                                                return result;
1219                                                        }
1220                                                }
1221                                                catch (NoSuchMethodException ex) {
1222                                                        // No equivalent method found
1223                                                }
1224                                                // Search on interfaces declared on superclass
1225                                                result = searchOnInterfaces(method, annotationType, annotationName,
1226                                                                containerType, processor, visited, metaDepth, clazz.getInterfaces());
1227                                                if (result != null) {
1228                                                        return result;
1229                                                }
1230                                        }
1231                                }
1232                                else if (element instanceof Class) {
1233                                        Class<?> clazz = (Class<?>) element;
1234
1235                                        // Search on interfaces
1236                                        for (Class<?> ifc : clazz.getInterfaces()) {
1237                                                T result = searchWithFindSemantics(ifc, annotationType, annotationName,
1238                                                                containerType, processor, visited, metaDepth);
1239                                                if (result != null) {
1240                                                        return result;
1241                                                }
1242                                        }
1243
1244                                        // Search on superclass
1245                                        Class<?> superclass = clazz.getSuperclass();
1246                                        if (superclass != null && Object.class != superclass) {
1247                                                T result = searchWithFindSemantics(superclass, annotationType, annotationName,
1248                                                                containerType, processor, visited, metaDepth);
1249                                                if (result != null) {
1250                                                        return result;
1251                                                }
1252                                        }
1253                                }
1254                        }
1255                        catch (Throwable ex) {
1256                                AnnotationUtils.handleIntrospectionFailure(element, ex);
1257                        }
1258                }
1259                return null;
1260        }
1261
1262        private static <T> T searchOnInterfaces(Method method, Class<? extends Annotation> annotationType,
1263                        String annotationName, Class<? extends Annotation> containerType, Processor<T> processor,
1264                        Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
1265
1266                for (Class<?> iface : ifcs) {
1267                        if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) {
1268                                try {
1269                                        Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
1270                                        T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType,
1271                                                        processor, visited, metaDepth);
1272                                        if (result != null) {
1273                                                return result;
1274                                        }
1275                                }
1276                                catch (NoSuchMethodException ex) {
1277                                        // Skip this interface - it doesn't have the method...
1278                                }
1279                        }
1280                }
1281
1282                return null;
1283        }
1284
1285        /**
1286         * Get the array of raw (unsynthesized) annotations from the {@code value}
1287         * attribute of the supplied repeatable annotation {@code container}.
1288         * @since 4.3
1289         */
1290        @SuppressWarnings("unchecked")
1291        private static <A extends Annotation> A[] getRawAnnotationsFromContainer(AnnotatedElement element,
1292                        Annotation container) {
1293
1294                try {
1295                        return (A[]) AnnotationUtils.getValue(container);
1296                }
1297                catch (Throwable ex) {
1298                        AnnotationUtils.handleIntrospectionFailure(element, ex);
1299                }
1300                // Unable to read value from repeating annotation container -> ignore it.
1301                return (A[]) EMPTY_ANNOTATION_ARRAY;
1302        }
1303
1304        /**
1305         * Resolve the container type for the supplied repeatable {@code annotationType}.
1306         * <p>Delegates to {@link AnnotationUtils#resolveContainerAnnotationType(Class)}.
1307         * @param annotationType the annotation type to resolve the container for
1308         * @return the container type (never {@code null})
1309         * @throws IllegalArgumentException if the container type cannot be resolved
1310         * @since 4.3
1311         */
1312        private static Class<? extends Annotation> resolveContainerType(Class<? extends Annotation> annotationType) {
1313                Class<? extends Annotation> containerType = AnnotationUtils.resolveContainerAnnotationType(annotationType);
1314                if (containerType == null) {
1315                        throw new IllegalArgumentException(
1316                                        "Annotation type must be a repeatable annotation: failed to resolve container type for " +
1317                                        annotationType.getName());
1318                }
1319                return containerType;
1320        }
1321
1322        /**
1323         * Validate that the supplied {@code containerType} is a proper container
1324         * annotation for the supplied repeatable {@code annotationType} (i.e.,
1325         * that it declares a {@code value} attribute that holds an array of the
1326         * {@code annotationType}).
1327         * @throws AnnotationConfigurationException if the supplied {@code containerType}
1328         * is not a valid container annotation for the supplied {@code annotationType}
1329         * @since 4.3
1330         */
1331        private static void validateContainerType(Class<? extends Annotation> annotationType,
1332                        Class<? extends Annotation> containerType) {
1333
1334                try {
1335                        Method method = containerType.getDeclaredMethod(AnnotationUtils.VALUE);
1336                        Class<?> returnType = method.getReturnType();
1337                        if (!returnType.isArray() || returnType.getComponentType() != annotationType) {
1338                                String msg = String.format(
1339                                                "Container type [%s] must declare a 'value' attribute for an array of type [%s]",
1340                                                containerType.getName(), annotationType.getName());
1341                                throw new AnnotationConfigurationException(msg);
1342                        }
1343                }
1344                catch (Throwable ex) {
1345                        AnnotationUtils.rethrowAnnotationConfigurationException(ex);
1346                        String msg = String.format("Invalid declaration of container type [%s] for repeatable annotation [%s]",
1347                                        containerType.getName(), annotationType.getName());
1348                        throw new AnnotationConfigurationException(msg, ex);
1349                }
1350        }
1351
1352        private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(AnnotatedElement element,
1353                        Class<A> annotationType, List<AnnotationAttributes> aggregatedResults) {
1354
1355                Set<A> annotations = new LinkedHashSet<A>();
1356                for (AnnotationAttributes attributes : aggregatedResults) {
1357                        AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
1358                        annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element));
1359                }
1360                return annotations;
1361        }
1362
1363
1364        /**
1365         * Callback interface that is used to process annotations during a search.
1366         * <p>Depending on the use case, a processor may choose to
1367         * {@linkplain #process} a single target annotation, multiple target
1368         * annotations, or all annotations discovered by the currently executing
1369         * search. The term "target" in this context refers to a matching
1370         * annotation (i.e., a specific annotation type that was found during
1371         * the search).
1372         * <p>Returning a non-null value from the {@link #process}
1373         * method instructs the search algorithm to stop searching further;
1374         * whereas, returning {@code null} from the {@link #process} method
1375         * instructs the search algorithm to continue searching for additional
1376         * annotations. One exception to this rule applies to processors
1377         * that {@linkplain #aggregates aggregate} results. If an aggregating
1378         * processor returns a non-null value, that value will be added to the
1379         * list of {@linkplain #getAggregatedResults aggregated results}
1380         * and the search algorithm will continue.
1381         * <p>Processors can optionally {@linkplain #postProcess post-process}
1382         * the result of the {@link #process} method as the search algorithm
1383         * goes back down the annotation hierarchy from an invocation of
1384         * {@link #process} that returned a non-null value down to the
1385         * {@link AnnotatedElement} that was supplied as the starting point to
1386         * the search algorithm.
1387         * @param <T> the type of result returned by the processor
1388         */
1389        private interface Processor<T> {
1390
1391                /**
1392                 * Process the supplied annotation.
1393                 * <p>The supplied annotation will be an actual target annotation
1394                 * that has been found by the search algorithm, unless this processor
1395                 * is configured to {@linkplain #alwaysProcesses always process}
1396                 * annotations in which case it may be some other annotation within an
1397                 * annotation hierarchy. In the latter case, the {@code metaDepth}
1398                 * will have a value greater than {@code 0}. In any case, it is
1399                 * up to concrete implementations of this method to decide what to
1400                 * do with the supplied annotation.
1401                 * <p>The {@code metaDepth} parameter represents the depth of the
1402                 * annotation relative to the first annotated element in the
1403                 * annotation hierarchy. For example, an annotation that is
1404                 * <em>present</em> on a non-annotation element will have a depth
1405                 * of 0; a meta-annotation will have a depth of 1; and a
1406                 * meta-meta-annotation will have a depth of 2; etc.
1407                 * @param annotatedElement the element that is annotated with the
1408                 * supplied annotation, used for contextual logging; may be
1409                 * {@code null} if unknown
1410                 * @param annotation the annotation to process
1411                 * @param metaDepth the meta-depth of the annotation
1412                 * @return the result of the processing, or {@code null} to continue
1413                 * searching for additional annotations
1414                 */
1415                T process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);
1416
1417                /**
1418                 * Post-process the result returned by the {@link #process} method.
1419                 * <p>The {@code annotation} supplied to this method is an annotation
1420                 * that is present in the annotation hierarchy, between the initial
1421                 * {@link AnnotatedElement} and an invocation of {@link #process}
1422                 * that returned a non-null value.
1423                 * @param annotatedElement the element that is annotated with the
1424                 * supplied annotation, used for contextual logging; may be
1425                 * {@code null} if unknown
1426                 * @param annotation the annotation to post-process
1427                 * @param result the result to post-process
1428                 */
1429                void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);
1430
1431                /**
1432                 * Determine if this processor always processes annotations regardless of
1433                 * whether or not the target annotation has been found.
1434                 * @return {@code true} if this processor always processes annotations
1435                 * @since 4.3
1436                 */
1437                boolean alwaysProcesses();
1438
1439                /**
1440                 * Determine if this processor aggregates the results returned by {@link #process}.
1441                 * <p>If this method returns {@code true}, then {@link #getAggregatedResults()}
1442                 * must return a non-null value.
1443                 * @return {@code true} if this processor supports aggregated results
1444                 * @since 4.3
1445                 * @see #getAggregatedResults
1446                 */
1447                boolean aggregates();
1448
1449                /**
1450                 * Get the list of results aggregated by this processor.
1451                 * <p>NOTE: the processor does <strong>not</strong> aggregate the results
1452                 * itself. Rather, the search algorithm that uses this processor is
1453                 * responsible for asking this processor if it {@link #aggregates} results
1454                 * and then adding the post-processed results to the list returned by this
1455                 * method.
1456                 * @return the list of results aggregated by this processor (never {@code null})
1457                 * @since 4.3
1458                 * @see #aggregates
1459                 */
1460                List<T> getAggregatedResults();
1461        }
1462
1463
1464        /**
1465         * {@link Processor} that {@linkplain #process(AnnotatedElement, Annotation, int)
1466         * processes} annotations but does not {@linkplain #postProcess post-process} or
1467         * {@linkplain #aggregates aggregate} results.
1468         * @since 4.2
1469         */
1470        private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> {
1471
1472                private final boolean alwaysProcesses;
1473
1474                public SimpleAnnotationProcessor() {
1475                        this(false);
1476                }
1477
1478                public SimpleAnnotationProcessor(boolean alwaysProcesses) {
1479                        this.alwaysProcesses = alwaysProcesses;
1480                }
1481
1482                @Override
1483                public final boolean alwaysProcesses() {
1484                        return this.alwaysProcesses;
1485                }
1486
1487                @Override
1488                public final void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result) {
1489                        // no-op
1490                }
1491
1492                @Override
1493                public final boolean aggregates() {
1494                        return false;
1495                }
1496
1497                @Override
1498                public final List<T> getAggregatedResults() {
1499                        throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results");
1500                }
1501        }
1502
1503
1504        /**
1505         * {@link SimpleAnnotationProcessor} that always returns {@link Boolean#TRUE} when
1506         * asked to {@linkplain #process(AnnotatedElement, Annotation, int) process} an
1507         * annotation.
1508         * @since 4.3
1509         */
1510        static class AlwaysTrueBooleanAnnotationProcessor extends SimpleAnnotationProcessor<Boolean> {
1511
1512                @Override
1513                public final Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
1514                        return Boolean.TRUE;
1515                }
1516        }
1517
1518
1519        /**
1520         * {@link Processor} that gets the {@code AnnotationAttributes} for the
1521         * target annotation during the {@link #process} phase and then merges
1522         * annotation attributes from lower levels in the annotation hierarchy
1523         * during the {@link #postProcess} phase.
1524         * <p>A {@code MergedAnnotationAttributesProcessor} may optionally be
1525         * configured to {@linkplain #aggregates aggregate} results.
1526         * @since 4.2
1527         * @see AnnotationUtils#retrieveAnnotationAttributes
1528         * @see AnnotationUtils#postProcessAnnotationAttributes
1529         */
1530        private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {
1531
1532                private final boolean classValuesAsString;
1533
1534                private final boolean nestedAnnotationsAsMap;
1535
1536                private final boolean aggregates;
1537
1538                private final List<AnnotationAttributes> aggregatedResults;
1539
1540                MergedAnnotationAttributesProcessor() {
1541                        this(false, false, false);
1542                }
1543
1544                MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
1545                        this(classValuesAsString, nestedAnnotationsAsMap, false);
1546                }
1547
1548                MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
1549                                boolean aggregates) {
1550
1551                        this.classValuesAsString = classValuesAsString;
1552                        this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
1553                        this.aggregates = aggregates;
1554                        this.aggregatedResults = (aggregates ? new ArrayList<AnnotationAttributes>() : null);
1555                }
1556
1557                @Override
1558                public boolean alwaysProcesses() {
1559                        return false;
1560                }
1561
1562                @Override
1563                public boolean aggregates() {
1564                        return this.aggregates;
1565                }
1566
1567                @Override
1568                public List<AnnotationAttributes> getAggregatedResults() {
1569                        return this.aggregatedResults;
1570                }
1571
1572                @Override
1573                public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
1574                        return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
1575                                        this.classValuesAsString, this.nestedAnnotationsAsMap);
1576                }
1577
1578                @Override
1579                public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
1580                        annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
1581                        Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
1582
1583                        // Track which attribute values have already been replaced so that we can short
1584                        // circuit the search algorithms.
1585                        Set<String> valuesAlreadyReplaced = new HashSet<String>();
1586
1587                        for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
1588                                String attributeName = attributeMethod.getName();
1589                                String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
1590
1591                                // Explicit annotation attribute override declared via @AliasFor
1592                                if (attributeOverrideName != null) {
1593                                        if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
1594                                                continue;
1595                                        }
1596
1597                                        List<String> targetAttributeNames = new ArrayList<String>();
1598                                        targetAttributeNames.add(attributeOverrideName);
1599                                        valuesAlreadyReplaced.add(attributeOverrideName);
1600
1601                                        // Ensure all aliased attributes in the target annotation are overridden. (SPR-14069)
1602                                        List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
1603                                        if (aliases != null) {
1604                                                for (String alias : aliases) {
1605                                                        if (!valuesAlreadyReplaced.contains(alias)) {
1606                                                                targetAttributeNames.add(alias);
1607                                                                valuesAlreadyReplaced.add(alias);
1608                                                        }
1609                                                }
1610                                        }
1611
1612                                        overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
1613                                }
1614                                // Implicit annotation attribute override based on convention
1615                                else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
1616                                        overrideAttribute(element, annotation, attributes, attributeName, attributeName);
1617                                }
1618                        }
1619                }
1620
1621                private void overrideAttributes(AnnotatedElement element, Annotation annotation,
1622                                AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) {
1623
1624                        Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName);
1625
1626                        for (String targetAttributeName : targetAttributeNames) {
1627                                attributes.put(targetAttributeName, adaptedValue);
1628                        }
1629                }
1630
1631                private void overrideAttribute(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes,
1632                                String sourceAttributeName, String targetAttributeName) {
1633
1634                        attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName));
1635                }
1636
1637                private Object getAdaptedValue(AnnotatedElement element, Annotation annotation, String sourceAttributeName) {
1638                        Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
1639                        return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
1640                }
1641        }
1642
1643}