001/*
002 * Copyright 2002-2019 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.Array;
022import java.lang.reflect.InvocationHandler;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.lang.reflect.Modifier;
026import java.lang.reflect.Proxy;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.HashSet;
030import java.util.LinkedHashMap;
031import java.util.LinkedHashSet;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038
039import org.springframework.core.BridgeMethodResolver;
040import org.springframework.util.Assert;
041import org.springframework.util.ConcurrentReferenceHashMap;
042import org.springframework.util.ObjectUtils;
043import org.springframework.util.ReflectionUtils;
044import org.springframework.util.StringUtils;
045
046/**
047 * General utility methods for working with annotations, handling meta-annotations,
048 * bridge methods (which the compiler generates for generic declarations) as well
049 * as super methods (for optional <em>annotation inheritance</em>).
050 *
051 * <p>Note that most of the features of this class are not provided by the
052 * JDK's introspection facilities themselves.
053 *
054 * <p>As a general rule for runtime-retained application annotations (e.g. for
055 * transaction control, authorization, or service exposure), always use the
056 * lookup methods on this class (e.g. {@link #findAnnotation(Method, Class)} or
057 * {@link #getAnnotation(Method, Class)}) instead of the plain annotation lookup
058 * methods in the JDK. You can still explicitly choose between a <em>get</em>
059 * lookup on the given class level only ({@link #getAnnotation(Method, Class)})
060 * and a <em>find</em> lookup in the entire inheritance hierarchy of the given
061 * method ({@link #findAnnotation(Method, Class)}).
062 *
063 * <h3>Terminology</h3>
064 * The terms <em>directly present</em>, <em>indirectly present</em>, and
065 * <em>present</em> have the same meanings as defined in the class-level
066 * javadoc for {@link AnnotatedElement} (in Java 8).
067 *
068 * <p>An annotation is <em>meta-present</em> on an element if the annotation
069 * is declared as a meta-annotation on some other annotation which is
070 * <em>present</em> on the element. Annotation {@code A} is <em>meta-present</em>
071 * on another annotation if {@code A} is either <em>directly present</em> or
072 * <em>meta-present</em> on the other annotation.
073 *
074 * <h3>Meta-annotation Support</h3>
075 * <p>Most {@code find*()} methods and some {@code get*()} methods in this class
076 * provide support for finding annotations used as meta-annotations. Consult the
077 * javadoc for each method in this class for details. For fine-grained support for
078 * meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>,
079 * consider using {@link AnnotatedElementUtils}'s more specific methods instead.
080 *
081 * <h3>Attribute Aliases</h3>
082 * <p>All public methods in this class that return annotations, arrays of
083 * annotations, or {@link AnnotationAttributes} transparently support attribute
084 * aliases configured via {@link AliasFor @AliasFor}. Consult the various
085 * {@code synthesizeAnnotation*(..)} methods for details.
086 *
087 * <h3>Search Scope</h3>
088 * <p>The search algorithms used by methods in this class stop searching for
089 * an annotation once the first annotation of the specified type has been
090 * found. As a consequence, additional annotations of the specified type will
091 * be silently ignored.
092 *
093 * @author Rob Harrop
094 * @author Juergen Hoeller
095 * @author Sam Brannen
096 * @author Mark Fisher
097 * @author Chris Beams
098 * @author Phillip Webb
099 * @since 2.0
100 * @see AliasFor
101 * @see AnnotationAttributes
102 * @see AnnotatedElementUtils
103 * @see BridgeMethodResolver
104 * @see java.lang.reflect.AnnotatedElement#getAnnotations()
105 * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class)
106 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations()
107 */
108public abstract class AnnotationUtils {
109
110        /**
111         * The attribute name for annotations with a single element.
112         */
113        public static final String VALUE = "value";
114
115        private static final String REPEATABLE_CLASS_NAME = "java.lang.annotation.Repeatable";
116
117        private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache =
118                        new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256);
119
120        private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
121                        new ConcurrentReferenceHashMap<AnnotationCacheKey, Boolean>(256);
122
123        private static final Map<Class<?>, Boolean> annotatedInterfaceCache =
124                        new ConcurrentReferenceHashMap<Class<?>, Boolean>(256);
125
126        private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache =
127                        new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256);
128
129        private static final Map<Class<? extends Annotation>, Map<String, List<String>>> attributeAliasesCache =
130                        new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, List<String>>>(256);
131
132        private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache =
133                        new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);
134
135        private static final Map<Method, AliasDescriptor> aliasDescriptorCache =
136                        new ConcurrentReferenceHashMap<Method, AliasDescriptor>(256);
137
138        private static transient Log logger;
139
140
141        /**
142         * Get a single {@link Annotation} of {@code annotationType} from the supplied
143         * annotation: either the given annotation itself or a direct meta-annotation
144         * thereof.
145         * <p>Note that this method supports only a single level of meta-annotations.
146         * For support for arbitrary levels of meta-annotations, use one of the
147         * {@code find*()} methods instead.
148         * @param annotation the Annotation to check
149         * @param annotationType the annotation type to look for, both locally and as a meta-annotation
150         * @return the first matching annotation, or {@code null} if not found
151         * @since 4.0
152         */
153        @SuppressWarnings("unchecked")
154        public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
155                if (annotationType.isInstance(annotation)) {
156                        return synthesizeAnnotation((A) annotation);
157                }
158                Class<? extends Annotation> annotatedElement = annotation.annotationType();
159                try {
160                        return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
161                }
162                catch (Throwable ex) {
163                        handleIntrospectionFailure(annotatedElement, ex);
164                        return null;
165                }
166        }
167
168        /**
169         * Get a single {@link Annotation} of {@code annotationType} from the supplied
170         * {@link AnnotatedElement}, where the annotation is either <em>present</em> or
171         * <em>meta-present</em> on the {@code AnnotatedElement}.
172         * <p>Note that this method supports only a single level of meta-annotations.
173         * For support for arbitrary levels of meta-annotations, use
174         * {@link #findAnnotation(AnnotatedElement, Class)} instead.
175         * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation
176         * @param annotationType the annotation type to look for, both locally and as a meta-annotation
177         * @return the first matching annotation, or {@code null} if not found
178         * @since 3.1
179         */
180        public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
181                try {
182                        A annotation = annotatedElement.getAnnotation(annotationType);
183                        if (annotation == null) {
184                                for (Annotation metaAnn : annotatedElement.getAnnotations()) {
185                                        annotation = metaAnn.annotationType().getAnnotation(annotationType);
186                                        if (annotation != null) {
187                                                break;
188                                        }
189                                }
190                        }
191                        return synthesizeAnnotation(annotation, annotatedElement);
192                }
193                catch (Throwable ex) {
194                        handleIntrospectionFailure(annotatedElement, ex);
195                        return null;
196                }
197        }
198
199        /**
200         * Get a single {@link Annotation} of {@code annotationType} from the
201         * supplied {@link Method}, where the annotation is either <em>present</em>
202         * or <em>meta-present</em> on the method.
203         * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
204         * <p>Note that this method supports only a single level of meta-annotations.
205         * For support for arbitrary levels of meta-annotations, use
206         * {@link #findAnnotation(Method, Class)} instead.
207         * @param method the method to look for annotations on
208         * @param annotationType the annotation type to look for
209         * @return the first matching annotation, or {@code null} if not found
210         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
211         * @see #getAnnotation(AnnotatedElement, Class)
212         */
213        public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
214                Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
215                return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
216        }
217
218        /**
219         * Get all {@link Annotation Annotations} that are <em>present</em> on the
220         * supplied {@link AnnotatedElement}.
221         * <p>Meta-annotations will <em>not</em> be searched.
222         * @param annotatedElement the Method, Constructor or Field to retrieve annotations from
223         * @return the annotations found, an empty array, or {@code null} if not
224         * resolvable (e.g. because nested Class values in annotation attributes
225         * failed to resolve at runtime)
226         * @since 4.0.8
227         * @see AnnotatedElement#getAnnotations()
228         */
229        public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) {
230                try {
231                        return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement);
232                }
233                catch (Throwable ex) {
234                        handleIntrospectionFailure(annotatedElement, ex);
235                        return null;
236                }
237        }
238
239        /**
240         * Get all {@link Annotation Annotations} that are <em>present</em> on the
241         * supplied {@link Method}.
242         * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
243         * <p>Meta-annotations will <em>not</em> be searched.
244         * @param method the Method to retrieve annotations from
245         * @return the annotations found, an empty array, or {@code null} if not
246         * resolvable (e.g. because nested Class values in annotation attributes
247         * failed to resolve at runtime)
248         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
249         * @see AnnotatedElement#getAnnotations()
250         */
251        public static Annotation[] getAnnotations(Method method) {
252                try {
253                        return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method);
254                }
255                catch (Throwable ex) {
256                        handleIntrospectionFailure(method, ex);
257                        return null;
258                }
259        }
260
261        /**
262         * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}.
263         * @since 4.0
264         * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class)
265         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)
266         * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()}
267         * or {@code getDeclaredRepeatableAnnotations()} instead.
268         */
269        @Deprecated
270        public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
271                        Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
272
273                return getRepeatableAnnotations(method, annotationType, containerAnnotationType);
274        }
275
276        /**
277         * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}.
278         * @since 4.0
279         * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class)
280         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)
281         * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()}
282         * or {@code getDeclaredRepeatableAnnotations()} instead.
283         */
284        @Deprecated
285        public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
286                        Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
287
288                return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType);
289        }
290
291        /**
292         * Get the <em>repeatable</em> {@linkplain Annotation annotations} of
293         * {@code annotationType} from the supplied {@link AnnotatedElement}, where
294         * such annotations are either <em>present</em>, <em>indirectly present</em>,
295         * or <em>meta-present</em> on the element.
296         * <p>This method mimics the functionality of Java 8's
297         * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
298         * with support for automatic detection of a <em>container annotation</em>
299         * declared via @{@link java.lang.annotation.Repeatable} (when running on
300         * Java 8 or higher) and with additional support for meta-annotations.
301         * <p>Handles both single annotations and annotations nested within a
302         * <em>container annotation</em>.
303         * <p>Correctly handles <em>bridge methods</em> generated by the
304         * compiler if the supplied element is a {@link Method}.
305         * <p>Meta-annotations will be searched if the annotation is not
306         * <em>present</em> on the supplied element.
307         * @param annotatedElement the element to look for annotations on
308         * @param annotationType the annotation type to look for
309         * @return the annotations found or an empty set (never {@code null})
310         * @since 4.2
311         * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class)
312         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)
313         * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class)
314         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
315         * @see java.lang.annotation.Repeatable
316         * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType
317         */
318        public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
319                        Class<A> annotationType) {
320
321                return getRepeatableAnnotations(annotatedElement, annotationType, null);
322        }
323
324        /**
325         * Get the <em>repeatable</em> {@linkplain Annotation annotations} of
326         * {@code annotationType} from the supplied {@link AnnotatedElement}, where
327         * such annotations are either <em>present</em>, <em>indirectly present</em>,
328         * or <em>meta-present</em> on the element.
329         * <p>This method mimics the functionality of Java 8's
330         * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
331         * with additional support for meta-annotations.
332         * <p>Handles both single annotations and annotations nested within a
333         * <em>container annotation</em>.
334         * <p>Correctly handles <em>bridge methods</em> generated by the
335         * compiler if the supplied element is a {@link Method}.
336         * <p>Meta-annotations will be searched if the annotation is not
337         * <em>present</em> on the supplied element.
338         * @param annotatedElement the element to look for annotations on
339         * @param annotationType the annotation type to look for
340         * @param containerAnnotationType the type of the container that holds
341         * the annotations; may be {@code null} if a container is not supported
342         * or if it should be looked up via @{@link java.lang.annotation.Repeatable}
343         * when running on Java 8 or higher
344         * @return the annotations found or an empty set (never {@code null})
345         * @since 4.2
346         * @see #getRepeatableAnnotations(AnnotatedElement, Class)
347         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class)
348         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)
349         * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
350         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
351         * @see java.lang.annotation.Repeatable
352         * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType
353         */
354        public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
355                        Class<A> annotationType, Class<? extends Annotation> containerAnnotationType) {
356
357                Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType);
358                if (!annotations.isEmpty()) {
359                        return annotations;
360                }
361
362                if (annotatedElement instanceof Class) {
363                        Class<?> superclass = ((Class<?>) annotatedElement).getSuperclass();
364                        if (superclass != null && Object.class != superclass) {
365                                return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType);
366                        }
367                }
368
369                return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false);
370        }
371
372        /**
373         * Get the declared <em>repeatable</em> {@linkplain Annotation annotations}
374         * of {@code annotationType} from the supplied {@link AnnotatedElement},
375         * where such annotations are either <em>directly present</em>,
376         * <em>indirectly present</em>, or <em>meta-present</em> on the element.
377         * <p>This method mimics the functionality of Java 8's
378         * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
379         * with support for automatic detection of a <em>container annotation</em>
380         * declared via @{@link java.lang.annotation.Repeatable} (when running on
381         * Java 8 or higher) and with additional support for meta-annotations.
382         * <p>Handles both single annotations and annotations nested within a
383         * <em>container annotation</em>.
384         * <p>Correctly handles <em>bridge methods</em> generated by the
385         * compiler if the supplied element is a {@link Method}.
386         * <p>Meta-annotations will be searched if the annotation is not
387         * <em>present</em> on the supplied element.
388         * @param annotatedElement the element to look for annotations on
389         * @param annotationType the annotation type to look for
390         * @return the annotations found or an empty set (never {@code null})
391         * @since 4.2
392         * @see #getRepeatableAnnotations(AnnotatedElement, Class)
393         * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class)
394         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)
395         * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class)
396         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
397         * @see java.lang.annotation.Repeatable
398         * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType
399         */
400        public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
401                        Class<A> annotationType) {
402
403                return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null);
404        }
405
406        /**
407         * Get the declared <em>repeatable</em> {@linkplain Annotation annotations}
408         * of {@code annotationType} from the supplied {@link AnnotatedElement},
409         * where such annotations are either <em>directly present</em>,
410         * <em>indirectly present</em>, or <em>meta-present</em> on the element.
411         * <p>This method mimics the functionality of Java 8's
412         * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
413         * with additional support for meta-annotations.
414         * <p>Handles both single annotations and annotations nested within a
415         * <em>container annotation</em>.
416         * <p>Correctly handles <em>bridge methods</em> generated by the
417         * compiler if the supplied element is a {@link Method}.
418         * <p>Meta-annotations will be searched if the annotation is not
419         * <em>present</em> on the supplied element.
420         * @param annotatedElement the element to look for annotations on
421         * @param annotationType the annotation type to look for
422         * @param containerAnnotationType the type of the container that holds
423         * the annotations; may be {@code null} if a container is not supported
424         * or if it should be looked up via @{@link java.lang.annotation.Repeatable}
425         * when running on Java 8 or higher
426         * @return the annotations found or an empty set (never {@code null})
427         * @since 4.2
428         * @see #getRepeatableAnnotations(AnnotatedElement, Class)
429         * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class)
430         * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class)
431         * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
432         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
433         * @see java.lang.annotation.Repeatable
434         * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType
435         */
436        public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
437                        Class<A> annotationType, Class<? extends Annotation> containerAnnotationType) {
438
439                return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true);
440        }
441
442        /**
443         * Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}
444         * and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}.
445         * <p>Correctly handles <em>bridge methods</em> generated by the
446         * compiler if the supplied element is a {@link Method}.
447         * <p>Meta-annotations will be searched if the annotation is not
448         * <em>present</em> on the supplied element.
449         * @param annotatedElement the element to look for annotations on
450         * @param annotationType the annotation type to look for
451         * @param containerAnnotationType the type of the container that holds
452         * the annotations; may be {@code null} if a container is not supported
453         * or if it should be looked up via @{@link java.lang.annotation.Repeatable}
454         * when running on Java 8 or higher
455         * @param declaredMode {@code true} if only declared annotations (i.e.,
456         * directly or indirectly present) should be considered
457         * @return the annotations found or an empty set (never {@code null})
458         * @since 4.2
459         * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod
460         * @see java.lang.annotation.Repeatable
461         */
462        private static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
463                        Class<A> annotationType, Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
464
465                Assert.notNull(annotatedElement, "AnnotatedElement must not be null");
466                Assert.notNull(annotationType, "Annotation type must not be null");
467
468                try {
469                        if (annotatedElement instanceof Method) {
470                                annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement);
471                        }
472                        return new AnnotationCollector<A>(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement);
473                }
474                catch (Throwable ex) {
475                        handleIntrospectionFailure(annotatedElement, ex);
476                        return Collections.emptySet();
477                }
478        }
479
480        /**
481         * Find a single {@link Annotation} of {@code annotationType} on the
482         * supplied {@link AnnotatedElement}.
483         * <p>Meta-annotations will be searched if the annotation is not
484         * <em>directly present</em> on the supplied element.
485         * <p><strong>Warning</strong>: this method operates generically on
486         * annotated elements. In other words, this method does not execute
487         * specialized search algorithms for classes or methods. If you require
488         * the more specific semantics of {@link #findAnnotation(Class, Class)}
489         * or {@link #findAnnotation(Method, Class)}, invoke one of those methods
490         * instead.
491         * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
492         * @param annotationType the annotation type to look for, both locally and as a meta-annotation
493         * @return the first matching annotation, or {@code null} if not found
494         * @since 4.2
495         */
496        public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
497                Assert.notNull(annotatedElement, "AnnotatedElement must not be null");
498                if (annotationType == null) {
499                        return null;
500                }
501
502                // Do NOT store result in the findAnnotationCache since doing so could break
503                // findAnnotation(Class, Class) and findAnnotation(Method, Class).
504                A ann = findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>());
505                return synthesizeAnnotation(ann, annotatedElement);
506        }
507
508        /**
509         * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)}
510         * avoiding endless recursion by tracking which annotations have already
511         * been <em>visited</em>.
512         * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
513         * @param annotationType the annotation type to look for, both locally and as a meta-annotation
514         * @param visited the set of annotations that have already been visited
515         * @return the first matching annotation, or {@code null} if not found
516         * @since 4.2
517         */
518        @SuppressWarnings("unchecked")
519        private static <A extends Annotation> A findAnnotation(
520                        AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
521                try {
522                        Annotation[] anns = annotatedElement.getDeclaredAnnotations();
523                        for (Annotation ann : anns) {
524                                if (ann.annotationType() == annotationType) {
525                                        return (A) ann;
526                                }
527                        }
528                        for (Annotation ann : anns) {
529                                if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
530                                        A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited);
531                                        if (annotation != null) {
532                                                return annotation;
533                                        }
534                                }
535                        }
536                }
537                catch (Throwable ex) {
538                        handleIntrospectionFailure(annotatedElement, ex);
539                }
540                return null;
541        }
542
543        /**
544         * Find a single {@link Annotation} of {@code annotationType} on the supplied
545         * {@link Method}, traversing its super methods (i.e. from superclasses and
546         * interfaces) if the annotation is not <em>directly present</em> on the given
547         * method itself.
548         * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
549         * <p>Meta-annotations will be searched if the annotation is not
550         * <em>directly present</em> on the method.
551         * <p>Annotations on methods are not inherited by default, so we need to handle
552         * this explicitly.
553         * @param method the method to look for annotations on
554         * @param annotationType the annotation type to look for
555         * @return the first matching annotation, or {@code null} if not found
556         * @see #getAnnotation(Method, Class)
557         */
558        @SuppressWarnings("unchecked")
559        public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
560                Assert.notNull(method, "Method must not be null");
561                if (annotationType == null) {
562                        return null;
563                }
564
565                AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
566                A result = (A) findAnnotationCache.get(cacheKey);
567
568                if (result == null) {
569                        Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
570                        result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
571                        if (result == null) {
572                                result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
573                        }
574
575                        Class<?> clazz = method.getDeclaringClass();
576                        while (result == null) {
577                                clazz = clazz.getSuperclass();
578                                if (clazz == null || Object.class == clazz) {
579                                        break;
580                                }
581                                try {
582                                        Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
583                                        Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
584                                        result = findAnnotation((AnnotatedElement) resolvedEquivalentMethod, annotationType);
585                                }
586                                catch (NoSuchMethodException ex) {
587                                        // No equivalent method found
588                                }
589                                if (result == null) {
590                                        result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
591                                }
592                        }
593
594                        if (result != null) {
595                                result = synthesizeAnnotation(result, method);
596                                findAnnotationCache.put(cacheKey, result);
597                        }
598                }
599
600                return result;
601        }
602
603        private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
604                A annotation = null;
605                for (Class<?> ifc : ifcs) {
606                        if (isInterfaceWithAnnotatedMethods(ifc)) {
607                                try {
608                                        Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes());
609                                        annotation = getAnnotation(equivalentMethod, annotationType);
610                                }
611                                catch (NoSuchMethodException ex) {
612                                        // Skip this interface - it doesn't have the method...
613                                }
614                                if (annotation != null) {
615                                        break;
616                                }
617                        }
618                }
619                return annotation;
620        }
621
622        static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) {
623                Boolean found = annotatedInterfaceCache.get(ifc);
624                if (found != null) {
625                        return found;
626                }
627                found = Boolean.FALSE;
628                for (Method ifcMethod : ifc.getMethods()) {
629                        try {
630                                if (ifcMethod.getAnnotations().length > 0) {
631                                        found = Boolean.TRUE;
632                                        break;
633                                }
634                        }
635                        catch (Throwable ex) {
636                                handleIntrospectionFailure(ifcMethod, ex);
637                        }
638                }
639                annotatedInterfaceCache.put(ifc, found);
640                return found;
641        }
642
643        /**
644         * Find a single {@link Annotation} of {@code annotationType} on the
645         * supplied {@link Class}, traversing its interfaces, annotations, and
646         * superclasses if the annotation is not <em>directly present</em> on
647         * the given class itself.
648         * <p>This method explicitly handles class-level annotations which are not
649         * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
650         * as meta-annotations and annotations on interfaces</em>.
651         * <p>The algorithm operates as follows:
652         * <ol>
653         * <li>Search for the annotation on the given class and return it if found.
654         * <li>Recursively search through all annotations that the given class declares.
655         * <li>Recursively search through all interfaces that the given class declares.
656         * <li>Recursively search through the superclass hierarchy of the given class.
657         * </ol>
658         * <p>Note: in this context, the term <em>recursively</em> means that the search
659         * process continues by returning to step #1 with the current interface,
660         * annotation, or superclass as the class to look for annotations on.
661         * @param clazz the class to look for annotations on
662         * @param annotationType the type of annotation to look for
663         * @return the first matching annotation, or {@code null} if not found
664         */
665        public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
666                return findAnnotation(clazz, annotationType, true);
667        }
668
669        /**
670         * Perform the actual work for {@link #findAnnotation(AnnotatedElement, Class)},
671         * honoring the {@code synthesize} flag.
672         * @param clazz the class to look for annotations on
673         * @param annotationType the type of annotation to look for
674         * @param synthesize {@code true} if the result should be
675         * {@linkplain #synthesizeAnnotation(Annotation) synthesized}
676         * @return the first matching annotation, or {@code null} if not found
677         * @since 4.2.1
678         */
679        @SuppressWarnings("unchecked")
680        private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, boolean synthesize) {
681                Assert.notNull(clazz, "Class must not be null");
682                if (annotationType == null) {
683                        return null;
684                }
685
686                AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);
687                A result = (A) findAnnotationCache.get(cacheKey);
688                if (result == null) {
689                        result = findAnnotation(clazz, annotationType, new HashSet<Annotation>());
690                        if (result != null && synthesize) {
691                                result = synthesizeAnnotation(result, clazz);
692                                findAnnotationCache.put(cacheKey, result);
693                        }
694                }
695                return result;
696        }
697
698        /**
699         * Perform the search algorithm for {@link #findAnnotation(Class, Class)},
700         * avoiding endless recursion by tracking which annotations have already
701         * been <em>visited</em>.
702         * @param clazz the class to look for annotations on
703         * @param annotationType the type of annotation to look for
704         * @param visited the set of annotations that have already been visited
705         * @return the first matching annotation, or {@code null} if not found
706         */
707        @SuppressWarnings("unchecked")
708        private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
709                try {
710                        Annotation[] anns = clazz.getDeclaredAnnotations();
711                        for (Annotation ann : anns) {
712                                if (ann.annotationType() == annotationType) {
713                                        return (A) ann;
714                                }
715                        }
716                        for (Annotation ann : anns) {
717                                if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
718                                        A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
719                                        if (annotation != null) {
720                                                return annotation;
721                                        }
722                                }
723                        }
724                }
725                catch (Throwable ex) {
726                        handleIntrospectionFailure(clazz, ex);
727                        return null;
728                }
729
730                for (Class<?> ifc : clazz.getInterfaces()) {
731                        A annotation = findAnnotation(ifc, annotationType, visited);
732                        if (annotation != null) {
733                                return annotation;
734                        }
735                }
736
737                Class<?> superclass = clazz.getSuperclass();
738                if (superclass == null || Object.class == superclass) {
739                        return null;
740                }
741                return findAnnotation(superclass, annotationType, visited);
742        }
743
744        /**
745         * Find the first {@link Class} in the inheritance hierarchy of the
746         * specified {@code clazz} (including the specified {@code clazz} itself)
747         * on which an annotation of the specified {@code annotationType} is
748         * <em>directly present</em>.
749         * <p>If the supplied {@code clazz} is an interface, only the interface
750         * itself will be checked; the inheritance hierarchy for interfaces will
751         * not be traversed.
752         * <p>Meta-annotations will <em>not</em> be searched.
753         * <p>The standard {@link Class} API does not provide a mechanism for
754         * determining which class in an inheritance hierarchy actually declares
755         * an {@link Annotation}, so we need to handle this explicitly.
756         * @param annotationType the annotation type to look for
757         * @param clazz the class to check for the annotation on (may be {@code null})
758         * @return the first {@link Class} in the inheritance hierarchy that
759         * declares an annotation of the specified {@code annotationType}, or
760         * {@code null} if not found
761         * @see Class#isAnnotationPresent(Class)
762         * @see Class#getDeclaredAnnotations()
763         * @see #findAnnotationDeclaringClassForTypes(List, Class)
764         * @see #isAnnotationDeclaredLocally(Class, Class)
765         */
766        public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
767                Assert.notNull(annotationType, "Annotation type must not be null");
768                if (clazz == null || Object.class == clazz) {
769                        return null;
770                }
771                if (isAnnotationDeclaredLocally(annotationType, clazz)) {
772                        return clazz;
773                }
774                return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
775        }
776
777        /**
778         * Find the first {@link Class} in the inheritance hierarchy of the
779         * specified {@code clazz} (including the specified {@code clazz} itself)
780         * on which at least one of the specified {@code annotationTypes} is
781         * <em>directly present</em>.
782         * <p>If the supplied {@code clazz} is an interface, only the interface
783         * itself will be checked; the inheritance hierarchy for interfaces will
784         * not be traversed.
785         * <p>Meta-annotations will <em>not</em> be searched.
786         * <p>The standard {@link Class} API does not provide a mechanism for
787         * determining which class in an inheritance hierarchy actually declares
788         * one of several candidate {@linkplain Annotation annotations}, so we
789         * need to handle this explicitly.
790         * @param annotationTypes the annotation types to look for
791         * @param clazz the class to check for the annotations on, or {@code null}
792         * @return the first {@link Class} in the inheritance hierarchy that
793         * declares an annotation of at least one of the specified
794         * {@code annotationTypes}, or {@code null} if not found
795         * @since 3.2.2
796         * @see Class#isAnnotationPresent(Class)
797         * @see Class#getDeclaredAnnotations()
798         * @see #findAnnotationDeclaringClass(Class, Class)
799         * @see #isAnnotationDeclaredLocally(Class, Class)
800         */
801        public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) {
802                Assert.notEmpty(annotationTypes, "List of annotation types must not be empty");
803                if (clazz == null || Object.class == clazz) {
804                        return null;
805                }
806                for (Class<? extends Annotation> annotationType : annotationTypes) {
807                        if (isAnnotationDeclaredLocally(annotationType, clazz)) {
808                                return clazz;
809                        }
810                }
811                return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
812        }
813
814        /**
815         * Determine whether an annotation of the specified {@code annotationType}
816         * is declared locally (i.e., <em>directly present</em>) on the supplied
817         * {@code clazz}.
818         * <p>The supplied {@link Class} may represent any type.
819         * <p>Meta-annotations will <em>not</em> be searched.
820         * <p>Note: This method does <strong>not</strong> determine if the annotation
821         * is {@linkplain java.lang.annotation.Inherited inherited}. For greater
822         * clarity regarding inherited annotations, consider using
823         * {@link #isAnnotationInherited(Class, Class)} instead.
824         * @param annotationType the annotation type to look for
825         * @param clazz the class to check for the annotation on
826         * @return {@code true} if an annotation of the specified {@code annotationType}
827         * is <em>directly present</em>
828         * @see java.lang.Class#getDeclaredAnnotations()
829         * @see java.lang.Class#getDeclaredAnnotation(Class)
830         * @see #isAnnotationInherited(Class, Class)
831         */
832        public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {
833                Assert.notNull(annotationType, "Annotation type must not be null");
834                Assert.notNull(clazz, "Class must not be null");
835                try {
836                        for (Annotation ann : clazz.getDeclaredAnnotations()) {
837                                if (ann.annotationType() == annotationType) {
838                                        return true;
839                                }
840                        }
841                }
842                catch (Throwable ex) {
843                        handleIntrospectionFailure(clazz, ex);
844                }
845                return false;
846        }
847
848        /**
849         * Determine whether an annotation of the specified {@code annotationType}
850         * is <em>present</em> on the supplied {@code clazz} and is
851         * {@linkplain java.lang.annotation.Inherited inherited} (i.e., not
852         * <em>directly present</em>).
853         * <p>Meta-annotations will <em>not</em> be searched.
854         * <p>If the supplied {@code clazz} is an interface, only the interface
855         * itself will be checked. In accordance with standard meta-annotation
856         * semantics in Java, the inheritance hierarchy for interfaces will not
857         * be traversed. See the {@linkplain java.lang.annotation.Inherited javadoc}
858         * for the {@code @Inherited} meta-annotation for further details regarding
859         * annotation inheritance.
860         * @param annotationType the annotation type to look for
861         * @param clazz the class to check for the annotation on
862         * @return {@code true} if an annotation of the specified {@code annotationType}
863         * is <em>present</em> and <em>inherited</em>
864         * @see Class#isAnnotationPresent(Class)
865         * @see #isAnnotationDeclaredLocally(Class, Class)
866         */
867        public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {
868                Assert.notNull(annotationType, "Annotation type must not be null");
869                Assert.notNull(clazz, "Class must not be null");
870                return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));
871        }
872
873        /**
874         * Determine if an annotation of type {@code metaAnnotationType} is
875         * <em>meta-present</em> on the supplied {@code annotationType}.
876         * @param annotationType the annotation type to search on
877         * @param metaAnnotationType the type of meta-annotation to search for
878         * @return {@code true} if such an annotation is meta-present
879         * @since 4.2.1
880         */
881        public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType,
882                        Class<? extends Annotation> metaAnnotationType) {
883
884                Assert.notNull(annotationType, "Annotation type must not be null");
885                if (metaAnnotationType == null) {
886                        return false;
887                }
888
889                AnnotationCacheKey cacheKey = new AnnotationCacheKey(annotationType, metaAnnotationType);
890                Boolean metaPresent = metaPresentCache.get(cacheKey);
891                if (metaPresent != null) {
892                        return metaPresent;
893                }
894                metaPresent = Boolean.FALSE;
895                if (findAnnotation(annotationType, metaAnnotationType, false) != null) {
896                        metaPresent = Boolean.TRUE;
897                }
898                metaPresentCache.put(cacheKey, metaPresent);
899                return metaPresent;
900        }
901
902        /**
903         * Determine if the supplied {@link Annotation} is defined in the core JDK
904         * {@code java.lang.annotation} package.
905         * @param annotation the annotation to check
906         * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
907         */
908        public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
909                return (annotation != null && isInJavaLangAnnotationPackage(annotation.annotationType()));
910        }
911
912        /**
913         * Determine if the {@link Annotation} with the supplied name is defined
914         * in the core JDK {@code java.lang.annotation} package.
915         * @param annotationType the annotation type to check
916         * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
917         * @since 4.3.8
918         */
919        static boolean isInJavaLangAnnotationPackage(Class<? extends Annotation> annotationType) {
920                return (annotationType != null && isInJavaLangAnnotationPackage(annotationType.getName()));
921        }
922
923        /**
924         * Determine if the {@link Annotation} with the supplied name is defined
925         * in the core JDK {@code java.lang.annotation} package.
926         * @param annotationType the name of the annotation type to check
927         * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
928         * @since 4.2
929         */
930        public static boolean isInJavaLangAnnotationPackage(String annotationType) {
931                return (annotationType != null && annotationType.startsWith("java.lang.annotation"));
932        }
933
934        /**
935         * Check the declared attributes of the given annotation, in particular covering
936         * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
937         * {@code Class} values (instead of early {@code Class.getAnnotations() failure}.
938         * <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
939         * won't failure either (when attempted later on).
940         * @param annotation the annotation to validate
941         * @throws IllegalStateException if a declared {@code Class} attribute could not be read
942         * @since 4.3.15
943         * @see Class#getAnnotations()
944         * @see #getAnnotationAttributes(Annotation)
945         */
946        public static void validateAnnotation(Annotation annotation) {
947                for (Method method : getAttributeMethods(annotation.annotationType())) {
948                        Class<?> returnType = method.getReturnType();
949                        if (returnType == Class.class || returnType == Class[].class) {
950                                try {
951                                        method.invoke(annotation);
952                                }
953                                catch (Throwable ex) {
954                                        throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
955                                }
956                        }
957                }
958        }
959
960        /**
961         * Retrieve the given annotation's attributes as a {@link Map}, preserving all
962         * attribute types.
963         * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)}
964         * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters
965         * set to {@code false}.
966         * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
967         * However, the {@code Map} signature has been preserved for binary compatibility.
968         * @param annotation the annotation to retrieve the attributes for
969         * @return the Map of annotation attributes, with attribute names as keys and
970         * corresponding attribute values as values (never {@code null})
971         * @see #getAnnotationAttributes(AnnotatedElement, Annotation)
972         * @see #getAnnotationAttributes(Annotation, boolean, boolean)
973         * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
974         */
975        public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
976                return getAnnotationAttributes(null, annotation);
977        }
978
979        /**
980         * Retrieve the given annotation's attributes as a {@link Map}.
981         * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)}
982         * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}.
983         * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
984         * However, the {@code Map} signature has been preserved for binary compatibility.
985         * @param annotation the annotation to retrieve the attributes for
986         * @param classValuesAsString whether to convert Class references into Strings (for
987         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
988         * or to preserve them as Class references
989         * @return the Map of annotation attributes, with attribute names as keys and
990         * corresponding attribute values as values (never {@code null})
991         * @see #getAnnotationAttributes(Annotation, boolean, boolean)
992         */
993        public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
994                return getAnnotationAttributes(annotation, classValuesAsString, false);
995        }
996
997        /**
998         * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
999         * <p>This method provides fully recursive annotation reading capabilities on par with
1000         * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
1001         * @param annotation the annotation to retrieve the attributes for
1002         * @param classValuesAsString whether to convert Class references into Strings (for
1003         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1004         * or to preserve them as Class references
1005         * @param nestedAnnotationsAsMap whether to convert nested annotations into
1006         * {@link AnnotationAttributes} maps (for compatibility with
1007         * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
1008         * {@code Annotation} instances
1009         * @return the annotation attributes (a specialized Map) with attribute names as keys
1010         * and corresponding attribute values as values (never {@code null})
1011         * @since 3.1.1
1012         */
1013        public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
1014                        boolean nestedAnnotationsAsMap) {
1015
1016                return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap);
1017        }
1018
1019        /**
1020         * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
1021         * <p>Equivalent to calling {@link #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)}
1022         * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters
1023         * set to {@code false}.
1024         * @param annotatedElement the element that is annotated with the supplied annotation;
1025         * may be {@code null} if unknown
1026         * @param annotation the annotation to retrieve the attributes for
1027         * @return the annotation attributes (a specialized Map) with attribute names as keys
1028         * and corresponding attribute values as values (never {@code null})
1029         * @since 4.2
1030         * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
1031         */
1032        public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation) {
1033                return getAnnotationAttributes(annotatedElement, annotation, false, false);
1034        }
1035
1036        /**
1037         * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
1038         * <p>This method provides fully recursive annotation reading capabilities on par with
1039         * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
1040         * @param annotatedElement the element that is annotated with the supplied annotation;
1041         * may be {@code null} if unknown
1042         * @param annotation the annotation to retrieve the attributes for
1043         * @param classValuesAsString whether to convert Class references into Strings (for
1044         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1045         * or to preserve them as Class references
1046         * @param nestedAnnotationsAsMap whether to convert nested annotations into
1047         * {@link AnnotationAttributes} maps (for compatibility with
1048         * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
1049         * {@code Annotation} instances
1050         * @return the annotation attributes (a specialized Map) with attribute names as keys
1051         * and corresponding attribute values as values (never {@code null})
1052         * @since 4.2
1053         */
1054        public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement,
1055                        Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
1056
1057                return getAnnotationAttributes(
1058                                (Object) annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap);
1059        }
1060
1061        private static AnnotationAttributes getAnnotationAttributes(Object annotatedElement,
1062                        Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
1063
1064                AnnotationAttributes attributes =
1065                                retrieveAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap);
1066                postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, nestedAnnotationsAsMap);
1067                return attributes;
1068        }
1069
1070        /**
1071         * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
1072         * <p>This method provides fully recursive annotation reading capabilities on par with
1073         * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
1074         * <p><strong>NOTE</strong>: This variant of {@code getAnnotationAttributes()} is
1075         * only intended for use within the framework. The following special rules apply:
1076         * <ol>
1077         * <li>Default values will be replaced with default value placeholders.</li>
1078         * <li>The resulting, merged annotation attributes should eventually be
1079         * {@linkplain #postProcessAnnotationAttributes post-processed} in order to
1080         * ensure that placeholders have been replaced by actual default values and
1081         * in order to enforce {@code @AliasFor} semantics.</li>
1082         * </ol>
1083         * @param annotatedElement the element that is annotated with the supplied annotation;
1084         * may be {@code null} if unknown
1085         * @param annotation the annotation to retrieve the attributes for
1086         * @param classValuesAsString whether to convert Class references into Strings (for
1087         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1088         * or to preserve them as Class references
1089         * @param nestedAnnotationsAsMap whether to convert nested annotations into
1090         * {@link AnnotationAttributes} maps (for compatibility with
1091         * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
1092         * {@code Annotation} instances
1093         * @return the annotation attributes (a specialized Map) with attribute names as keys
1094         * and corresponding attribute values as values (never {@code null})
1095         * @since 4.2
1096         * @see #postProcessAnnotationAttributes
1097         */
1098        static AnnotationAttributes retrieveAnnotationAttributes(Object annotatedElement, Annotation annotation,
1099                        boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
1100
1101                Class<? extends Annotation> annotationType = annotation.annotationType();
1102                AnnotationAttributes attributes = new AnnotationAttributes(annotationType);
1103
1104                for (Method method : getAttributeMethods(annotationType)) {
1105                        try {
1106                                Object attributeValue = method.invoke(annotation);
1107                                Object defaultValue = method.getDefaultValue();
1108                                if (defaultValue != null && ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) {
1109                                        attributeValue = new DefaultValueHolder(defaultValue);
1110                                }
1111                                attributes.put(method.getName(),
1112                                                adaptValue(annotatedElement, attributeValue, classValuesAsString, nestedAnnotationsAsMap));
1113                        }
1114                        catch (Throwable ex) {
1115                                if (ex instanceof InvocationTargetException) {
1116                                        Throwable targetException = ((InvocationTargetException) ex).getTargetException();
1117                                        rethrowAnnotationConfigurationException(targetException);
1118                                }
1119                                throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
1120                        }
1121                }
1122
1123                return attributes;
1124        }
1125
1126        /**
1127         * Adapt the given value according to the given class and nested annotation settings.
1128         * <p>Nested annotations will be
1129         * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}.
1130         * @param annotatedElement the element that is annotated, used for contextual
1131         * logging; may be {@code null} if unknown
1132         * @param value the annotation attribute value
1133         * @param classValuesAsString whether to convert Class references into Strings (for
1134         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1135         * or to preserve them as Class references
1136         * @param nestedAnnotationsAsMap whether to convert nested annotations into
1137         * {@link AnnotationAttributes} maps (for compatibility with
1138         * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
1139         * {@code Annotation} instances
1140         * @return the adapted value, or the original value if no adaptation is needed
1141         */
1142        static Object adaptValue(Object annotatedElement, Object value, boolean classValuesAsString,
1143                        boolean nestedAnnotationsAsMap) {
1144
1145                if (classValuesAsString) {
1146                        if (value instanceof Class) {
1147                                return ((Class<?>) value).getName();
1148                        }
1149                        else if (value instanceof Class[]) {
1150                                Class<?>[] clazzArray = (Class<?>[]) value;
1151                                String[] classNames = new String[clazzArray.length];
1152                                for (int i = 0; i < clazzArray.length; i++) {
1153                                        classNames[i] = clazzArray[i].getName();
1154                                }
1155                                return classNames;
1156                        }
1157                }
1158
1159                if (value instanceof Annotation) {
1160                        Annotation annotation = (Annotation) value;
1161                        if (nestedAnnotationsAsMap) {
1162                                return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, true);
1163                        }
1164                        else {
1165                                return synthesizeAnnotation(annotation, annotatedElement);
1166                        }
1167                }
1168
1169                if (value instanceof Annotation[]) {
1170                        Annotation[] annotations = (Annotation[]) value;
1171                        if (nestedAnnotationsAsMap) {
1172                                AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[annotations.length];
1173                                for (int i = 0; i < annotations.length; i++) {
1174                                        mappedAnnotations[i] =
1175                                                        getAnnotationAttributes(annotatedElement, annotations[i], classValuesAsString, true);
1176                                }
1177                                return mappedAnnotations;
1178                        }
1179                        else {
1180                                return synthesizeAnnotationArray(annotations, annotatedElement);
1181                        }
1182                }
1183
1184                // Fallback
1185                return value;
1186        }
1187
1188        /**
1189         * Register the annotation-declared default values for the given attributes,
1190         * if available.
1191         * @param attributes the annotation attributes to process
1192         * @since 4.3.2
1193         */
1194        public static void registerDefaultValues(AnnotationAttributes attributes) {
1195                // Only do defaults scanning for public annotations; we'd run into
1196                // IllegalAccessExceptions otherwise, and we don't want to mess with
1197                // accessibility in a SecurityManager environment.
1198                Class<? extends Annotation> annotationType = attributes.annotationType();
1199                if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) {
1200                        // Check declared default values of attributes in the annotation type.
1201                        for (Method annotationAttribute : getAttributeMethods(annotationType)) {
1202                                String attributeName = annotationAttribute.getName();
1203                                Object defaultValue = annotationAttribute.getDefaultValue();
1204                                if (defaultValue != null && !attributes.containsKey(attributeName)) {
1205                                        if (defaultValue instanceof Annotation) {
1206                                                defaultValue = getAnnotationAttributes((Annotation) defaultValue, false, true);
1207                                        }
1208                                        else if (defaultValue instanceof Annotation[]) {
1209                                                Annotation[] realAnnotations = (Annotation[]) defaultValue;
1210                                                AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
1211                                                for (int i = 0; i < realAnnotations.length; i++) {
1212                                                        mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], false, true);
1213                                                }
1214                                                defaultValue = mappedAnnotations;
1215                                        }
1216                                        attributes.put(attributeName, new DefaultValueHolder(defaultValue));
1217                                }
1218                        }
1219                }
1220        }
1221
1222        /**
1223         * Post-process the supplied {@link AnnotationAttributes}, preserving nested
1224         * annotations as {@code Annotation} instances.
1225         * <p>Specifically, this method enforces <em>attribute alias</em> semantics
1226         * for annotation attributes that are annotated with {@link AliasFor @AliasFor}
1227         * and replaces default value placeholders with their original default values.
1228         * @param annotatedElement the element that is annotated with an annotation or
1229         * annotation hierarchy from which the supplied attributes were created;
1230         * may be {@code null} if unknown
1231         * @param attributes the annotation attributes to post-process
1232         * @param classValuesAsString whether to convert Class references into Strings (for
1233         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1234         * or to preserve them as Class references
1235         * @since 4.3.2
1236         * @see #postProcessAnnotationAttributes(Object, AnnotationAttributes, boolean, boolean)
1237         * @see #getDefaultValue(Class, String)
1238         */
1239        public static void postProcessAnnotationAttributes(Object annotatedElement,
1240                        AnnotationAttributes attributes, boolean classValuesAsString) {
1241
1242                postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, false);
1243        }
1244
1245        /**
1246         * Post-process the supplied {@link AnnotationAttributes}.
1247         * <p>Specifically, this method enforces <em>attribute alias</em> semantics
1248         * for annotation attributes that are annotated with {@link AliasFor @AliasFor}
1249         * and replaces default value placeholders with their original default values.
1250         * @param annotatedElement the element that is annotated with an annotation or
1251         * annotation hierarchy from which the supplied attributes were created;
1252         * may be {@code null} if unknown
1253         * @param attributes the annotation attributes to post-process
1254         * @param classValuesAsString whether to convert Class references into Strings (for
1255         * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
1256         * or to preserve them as Class references
1257         * @param nestedAnnotationsAsMap whether to convert nested annotations into
1258         * {@link AnnotationAttributes} maps (for compatibility with
1259         * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
1260         * {@code Annotation} instances
1261         * @since 4.2
1262         * @see #retrieveAnnotationAttributes(Object, Annotation, boolean, boolean)
1263         * @see #getDefaultValue(Class, String)
1264         */
1265        static void postProcessAnnotationAttributes(Object annotatedElement,
1266                        AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
1267
1268                // Abort?
1269                if (attributes == null) {
1270                        return;
1271                }
1272
1273                Class<? extends Annotation> annotationType = attributes.annotationType();
1274
1275                // Track which attribute values have already been replaced so that we can short
1276                // circuit the search algorithms.
1277                Set<String> valuesAlreadyReplaced = new HashSet<String>();
1278
1279                if (!attributes.validated) {
1280                        // Validate @AliasFor configuration
1281                        Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType);
1282                        for (String attributeName : aliasMap.keySet()) {
1283                                if (valuesAlreadyReplaced.contains(attributeName)) {
1284                                        continue;
1285                                }
1286                                Object value = attributes.get(attributeName);
1287                                boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder));
1288                                for (String aliasedAttributeName : aliasMap.get(attributeName)) {
1289                                        if (valuesAlreadyReplaced.contains(aliasedAttributeName)) {
1290                                                continue;
1291                                        }
1292                                        Object aliasedValue = attributes.get(aliasedAttributeName);
1293                                        boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder));
1294                                        // Something to validate or replace with an alias?
1295                                        if (valuePresent || aliasPresent) {
1296                                                if (valuePresent && aliasPresent) {
1297                                                        // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals().
1298                                                        if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) {
1299                                                                String elementAsString =
1300                                                                                (annotatedElement != null ? annotatedElement.toString() : "unknown element");
1301                                                                throw new AnnotationConfigurationException(String.format(
1302                                                                                "In AnnotationAttributes for annotation [%s] declared on %s, " +
1303                                                                                "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " +
1304                                                                                "but only one is permitted.", annotationType.getName(), elementAsString,
1305                                                                                attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value),
1306                                                                                ObjectUtils.nullSafeToString(aliasedValue)));
1307                                                        }
1308                                                }
1309                                                else if (aliasPresent) {
1310                                                        // Replace value with aliasedValue
1311                                                        attributes.put(attributeName,
1312                                                                        adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
1313                                                        valuesAlreadyReplaced.add(attributeName);
1314                                                }
1315                                                else {
1316                                                        // Replace aliasedValue with value
1317                                                        attributes.put(aliasedAttributeName,
1318                                                                        adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
1319                                                        valuesAlreadyReplaced.add(aliasedAttributeName);
1320                                                }
1321                                        }
1322                                }
1323                        }
1324                        attributes.validated = true;
1325                }
1326
1327                // Replace any remaining placeholders with actual default values
1328                for (String attributeName : attributes.keySet()) {
1329                        if (valuesAlreadyReplaced.contains(attributeName)) {
1330                                continue;
1331                        }
1332                        Object value = attributes.get(attributeName);
1333                        if (value instanceof DefaultValueHolder) {
1334                                value = ((DefaultValueHolder) value).defaultValue;
1335                                attributes.put(attributeName,
1336                                                adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
1337                        }
1338                }
1339        }
1340
1341        /**
1342         * Retrieve the <em>value</em> of the {@code value} attribute of a
1343         * single-element Annotation, given an annotation instance.
1344         * @param annotation the annotation instance from which to retrieve the value
1345         * @return the attribute value, or {@code null} if not found unless the attribute
1346         * value cannot be retrieved due to an {@link AnnotationConfigurationException},
1347         * in which case such an exception will be rethrown
1348         * @see #getValue(Annotation, String)
1349         */
1350        public static Object getValue(Annotation annotation) {
1351                return getValue(annotation, VALUE);
1352        }
1353
1354        /**
1355         * Retrieve the <em>value</em> of a named attribute, given an annotation instance.
1356         * @param annotation the annotation instance from which to retrieve the value
1357         * @param attributeName the name of the attribute value to retrieve
1358         * @return the attribute value, or {@code null} if not found unless the attribute
1359         * value cannot be retrieved due to an {@link AnnotationConfigurationException},
1360         * in which case such an exception will be rethrown
1361         * @see #getValue(Annotation)
1362         * @see #rethrowAnnotationConfigurationException(Throwable)
1363         */
1364        public static Object getValue(Annotation annotation, String attributeName) {
1365                if (annotation == null || !StringUtils.hasText(attributeName)) {
1366                        return null;
1367                }
1368                try {
1369                        Method method = annotation.annotationType().getDeclaredMethod(attributeName);
1370                        ReflectionUtils.makeAccessible(method);
1371                        return method.invoke(annotation);
1372                }
1373                catch (NoSuchMethodException ex) {
1374                        return null;
1375                }
1376                catch (InvocationTargetException ex) {
1377                        rethrowAnnotationConfigurationException(ex.getTargetException());
1378                        throw new IllegalStateException(
1379                                        "Could not obtain value for annotation attribute '" + attributeName + "' in " + annotation, ex);
1380                }
1381                catch (Throwable ex) {
1382                        handleIntrospectionFailure(annotation.getClass(), ex);
1383                        return null;
1384                }
1385        }
1386
1387        /**
1388         * Retrieve the <em>default value</em> of the {@code value} attribute
1389         * of a single-element Annotation, given an annotation instance.
1390         * @param annotation the annotation instance from which to retrieve the default value
1391         * @return the default value, or {@code null} if not found
1392         * @see #getDefaultValue(Annotation, String)
1393         */
1394        public static Object getDefaultValue(Annotation annotation) {
1395                return getDefaultValue(annotation, VALUE);
1396        }
1397
1398        /**
1399         * Retrieve the <em>default value</em> of a named attribute, given an annotation instance.
1400         * @param annotation the annotation instance from which to retrieve the default value
1401         * @param attributeName the name of the attribute value to retrieve
1402         * @return the default value of the named attribute, or {@code null} if not found
1403         * @see #getDefaultValue(Class, String)
1404         */
1405        public static Object getDefaultValue(Annotation annotation, String attributeName) {
1406                if (annotation == null) {
1407                        return null;
1408                }
1409                return getDefaultValue(annotation.annotationType(), attributeName);
1410        }
1411
1412        /**
1413         * Retrieve the <em>default value</em> of the {@code value} attribute
1414         * of a single-element Annotation, given the {@link Class annotation type}.
1415         * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
1416         * @return the default value, or {@code null} if not found
1417         * @see #getDefaultValue(Class, String)
1418         */
1419        public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
1420                return getDefaultValue(annotationType, VALUE);
1421        }
1422
1423        /**
1424         * Retrieve the <em>default value</em> of a named attribute, given the
1425         * {@link Class annotation type}.
1426         * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
1427         * @param attributeName the name of the attribute value to retrieve.
1428         * @return the default value of the named attribute, or {@code null} if not found
1429         * @see #getDefaultValue(Annotation, String)
1430         */
1431        public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
1432                if (annotationType == null || !StringUtils.hasText(attributeName)) {
1433                        return null;
1434                }
1435                try {
1436                        return annotationType.getDeclaredMethod(attributeName).getDefaultValue();
1437                }
1438                catch (Throwable ex) {
1439                        handleIntrospectionFailure(annotationType, ex);
1440                        return null;
1441                }
1442        }
1443
1444        /**
1445         * <em>Synthesize</em> an annotation from the supplied {@code annotation}
1446         * by wrapping it in a dynamic proxy that transparently enforces
1447         * <em>attribute alias</em> semantics for annotation attributes that are
1448         * annotated with {@link AliasFor @AliasFor}.
1449         * @param annotation the annotation to synthesize
1450         * @return the synthesized annotation, if the supplied annotation is
1451         * <em>synthesizable</em>; {@code null} if the supplied annotation is
1452         * {@code null}; otherwise, the supplied annotation unmodified
1453         * @throws AnnotationConfigurationException if invalid configuration of
1454         * {@code @AliasFor} is detected
1455         * @since 4.2
1456         * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1457         */
1458        static <A extends Annotation> A synthesizeAnnotation(A annotation) {
1459                return synthesizeAnnotation(annotation, null);
1460        }
1461
1462        /**
1463         * <em>Synthesize</em> an annotation from the supplied {@code annotation}
1464         * by wrapping it in a dynamic proxy that transparently enforces
1465         * <em>attribute alias</em> semantics for annotation attributes that are
1466         * annotated with {@link AliasFor @AliasFor}.
1467         * @param annotation the annotation to synthesize
1468         * @param annotatedElement the element that is annotated with the supplied
1469         * annotation; may be {@code null} if unknown
1470         * @return the synthesized annotation if the supplied annotation is
1471         * <em>synthesizable</em>; {@code null} if the supplied annotation is
1472         * {@code null}; otherwise the supplied annotation unmodified
1473         * @throws AnnotationConfigurationException if invalid configuration of
1474         * {@code @AliasFor} is detected
1475         * @since 4.2
1476         * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
1477         * @see #synthesizeAnnotation(Class)
1478         */
1479        public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) {
1480                return synthesizeAnnotation(annotation, (Object) annotatedElement);
1481        }
1482
1483        @SuppressWarnings("unchecked")
1484        static <A extends Annotation> A synthesizeAnnotation(A annotation, Object annotatedElement) {
1485                if (annotation == null) {
1486                        return null;
1487                }
1488                if (annotation instanceof SynthesizedAnnotation) {
1489                        return annotation;
1490                }
1491
1492                Class<? extends Annotation> annotationType = annotation.annotationType();
1493                if (!isSynthesizable(annotationType)) {
1494                        return annotation;
1495                }
1496
1497                DefaultAnnotationAttributeExtractor attributeExtractor =
1498                                new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
1499                InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1500
1501                // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a
1502                // synthesizable annotation before (which needs to declare @AliasFor from the same package)
1503                Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class};
1504                return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
1505        }
1506
1507        /**
1508         * <em>Synthesize</em> an annotation from the supplied map of annotation
1509         * attributes by wrapping the map in a dynamic proxy that implements an
1510         * annotation of the specified {@code annotationType} and transparently
1511         * enforces <em>attribute alias</em> semantics for annotation attributes
1512         * that are annotated with {@link AliasFor @AliasFor}.
1513         * <p>The supplied map must contain a key-value pair for every attribute
1514         * defined in the supplied {@code annotationType} that is not aliased or
1515         * does not have a default value. Nested maps and nested arrays of maps
1516         * will be recursively synthesized into nested annotations or nested
1517         * arrays of annotations, respectively.
1518         * <p>Note that {@link AnnotationAttributes} is a specialized type of
1519         * {@link Map} that is an ideal candidate for this method's
1520         * {@code attributes} argument.
1521         * @param attributes the map of annotation attributes to synthesize
1522         * @param annotationType the type of annotation to synthesize
1523         * @param annotatedElement the element that is annotated with the annotation
1524         * corresponding to the supplied attributes; may be {@code null} if unknown
1525         * @return the synthesized annotation, or {@code null} if the supplied attributes
1526         * map is {@code null}
1527         * @throws IllegalArgumentException if a required attribute is missing or if an
1528         * attribute is not of the correct type
1529         * @throws AnnotationConfigurationException if invalid configuration of
1530         * {@code @AliasFor} is detected
1531         * @since 4.2
1532         * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1533         * @see #synthesizeAnnotation(Class)
1534         * @see #getAnnotationAttributes(AnnotatedElement, Annotation)
1535         * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
1536         */
1537        @SuppressWarnings("unchecked")
1538        public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes,
1539                        Class<A> annotationType, AnnotatedElement annotatedElement) {
1540
1541                Assert.notNull(annotationType, "'annotationType' must not be null");
1542                if (attributes == null) {
1543                        return null;
1544                }
1545
1546                MapAnnotationAttributeExtractor attributeExtractor =
1547                                new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement);
1548                InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1549                Class<?>[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ?
1550                                new Class<?>[] {annotationType, SynthesizedAnnotation.class} : new Class<?>[] {annotationType});
1551                return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler);
1552        }
1553
1554        /**
1555         * <em>Synthesize</em> an annotation from its default attributes values.
1556         * <p>This method simply delegates to
1557         * {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)},
1558         * supplying an empty map for the source attribute values and {@code null}
1559         * for the {@link AnnotatedElement}.
1560         * @param annotationType the type of annotation to synthesize
1561         * @return the synthesized annotation
1562         * @throws IllegalArgumentException if a required attribute is missing
1563         * @throws AnnotationConfigurationException if invalid configuration of
1564         * {@code @AliasFor} is detected
1565         * @since 4.2
1566         * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
1567         * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1568         */
1569        public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) {
1570                return synthesizeAnnotation(Collections.<String, Object> emptyMap(), annotationType, null);
1571        }
1572
1573        /**
1574         * <em>Synthesize</em> an array of annotations from the supplied array
1575         * of {@code annotations} by creating a new array of the same size and
1576         * type and populating it with {@linkplain #synthesizeAnnotation(Annotation)
1577         * synthesized} versions of the annotations from the input array.
1578         * @param annotations the array of annotations to synthesize
1579         * @param annotatedElement the element that is annotated with the supplied
1580         * array of annotations; may be {@code null} if unknown
1581         * @return a new array of synthesized annotations, or {@code null} if
1582         * the supplied array is {@code null}
1583         * @throws AnnotationConfigurationException if invalid configuration of
1584         * {@code @AliasFor} is detected
1585         * @since 4.2
1586         * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1587         * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
1588         */
1589        static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Object annotatedElement) {
1590                if (annotations == null) {
1591                        return null;
1592                }
1593
1594                Annotation[] synthesized = (Annotation[]) Array.newInstance(
1595                                annotations.getClass().getComponentType(), annotations.length);
1596                for (int i = 0; i < annotations.length; i++) {
1597                        synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement);
1598                }
1599                return synthesized;
1600        }
1601
1602        /**
1603         * <em>Synthesize</em> an array of annotations from the supplied array
1604         * of {@code maps} of annotation attributes by creating a new array of
1605         * {@code annotationType} with the same size and populating it with
1606         * {@linkplain #synthesizeAnnotation(Map, Class, AnnotatedElement)
1607         * synthesized} versions of the maps from the input array.
1608         * @param maps the array of maps of annotation attributes to synthesize
1609         * @param annotationType the type of annotations to synthesize
1610         * (never {@code null})
1611         * @return a new array of synthesized annotations, or {@code null} if
1612         * the supplied array is {@code null}
1613         * @throws AnnotationConfigurationException if invalid configuration of
1614         * {@code @AliasFor} is detected
1615         * @since 4.2.1
1616         * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
1617         * @see #synthesizeAnnotationArray(Annotation[], Object)
1618         */
1619        @SuppressWarnings("unchecked")
1620        static <A extends Annotation> A[] synthesizeAnnotationArray(Map<String, Object>[] maps, Class<A> annotationType) {
1621                Assert.notNull(annotationType, "'annotationType' must not be null");
1622                if (maps == null) {
1623                        return null;
1624                }
1625
1626                A[] synthesized = (A[]) Array.newInstance(annotationType, maps.length);
1627                for (int i = 0; i < maps.length; i++) {
1628                        synthesized[i] = synthesizeAnnotation(maps[i], annotationType, null);
1629                }
1630                return synthesized;
1631        }
1632
1633        /**
1634         * Get a map of all attribute aliases declared via {@code @AliasFor}
1635         * in the supplied annotation type.
1636         * <p>The map is keyed by attribute name with each value representing
1637         * a list of names of aliased attributes.
1638         * <p>For <em>explicit</em> alias pairs such as x and y (i.e., where x
1639         * is an {@code @AliasFor("y")} and y is an {@code @AliasFor("x")}, there
1640         * will be two entries in the map: {@code x -> (y)} and {@code y -> (x)}.
1641         * <p>For <em>implicit</em> aliases (i.e., attributes that are declared
1642         * as attribute overrides for the same attribute in the same meta-annotation),
1643         * there will be n entries in the map. For example, if x, y, and z are
1644         * implicit aliases, the map will contain the following entries:
1645         * {@code x -> (y, z)}, {@code y -> (x, z)}, {@code z -> (x, y)}.
1646         * <p>An empty return value implies that the annotation does not declare
1647         * any attribute aliases.
1648         * @param annotationType the annotation type to find attribute aliases in
1649         * @return a map containing attribute aliases (never {@code null})
1650         * @since 4.2
1651         */
1652        static Map<String, List<String>> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
1653                if (annotationType == null) {
1654                        return Collections.emptyMap();
1655                }
1656
1657                Map<String, List<String>> map = attributeAliasesCache.get(annotationType);
1658                if (map != null) {
1659                        return map;
1660                }
1661
1662                map = new LinkedHashMap<String, List<String>>();
1663                for (Method attribute : getAttributeMethods(annotationType)) {
1664                        List<String> aliasNames = getAttributeAliasNames(attribute);
1665                        if (!aliasNames.isEmpty()) {
1666                                map.put(attribute.getName(), aliasNames);
1667                        }
1668                }
1669
1670                attributeAliasesCache.put(annotationType, map);
1671                return map;
1672        }
1673
1674        /**
1675         * Check whether we can expose our {@link SynthesizedAnnotation} marker for the given annotation type.
1676         * @param annotationType the annotation type that we are about to create a synthesized proxy for
1677         */
1678        private static boolean canExposeSynthesizedMarker(Class<? extends Annotation> annotationType) {
1679                try {
1680                        return (Class.forName(SynthesizedAnnotation.class.getName(), false, annotationType.getClassLoader()) ==
1681                                        SynthesizedAnnotation.class);
1682                }
1683                catch (ClassNotFoundException ex) {
1684                        return false;
1685                }
1686        }
1687
1688        /**
1689         * Determine if annotations of the supplied {@code annotationType} are
1690         * <em>synthesizable</em> (i.e., in need of being wrapped in a dynamic
1691         * proxy that provides functionality above that of a standard JDK
1692         * annotation).
1693         * <p>Specifically, an annotation is <em>synthesizable</em> if it declares
1694         * any attributes that are configured as <em>aliased pairs</em> via
1695         * {@link AliasFor @AliasFor} or if any nested annotations used by the
1696         * annotation declare such <em>aliased pairs</em>.
1697         * @since 4.2
1698         * @see SynthesizedAnnotation
1699         * @see SynthesizedAnnotationInvocationHandler
1700         */
1701        @SuppressWarnings("unchecked")
1702        private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {
1703                Boolean synthesizable = synthesizableCache.get(annotationType);
1704                if (synthesizable != null) {
1705                        return synthesizable;
1706                }
1707
1708                synthesizable = Boolean.FALSE;
1709                for (Method attribute : getAttributeMethods(annotationType)) {
1710                        if (!getAttributeAliasNames(attribute).isEmpty()) {
1711                                synthesizable = Boolean.TRUE;
1712                                break;
1713                        }
1714                        Class<?> returnType = attribute.getReturnType();
1715                        if (Annotation[].class.isAssignableFrom(returnType)) {
1716                                Class<? extends Annotation> nestedAnnotationType =
1717                                                (Class<? extends Annotation>) returnType.getComponentType();
1718                                if (isSynthesizable(nestedAnnotationType)) {
1719                                        synthesizable = Boolean.TRUE;
1720                                        break;
1721                                }
1722                        }
1723                        else if (Annotation.class.isAssignableFrom(returnType)) {
1724                                Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
1725                                if (isSynthesizable(nestedAnnotationType)) {
1726                                        synthesizable = Boolean.TRUE;
1727                                        break;
1728                                }
1729                        }
1730                }
1731
1732                synthesizableCache.put(annotationType, synthesizable);
1733                return synthesizable;
1734        }
1735
1736        /**
1737         * Get the names of the aliased attributes configured via
1738         * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
1739         * @param attribute the attribute to find aliases for
1740         * @return the names of the aliased attributes (never {@code null}, though
1741         * potentially <em>empty</em>)
1742         * @throws IllegalArgumentException if the supplied attribute method is
1743         * {@code null} or not from an annotation
1744         * @throws AnnotationConfigurationException if invalid configuration of
1745         * {@code @AliasFor} is detected
1746         * @since 4.2
1747         * @see #getAttributeOverrideName(Method, Class)
1748         */
1749        static List<String> getAttributeAliasNames(Method attribute) {
1750                Assert.notNull(attribute, "attribute must not be null");
1751                AliasDescriptor descriptor = AliasDescriptor.from(attribute);
1752                return (descriptor != null ? descriptor.getAttributeAliasNames() : Collections.<String> emptyList());
1753        }
1754
1755        /**
1756         * Get the name of the overridden attribute configured via
1757         * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
1758         * @param attribute the attribute from which to retrieve the override
1759         * (never {@code null})
1760         * @param metaAnnotationType the type of meta-annotation in which the
1761         * overridden attribute is allowed to be declared
1762         * @return the name of the overridden attribute, or {@code null} if not
1763         * found or not applicable for the specified meta-annotation type
1764         * @throws IllegalArgumentException if the supplied attribute method is
1765         * {@code null} or not from an annotation, or if the supplied meta-annotation
1766         * type is {@code null} or {@link Annotation}
1767         * @throws AnnotationConfigurationException if invalid configuration of
1768         * {@code @AliasFor} is detected
1769         * @since 4.2
1770         */
1771        static String getAttributeOverrideName(Method attribute, Class<? extends Annotation> metaAnnotationType) {
1772                Assert.notNull(attribute, "attribute must not be null");
1773                Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
1774                Assert.isTrue(Annotation.class != metaAnnotationType,
1775                                "metaAnnotationType must not be [java.lang.annotation.Annotation]");
1776
1777                AliasDescriptor descriptor = AliasDescriptor.from(attribute);
1778                return (descriptor != null ? descriptor.getAttributeOverrideName(metaAnnotationType) : null);
1779        }
1780
1781        /**
1782         * Get all methods declared in the supplied {@code annotationType} that
1783         * match Java's requirements for annotation <em>attributes</em>.
1784         * <p>All methods in the returned list will be
1785         * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}.
1786         * @param annotationType the type in which to search for attribute methods
1787         * (never {@code null})
1788         * @return all annotation attribute methods in the specified annotation
1789         * type (never {@code null}, though potentially <em>empty</em>)
1790         * @since 4.2
1791         */
1792        static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
1793                List<Method> methods = attributeMethodsCache.get(annotationType);
1794                if (methods != null) {
1795                        return methods;
1796                }
1797
1798                methods = new ArrayList<Method>();
1799                for (Method method : annotationType.getDeclaredMethods()) {
1800                        if (isAttributeMethod(method)) {
1801                                ReflectionUtils.makeAccessible(method);
1802                                methods.add(method);
1803                        }
1804                }
1805
1806                attributeMethodsCache.put(annotationType, methods);
1807                return methods;
1808        }
1809
1810        /**
1811         * Get the annotation with the supplied {@code annotationName} on the
1812         * supplied {@code element}.
1813         * @param element the element to search on
1814         * @param annotationName the fully qualified class name of the annotation
1815         * type to find
1816         * @return the annotation if found; {@code null} otherwise
1817         * @since 4.2
1818         */
1819        static Annotation getAnnotation(AnnotatedElement element, String annotationName) {
1820                for (Annotation annotation : element.getAnnotations()) {
1821                        if (annotation.annotationType().getName().equals(annotationName)) {
1822                                return annotation;
1823                        }
1824                }
1825                return null;
1826        }
1827
1828        /**
1829         * Determine if the supplied {@code method} is an annotation attribute method.
1830         * @param method the method to check
1831         * @return {@code true} if the method is an attribute method
1832         * @since 4.2
1833         */
1834        static boolean isAttributeMethod(Method method) {
1835                return (method != null && method.getParameterTypes().length == 0 && method.getReturnType() != void.class);
1836        }
1837
1838        /**
1839         * Determine if the supplied method is an "annotationType" method.
1840         * @return {@code true} if the method is an "annotationType" method
1841         * @see Annotation#annotationType()
1842         * @since 4.2
1843         */
1844        static boolean isAnnotationTypeMethod(Method method) {
1845                return (method != null && method.getName().equals("annotationType") && method.getParameterTypes().length == 0);
1846        }
1847
1848        /**
1849         * Resolve the container type for the supplied repeatable {@code annotationType}.
1850         * <p>Automatically detects a <em>container annotation</em> declared via
1851         * {@link java.lang.annotation.Repeatable}. If the supplied annotation type
1852         * is not annotated with {@code @Repeatable}, this method simply returns
1853         * {@code null}.
1854         * @since 4.2
1855         */
1856        @SuppressWarnings("unchecked")
1857        static Class<? extends Annotation> resolveContainerAnnotationType(Class<? extends Annotation> annotationType) {
1858                try {
1859                        Annotation repeatable = getAnnotation(annotationType, REPEATABLE_CLASS_NAME);
1860                        if (repeatable != null) {
1861                                Object value = getValue(repeatable);
1862                                return (Class<? extends Annotation>) value;
1863                        }
1864                }
1865                catch (Exception ex) {
1866                        handleIntrospectionFailure(annotationType, ex);
1867                }
1868                return null;
1869        }
1870
1871        /**
1872         * If the supplied throwable is an {@link AnnotationConfigurationException},
1873         * it will be cast to an {@code AnnotationConfigurationException} and thrown,
1874         * allowing it to propagate to the caller.
1875         * <p>Otherwise, this method does nothing.
1876         * @param ex the throwable to inspect
1877         * @since 4.2
1878         */
1879        static void rethrowAnnotationConfigurationException(Throwable ex) {
1880                if (ex instanceof AnnotationConfigurationException) {
1881                        throw (AnnotationConfigurationException) ex;
1882                }
1883        }
1884
1885        /**
1886         * Handle the supplied annotation introspection exception.
1887         * <p>If the supplied exception is an {@link AnnotationConfigurationException},
1888         * it will simply be thrown, allowing it to propagate to the caller, and
1889         * nothing will be logged.
1890         * <p>Otherwise, this method logs an introspection failure (in particular
1891         * {@code TypeNotPresentExceptions}) before moving on, assuming nested
1892         * Class values were not resolvable within annotation attributes and
1893         * thereby effectively pretending there were no annotations on the specified
1894         * element.
1895         * @param element the element that we tried to introspect annotations on
1896         * @param ex the exception that we encountered
1897         * @see #rethrowAnnotationConfigurationException
1898         */
1899        static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
1900                rethrowAnnotationConfigurationException(ex);
1901
1902                Log loggerToUse = logger;
1903                if (loggerToUse == null) {
1904                        loggerToUse = LogFactory.getLog(AnnotationUtils.class);
1905                        logger = loggerToUse;
1906                }
1907                if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
1908                        // Meta-annotation or (default) value lookup on an annotation type
1909                        if (loggerToUse.isDebugEnabled()) {
1910                                loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex);
1911                        }
1912                }
1913                else {
1914                        // Direct annotation lookup on regular Class, Method, Field
1915                        if (loggerToUse.isInfoEnabled()) {
1916                                loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex);
1917                        }
1918                }
1919        }
1920
1921        /**
1922         * Clear the internal annotation metadata cache.
1923         * @since 4.3.15
1924         */
1925        public static void clearCache() {
1926                findAnnotationCache.clear();
1927                metaPresentCache.clear();
1928                annotatedInterfaceCache.clear();
1929                synthesizableCache.clear();
1930                attributeAliasesCache.clear();
1931                attributeMethodsCache.clear();
1932                aliasDescriptorCache.clear();
1933        }
1934
1935
1936        /**
1937         * Cache key for the AnnotatedElement cache.
1938         */
1939        private static final class AnnotationCacheKey implements Comparable<AnnotationCacheKey> {
1940
1941                private final AnnotatedElement element;
1942
1943                private final Class<? extends Annotation> annotationType;
1944
1945                public AnnotationCacheKey(AnnotatedElement element, Class<? extends Annotation> annotationType) {
1946                        this.element = element;
1947                        this.annotationType = annotationType;
1948                }
1949
1950                @Override
1951                public boolean equals(Object other) {
1952                        if (this == other) {
1953                                return true;
1954                        }
1955                        if (!(other instanceof AnnotationCacheKey)) {
1956                                return false;
1957                        }
1958                        AnnotationCacheKey otherKey = (AnnotationCacheKey) other;
1959                        return (this.element.equals(otherKey.element) && this.annotationType.equals(otherKey.annotationType));
1960                }
1961
1962                @Override
1963                public int hashCode() {
1964                        return (this.element.hashCode() * 29 + this.annotationType.hashCode());
1965                }
1966
1967                @Override
1968                public String toString() {
1969                        return "@" + this.annotationType + " on " + this.element;
1970                }
1971
1972                @Override
1973                public int compareTo(AnnotationCacheKey other) {
1974                        int result = this.element.toString().compareTo(other.element.toString());
1975                        if (result == 0) {
1976                                result = this.annotationType.getName().compareTo(other.annotationType.getName());
1977                        }
1978                        return result;
1979                }
1980        }
1981
1982
1983        private static class AnnotationCollector<A extends Annotation> {
1984
1985                private final Class<A> annotationType;
1986
1987                private final Class<? extends Annotation> containerAnnotationType;
1988
1989                private final boolean declaredMode;
1990
1991                private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>();
1992
1993                private final Set<A> result = new LinkedHashSet<A>();
1994
1995                AnnotationCollector(Class<A> annotationType, Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
1996                        this.annotationType = annotationType;
1997                        this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType :
1998                                        resolveContainerAnnotationType(annotationType));
1999                        this.declaredMode = declaredMode;
2000                }
2001
2002                Set<A> getResult(AnnotatedElement element) {
2003                        process(element);
2004                        return Collections.unmodifiableSet(this.result);
2005                }
2006
2007                @SuppressWarnings("unchecked")
2008                private void process(AnnotatedElement element) {
2009                        if (this.visited.add(element)) {
2010                                try {
2011                                        Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations());
2012                                        for (Annotation ann : annotations) {
2013                                                Class<? extends Annotation> currentAnnotationType = ann.annotationType();
2014                                                if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
2015                                                        this.result.add(synthesizeAnnotation((A) ann, element));
2016                                                }
2017                                                else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
2018                                                        this.result.addAll(getValue(element, ann));
2019                                                }
2020                                                else if (!isInJavaLangAnnotationPackage(currentAnnotationType)) {
2021                                                        process(currentAnnotationType);
2022                                                }
2023                                        }
2024                                }
2025                                catch (Throwable ex) {
2026                                        handleIntrospectionFailure(element, ex);
2027                                }
2028                        }
2029                }
2030
2031                @SuppressWarnings("unchecked")
2032                private List<A> getValue(AnnotatedElement element, Annotation annotation) {
2033                        try {
2034                                List<A> synthesizedAnnotations = new ArrayList<A>();
2035                                for (A anno : (A[]) AnnotationUtils.getValue(annotation)) {
2036                                        synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
2037                                }
2038                                return synthesizedAnnotations;
2039                        }
2040                        catch (Throwable ex) {
2041                                handleIntrospectionFailure(element, ex);
2042                        }
2043                        // Unable to read value from repeating annotation container -> ignore it.
2044                        return Collections.emptyList();
2045                }
2046        }
2047
2048
2049        /**
2050         * {@code AliasDescriptor} encapsulates the declaration of {@code @AliasFor}
2051         * on a given annotation attribute and includes support for validating
2052         * the configuration of aliases (both explicit and implicit).
2053         * @since 4.2.1
2054         * @see #from
2055         * @see #getAttributeAliasNames
2056         * @see #getAttributeOverrideName
2057         */
2058        private static class AliasDescriptor {
2059
2060                private final Method sourceAttribute;
2061
2062                private final Class<? extends Annotation> sourceAnnotationType;
2063
2064                private final String sourceAttributeName;
2065
2066                private final Method aliasedAttribute;
2067
2068                private final Class<? extends Annotation> aliasedAnnotationType;
2069
2070                private final String aliasedAttributeName;
2071
2072                private final boolean isAliasPair;
2073
2074                /**
2075                 * Create an {@code AliasDescriptor} <em>from</em> the declaration
2076                 * of {@code @AliasFor} on the supplied annotation attribute and
2077                 * validate the configuration of {@code @AliasFor}.
2078                 * @param attribute the annotation attribute that is annotated with
2079                 * {@code @AliasFor}
2080                 * @return an alias descriptor, or {@code null} if the attribute
2081                 * is not annotated with {@code @AliasFor}
2082                 * @see #validateAgainst
2083                 */
2084                public static AliasDescriptor from(Method attribute) {
2085                        AliasDescriptor descriptor = aliasDescriptorCache.get(attribute);
2086                        if (descriptor != null) {
2087                                return descriptor;
2088                        }
2089
2090                        AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
2091                        if (aliasFor == null) {
2092                                return null;
2093                        }
2094
2095                        descriptor = new AliasDescriptor(attribute, aliasFor);
2096                        descriptor.validate();
2097                        aliasDescriptorCache.put(attribute, descriptor);
2098                        return descriptor;
2099                }
2100
2101                @SuppressWarnings("unchecked")
2102                private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
2103                        Class<?> declaringClass = sourceAttribute.getDeclaringClass();
2104                        Assert.isTrue(declaringClass.isAnnotation(), "sourceAttribute must be from an annotation");
2105
2106                        this.sourceAttribute = sourceAttribute;
2107                        this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
2108                        this.sourceAttributeName = sourceAttribute.getName();
2109
2110                        this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ?
2111                                        this.sourceAnnotationType : aliasFor.annotation());
2112                        this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute);
2113                        if (this.aliasedAnnotationType == this.sourceAnnotationType &&
2114                                        this.aliasedAttributeName.equals(this.sourceAttributeName)) {
2115                                String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] points to " +
2116                                                "itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.",
2117                                                sourceAttribute.getName(), declaringClass.getName());
2118                                throw new AnnotationConfigurationException(msg);
2119                        }
2120                        try {
2121                                this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
2122                        }
2123                        catch (NoSuchMethodException ex) {
2124                                String msg = String.format(
2125                                                "Attribute '%s' in annotation [%s] is declared as an @AliasFor nonexistent attribute '%s' in annotation [%s].",
2126                                                this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
2127                                                this.aliasedAnnotationType.getName());
2128                                throw new AnnotationConfigurationException(msg, ex);
2129                        }
2130
2131                        this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType);
2132                }
2133
2134                private void validate() {
2135                        // Target annotation is not meta-present?
2136                        if (!this.isAliasPair && !isAnnotationMetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) {
2137                                String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] declares " +
2138                                                "an alias for attribute '%s' in meta-annotation [%s] which is not meta-present.",
2139                                                this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
2140                                                this.aliasedAnnotationType.getName());
2141                                throw new AnnotationConfigurationException(msg);
2142                        }
2143
2144                        if (this.isAliasPair) {
2145                                AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class);
2146                                if (mirrorAliasFor == null) {
2147                                        String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s].",
2148                                                        this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName);
2149                                        throw new AnnotationConfigurationException(msg);
2150                                }
2151
2152                                String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute);
2153                                if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
2154                                        String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
2155                                                        this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName,
2156                                                        mirrorAliasedAttributeName);
2157                                        throw new AnnotationConfigurationException(msg);
2158                                }
2159                        }
2160
2161                        Class<?> returnType = this.sourceAttribute.getReturnType();
2162                        Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType();
2163                        if (returnType != aliasedReturnType &&
2164                                        (!aliasedReturnType.isArray() || returnType != aliasedReturnType.getComponentType())) {
2165                                String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
2166                                                "and attribute '%s' in annotation [%s] must declare the same return type.",
2167                                                this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
2168                                                this.aliasedAnnotationType.getName());
2169                                throw new AnnotationConfigurationException(msg);
2170                        }
2171
2172                        if (this.isAliasPair) {
2173                                validateDefaultValueConfiguration(this.aliasedAttribute);
2174                        }
2175                }
2176
2177                private void validateDefaultValueConfiguration(Method aliasedAttribute) {
2178                        Assert.notNull(aliasedAttribute, "aliasedAttribute must not be null");
2179                        Object defaultValue = this.sourceAttribute.getDefaultValue();
2180                        Object aliasedDefaultValue = aliasedAttribute.getDefaultValue();
2181
2182                        if (defaultValue == null || aliasedDefaultValue == null) {
2183                                String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
2184                                                "and attribute '%s' in annotation [%s] must declare default values.",
2185                                                this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
2186                                                aliasedAttribute.getDeclaringClass().getName());
2187                                throw new AnnotationConfigurationException(msg);
2188                        }
2189
2190                        if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) {
2191                                String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
2192                                                "and attribute '%s' in annotation [%s] must declare the same default value.",
2193                                                this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
2194                                                aliasedAttribute.getDeclaringClass().getName());
2195                                throw new AnnotationConfigurationException(msg);
2196                        }
2197                }
2198
2199                /**
2200                 * Validate this descriptor against the supplied descriptor.
2201                 * <p>This method only validates the configuration of default values
2202                 * for the two descriptors, since other aspects of the descriptors
2203                 * are validated when they are created.
2204                 */
2205                private void validateAgainst(AliasDescriptor otherDescriptor) {
2206                        validateDefaultValueConfiguration(otherDescriptor.sourceAttribute);
2207                }
2208
2209                /**
2210                 * Determine if this descriptor represents an explicit override for
2211                 * an attribute in the supplied {@code metaAnnotationType}.
2212                 * @see #isAliasFor
2213                 */
2214                private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) {
2215                        return (this.aliasedAnnotationType == metaAnnotationType);
2216                }
2217
2218                /**
2219                 * Determine if this descriptor and the supplied descriptor both
2220                 * effectively represent aliases for the same attribute in the same
2221                 * target annotation, either explicitly or implicitly.
2222                 * <p>This method searches the attribute override hierarchy, beginning
2223                 * with this descriptor, in order to detect implicit and transitively
2224                 * implicit aliases.
2225                 * @return {@code true} if this descriptor and the supplied descriptor
2226                 * effectively alias the same annotation attribute
2227                 * @see #isOverrideFor
2228                 */
2229                private boolean isAliasFor(AliasDescriptor otherDescriptor) {
2230                        for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) {
2231                                for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) {
2232                                        if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) {
2233                                                return true;
2234                                        }
2235                                }
2236                        }
2237                        return false;
2238                }
2239
2240                public List<String> getAttributeAliasNames() {
2241                        // Explicit alias pair?
2242                        if (this.isAliasPair) {
2243                                return Collections.singletonList(this.aliasedAttributeName);
2244                        }
2245
2246                        // Else: search for implicit aliases
2247                        List<String> aliases = new ArrayList<String>();
2248                        for (AliasDescriptor otherDescriptor : getOtherDescriptors()) {
2249                                if (this.isAliasFor(otherDescriptor)) {
2250                                        this.validateAgainst(otherDescriptor);
2251                                        aliases.add(otherDescriptor.sourceAttributeName);
2252                                }
2253                        }
2254                        return aliases;
2255                }
2256
2257                private List<AliasDescriptor> getOtherDescriptors() {
2258                        List<AliasDescriptor> otherDescriptors = new ArrayList<AliasDescriptor>();
2259                        for (Method currentAttribute : getAttributeMethods(this.sourceAnnotationType)) {
2260                                if (!this.sourceAttribute.equals(currentAttribute)) {
2261                                        AliasDescriptor otherDescriptor = AliasDescriptor.from(currentAttribute);
2262                                        if (otherDescriptor != null) {
2263                                                otherDescriptors.add(otherDescriptor);
2264                                        }
2265                                }
2266                        }
2267                        return otherDescriptors;
2268                }
2269
2270                public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) {
2271                        Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
2272                        Assert.isTrue(Annotation.class != metaAnnotationType,
2273                                        "metaAnnotationType must not be [java.lang.annotation.Annotation]");
2274
2275                        // Search the attribute override hierarchy, starting with the current attribute
2276                        for (AliasDescriptor desc = this; desc != null; desc = desc.getAttributeOverrideDescriptor()) {
2277                                if (desc.isOverrideFor(metaAnnotationType)) {
2278                                        return desc.aliasedAttributeName;
2279                                }
2280                        }
2281
2282                        // Else: explicit attribute override for a different meta-annotation
2283                        return null;
2284                }
2285
2286                private AliasDescriptor getAttributeOverrideDescriptor() {
2287                        if (this.isAliasPair) {
2288                                return null;
2289                        }
2290                        return AliasDescriptor.from(this.aliasedAttribute);
2291                }
2292
2293                /**
2294                 * Get the name of the aliased attribute configured via the supplied
2295                 * {@link AliasFor @AliasFor} annotation on the supplied {@code attribute},
2296                 * or the original attribute if no aliased one specified (indicating that
2297                 * the reference goes to a same-named attribute on a meta-annotation).
2298                 * <p>This method returns the value of either the {@code attribute}
2299                 * or {@code value} attribute of {@code @AliasFor}, ensuring that only
2300                 * one of the attributes has been declared while simultaneously ensuring
2301                 * that at least one of the attributes has been declared.
2302                 * @param aliasFor the {@code @AliasFor} annotation from which to retrieve
2303                 * the aliased attribute name
2304                 * @param attribute the attribute that is annotated with {@code @AliasFor}
2305                 * @return the name of the aliased attribute (never {@code null} or empty)
2306                 * @throws AnnotationConfigurationException if invalid configuration of
2307                 * {@code @AliasFor} is detected
2308                 */
2309                private String getAliasedAttributeName(AliasFor aliasFor, Method attribute) {
2310                        String attributeName = aliasFor.attribute();
2311                        String value = aliasFor.value();
2312                        boolean attributeDeclared = StringUtils.hasText(attributeName);
2313                        boolean valueDeclared = StringUtils.hasText(value);
2314
2315                        // Ensure user did not declare both 'value' and 'attribute' in @AliasFor
2316                        if (attributeDeclared && valueDeclared) {
2317                                String msg = String.format("In @AliasFor declared on attribute '%s' in annotation [%s], attribute 'attribute' " +
2318                                                "and its alias 'value' are present with values of [%s] and [%s], but only one is permitted.",
2319                                                attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value);
2320                                throw new AnnotationConfigurationException(msg);
2321                        }
2322
2323                        // Either explicit attribute name or pointing to same-named attribute by default
2324                        attributeName = (attributeDeclared ? attributeName : value);
2325                        return (StringUtils.hasText(attributeName) ? attributeName.trim() : attribute.getName());
2326                }
2327
2328                @Override
2329                public String toString() {
2330                        return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(),
2331                                        this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName,
2332                                        this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName);
2333                }
2334        }
2335
2336
2337        private static class DefaultValueHolder {
2338
2339                final Object defaultValue;
2340
2341                public DefaultValueHolder(Object defaultValue) {
2342                        this.defaultValue = defaultValue;
2343                }
2344        }
2345
2346}