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.util.Collections;
022import java.util.Comparator;
023import java.util.LinkedHashSet;
024import java.util.Set;
025import java.util.stream.Collectors;
026
027import org.springframework.core.BridgeMethodResolver;
028import org.springframework.core.annotation.MergedAnnotation.Adapt;
029import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
030import org.springframework.lang.Nullable;
031import org.springframework.util.MultiValueMap;
032
033/**
034 * General utility methods for finding annotations, meta-annotations, and
035 * repeatable annotations on {@link AnnotatedElement AnnotatedElements}.
036 *
037 * <p>{@code AnnotatedElementUtils} defines the public API for Spring's
038 * meta-annotation programming model with support for <em>annotation attribute
039 * overrides</em>. If you do not need support for annotation attribute
040 * overrides, consider using {@link AnnotationUtils} instead.
041 *
042 * <p>Note that the features of this class are not provided by the JDK's
043 * introspection facilities themselves.
044 *
045 * <h3>Annotation Attribute Overrides</h3>
046 * <p>Support for meta-annotations with <em>attribute overrides</em> in
047 * <em>composed annotations</em> is provided by all variants of the
048 * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()},
049 * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()},
050 * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()},
051 * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()}
052 * methods.
053 *
054 * <h3>Find vs. Get Semantics</h3>
055 * <p>The search algorithms used by methods in this class follow either
056 * <em>find</em> or <em>get</em> semantics. Consult the javadocs for each
057 * individual method for details on which search algorithm is used.
058 *
059 * <p><strong>Get semantics</strong> are limited to searching for annotations
060 * that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared
061 * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared
062 * within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}.
063 *
064 * <p><strong>Find semantics</strong> are much more exhaustive, providing
065 * <em>get semantics</em> plus support for the following:
066 *
067 * <ul>
068 * <li>Searching on interfaces, if the annotated element is a class
069 * <li>Searching on superclasses, if the annotated element is a class
070 * <li>Resolving bridged methods, if the annotated element is a method
071 * <li>Searching on methods in interfaces, if the annotated element is a method
072 * <li>Searching on methods in superclasses, if the annotated element is a method
073 * </ul>
074 *
075 * <h3>Support for {@code @Inherited}</h3>
076 * <p>Methods following <em>get semantics</em> will honor the contract of Java's
077 * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally
078 * declared annotations (including custom composed annotations) will be favored over
079 * inherited annotations. In contrast, methods following <em>find semantics</em>
080 * will completely ignore the presence of {@code @Inherited} since the <em>find</em>
081 * search algorithm manually traverses type and method hierarchies and thereby
082 * implicitly supports annotation inheritance without a need for {@code @Inherited}.
083 *
084 * @author Phillip Webb
085 * @author Juergen Hoeller
086 * @author Sam Brannen
087 * @since 4.0
088 * @see AliasFor
089 * @see AnnotationAttributes
090 * @see AnnotationUtils
091 * @see BridgeMethodResolver
092 */
093public abstract class AnnotatedElementUtils {
094
095        /**
096         * Build an adapted {@link AnnotatedElement} for the given annotations,
097         * typically for use with other methods on {@link AnnotatedElementUtils}.
098         * @param annotations the annotations to expose through the {@code AnnotatedElement}
099         * @since 4.3
100         */
101        public static AnnotatedElement forAnnotations(Annotation... annotations) {
102                return new AnnotatedElementForAnnotations(annotations);
103        }
104
105        /**
106         * Get the fully qualified class names of all meta-annotation types
107         * <em>present</em> on the annotation (of the specified {@code annotationType})
108         * on the supplied {@link AnnotatedElement}.
109         * <p>This method follows <em>get semantics</em> as described in the
110         * {@linkplain AnnotatedElementUtils class-level javadoc}.
111         * @param element the annotated element
112         * @param annotationType the annotation type on which to find meta-annotations
113         * @return the names of all meta-annotations present on the annotation,
114         * or an empty set if not found
115         * @since 4.2
116         * @see #getMetaAnnotationTypes(AnnotatedElement, String)
117         * @see #hasMetaAnnotationTypes
118         */
119        public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
120                        Class<? extends Annotation> annotationType) {
121
122                return getMetaAnnotationTypes(element, element.getAnnotation(annotationType));
123        }
124
125        /**
126         * Get the fully qualified class names of all meta-annotation
127         * types <em>present</em> on the annotation (of the specified
128         * {@code annotationName}) on the supplied {@link AnnotatedElement}.
129         * <p>This method follows <em>get semantics</em> as described in the
130         * {@linkplain AnnotatedElementUtils class-level javadoc}.
131         * @param element the annotated element
132         * @param annotationName the fully qualified class name of the annotation
133         * type on which to find meta-annotations
134         * @return the names of all meta-annotations present on the annotation,
135         * or an empty set if none found
136         * @see #getMetaAnnotationTypes(AnnotatedElement, Class)
137         * @see #hasMetaAnnotationTypes
138         */
139        public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
140                for (Annotation annotation : element.getAnnotations()) {
141                        if (annotation.annotationType().getName().equals(annotationName)) {
142                                return getMetaAnnotationTypes(element, annotation);
143                        }
144                }
145                return Collections.emptySet();
146        }
147
148        private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation annotation) {
149                if (annotation == null) {
150                        return Collections.emptySet();
151                }
152                return getAnnotations(annotation.annotationType()).stream()
153                                .map(mergedAnnotation -> mergedAnnotation.getType().getName())
154                                .collect(Collectors.toCollection(LinkedHashSet::new));
155        }
156
157        /**
158         * Determine if the supplied {@link AnnotatedElement} is annotated with
159         * a <em>composed annotation</em> that is meta-annotated with an
160         * annotation of the specified {@code annotationType}.
161         * <p>This method follows <em>get semantics</em> as described in the
162         * {@linkplain AnnotatedElementUtils class-level javadoc}.
163         * @param element the annotated element
164         * @param annotationType the meta-annotation type to find
165         * @return {@code true} if a matching meta-annotation is present
166         * @since 4.2.3
167         * @see #getMetaAnnotationTypes
168         */
169        public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
170                return getAnnotations(element).stream(annotationType).anyMatch(MergedAnnotation::isMetaPresent);
171        }
172
173        /**
174         * Determine if the supplied {@link AnnotatedElement} is annotated with a
175         * <em>composed annotation</em> that is meta-annotated with an annotation
176         * of the specified {@code annotationName}.
177         * <p>This method follows <em>get semantics</em> as described in the
178         * {@linkplain AnnotatedElementUtils class-level javadoc}.
179         * @param element the annotated element
180         * @param annotationName the fully qualified class name of the
181         * meta-annotation type to find
182         * @return {@code true} if a matching meta-annotation is present
183         * @see #getMetaAnnotationTypes
184         */
185        public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
186                return getAnnotations(element).stream(annotationName).anyMatch(MergedAnnotation::isMetaPresent);
187        }
188
189        /**
190         * Determine if an annotation of the specified {@code annotationType}
191         * is <em>present</em> on the supplied {@link AnnotatedElement} or
192         * within the annotation hierarchy <em>above</em> the specified element.
193         * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
194         * will return a non-null value.
195         * <p>This method follows <em>get semantics</em> as described in the
196         * {@linkplain AnnotatedElementUtils class-level javadoc}.
197         * @param element the annotated element
198         * @param annotationType the annotation type to find
199         * @return {@code true} if a matching annotation is present
200         * @since 4.2.3
201         * @see #hasAnnotation(AnnotatedElement, Class)
202         */
203        public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) {
204                // Shortcut: directly present on the element, with no merging needed?
205                if (AnnotationFilter.PLAIN.matches(annotationType) ||
206                                AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
207                        return element.isAnnotationPresent(annotationType);
208                }
209                // Exhaustive retrieval of merged annotations...
210                return getAnnotations(element).isPresent(annotationType);
211        }
212
213        /**
214         * Determine if an annotation of the specified {@code annotationName} is
215         * <em>present</em> on the supplied {@link AnnotatedElement} or within the
216         * annotation hierarchy <em>above</em> the specified element.
217         * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes}
218         * will return a non-null value.
219         * <p>This method follows <em>get semantics</em> as described in the
220         * {@linkplain AnnotatedElementUtils class-level javadoc}.
221         * @param element the annotated element
222         * @param annotationName the fully qualified class name of the annotation type to find
223         * @return {@code true} if a matching annotation is present
224         */
225        public static boolean isAnnotated(AnnotatedElement element, String annotationName) {
226                return getAnnotations(element).isPresent(annotationName);
227        }
228
229        /**
230         * Get the first annotation of the specified {@code annotationType} within
231         * the annotation hierarchy <em>above</em> the supplied {@code element} and
232         * merge that annotation's attributes with <em>matching</em> attributes from
233         * annotations in lower levels of the annotation hierarchy.
234         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
235         * within a single annotation and within the annotation hierarchy.
236         * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}.
237         * @param element the annotated element
238         * @param annotationType the annotation type to find
239         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
240         * @since 4.2
241         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
242         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
243         * @see #getMergedAnnotation(AnnotatedElement, Class)
244         * @see #findMergedAnnotation(AnnotatedElement, Class)
245         */
246        @Nullable
247        public static AnnotationAttributes getMergedAnnotationAttributes(
248                        AnnotatedElement element, Class<? extends Annotation> annotationType) {
249
250                MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
251                                .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
252                return getAnnotationAttributes(mergedAnnotation, false, false);
253        }
254
255        /**
256         * Get the first annotation of the specified {@code annotationName} within
257         * the annotation hierarchy <em>above</em> the supplied {@code element} and
258         * merge that annotation's attributes with <em>matching</em> attributes from
259         * annotations in lower levels of the annotation hierarchy.
260         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
261         * within a single annotation and within the annotation hierarchy.
262         * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)},
263         * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}.
264         * @param element the annotated element
265         * @param annotationName the fully qualified class name of the annotation type to find
266         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
267         * @since 4.2
268         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
269         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
270         * @see #findMergedAnnotation(AnnotatedElement, Class)
271         * @see #getAllAnnotationAttributes(AnnotatedElement, String)
272         */
273        @Nullable
274        public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
275                        String annotationName) {
276
277                return getMergedAnnotationAttributes(element, annotationName, false, false);
278        }
279
280        /**
281         * Get the first annotation of the specified {@code annotationName} within
282         * the annotation hierarchy <em>above</em> the supplied {@code element} and
283         * merge that annotation's attributes with <em>matching</em> attributes from
284         * annotations in lower levels of the annotation hierarchy.
285         * <p>Attributes from lower levels in the annotation hierarchy override attributes
286         * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are
287         * fully supported, both within a single annotation and within the annotation hierarchy.
288         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by
289         * this method will stop searching the annotation hierarchy once the first annotation
290         * of the specified {@code annotationName} has been found. As a consequence,
291         * additional annotations of the specified {@code annotationName} will be ignored.
292         * <p>This method follows <em>get semantics</em> as described in the
293         * {@linkplain AnnotatedElementUtils class-level javadoc}.
294         * @param element the annotated element
295         * @param annotationName the fully qualified class name of the annotation type to find
296         * @param classValuesAsString whether to convert Class references into Strings or to
297         * preserve them as Class references
298         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances
299         * into {@code AnnotationAttributes} maps or to preserve them as Annotation instances
300         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
301         * @since 4.2
302         * @see #findMergedAnnotation(AnnotatedElement, Class)
303         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
304         * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
305         */
306        @Nullable
307        public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
308                        String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
309
310                MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
311                                .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
312                return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
313        }
314
315        /**
316         * Get the first annotation of the specified {@code annotationType} within
317         * the annotation hierarchy <em>above</em> the supplied {@code element},
318         * merge that annotation's attributes with <em>matching</em> attributes from
319         * annotations in lower levels of the annotation hierarchy, and synthesize
320         * the result back into an annotation of the specified {@code annotationType}.
321         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
322         * within a single annotation and within the annotation hierarchy.
323         * @param element the annotated element
324         * @param annotationType the annotation type to find
325         * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
326         * @since 4.2
327         * @see #findMergedAnnotation(AnnotatedElement, Class)
328         */
329        @Nullable
330        public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
331                // Shortcut: directly present on the element, with no merging needed?
332                if (AnnotationFilter.PLAIN.matches(annotationType) ||
333                                AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
334                        return element.getDeclaredAnnotation(annotationType);
335                }
336                // Exhaustive retrieval of merged annotations...
337                return getAnnotations(element)
338                                .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
339                                .synthesize(MergedAnnotation::isPresent).orElse(null);
340        }
341
342        /**
343         * Get <strong>all</strong> annotations of the specified {@code annotationType}
344         * within the annotation hierarchy <em>above</em> the supplied {@code element};
345         * and for each annotation found, merge that annotation's attributes with
346         * <em>matching</em> attributes from annotations in lower levels of the annotation
347         * hierarchy and synthesize the results back into an annotation of the specified
348         * {@code annotationType}.
349         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
350         * single annotation and within annotation hierarchies.
351         * <p>This method follows <em>get semantics</em> as described in the
352         * {@linkplain AnnotatedElementUtils class-level javadoc}.
353         * @param element the annotated element (never {@code null})
354         * @param annotationType the annotation type to find (never {@code null})
355         * @return the set of all merged, synthesized {@code Annotations} found,
356         * or an empty set if none were found
357         * @since 4.3
358         * @see #getMergedAnnotation(AnnotatedElement, Class)
359         * @see #getAllAnnotationAttributes(AnnotatedElement, String)
360         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
361         */
362        public static <A extends Annotation> Set<A> getAllMergedAnnotations(
363                        AnnotatedElement element, Class<A> annotationType) {
364
365                return getAnnotations(element).stream(annotationType)
366                                .collect(MergedAnnotationCollectors.toAnnotationSet());
367        }
368
369        /**
370         * Get <strong>all</strong> annotations of the specified {@code annotationTypes}
371         * within the annotation hierarchy <em>above</em> the supplied {@code element};
372         * and for each annotation found, merge that annotation's attributes with
373         * <em>matching</em> attributes from annotations in lower levels of the
374         * annotation hierarchy and synthesize the results back into an annotation
375         * of the corresponding {@code annotationType}.
376         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
377         * single annotation and within annotation hierarchies.
378         * <p>This method follows <em>get semantics</em> as described in the
379         * {@linkplain AnnotatedElementUtils class-level javadoc}.
380         * @param element the annotated element (never {@code null})
381         * @param annotationTypes the annotation types to find
382         * @return the set of all merged, synthesized {@code Annotations} found,
383         * or an empty set if none were found
384         * @since 5.1
385         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
386         */
387        public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element,
388                        Set<Class<? extends Annotation>> annotationTypes) {
389
390                return getAnnotations(element).stream()
391                                .filter(MergedAnnotationPredicates.typeIn(annotationTypes))
392                                .collect(MergedAnnotationCollectors.toAnnotationSet());
393        }
394
395        /**
396         * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
397         * within the annotation hierarchy <em>above</em> the supplied {@code element};
398         * and for each annotation found, merge that annotation's attributes with
399         * <em>matching</em> attributes from annotations in lower levels of the annotation
400         * hierarchy and synthesize the results back into an annotation of the specified
401         * {@code annotationType}.
402         * <p>The container type that holds the repeatable annotations will be looked up
403         * via {@link java.lang.annotation.Repeatable}.
404         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
405         * single annotation and within annotation hierarchies.
406         * <p>This method follows <em>get semantics</em> as described in the
407         * {@linkplain AnnotatedElementUtils class-level javadoc}.
408         * @param element the annotated element (never {@code null})
409         * @param annotationType the annotation type to find (never {@code null})
410         * @return the set of all merged repeatable {@code Annotations} found,
411         * or an empty set if none were found
412         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
413         * is {@code null}, or if the container type cannot be resolved
414         * @since 4.3
415         * @see #getMergedAnnotation(AnnotatedElement, Class)
416         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
417         * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
418         */
419        public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(
420                        AnnotatedElement element, Class<A> annotationType) {
421
422                return getMergedRepeatableAnnotations(element, annotationType, null);
423        }
424
425        /**
426         * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
427         * within the annotation hierarchy <em>above</em> the supplied {@code element};
428         * and for each annotation found, merge that annotation's attributes with
429         * <em>matching</em> attributes from annotations in lower levels of the annotation
430         * hierarchy and synthesize the results back into an annotation of the specified
431         * {@code annotationType}.
432         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
433         * single annotation and within annotation hierarchies.
434         * <p>This method follows <em>get semantics</em> as described in the
435         * {@linkplain AnnotatedElementUtils class-level javadoc}.
436         * @param element the annotated element (never {@code null})
437         * @param annotationType the annotation type to find (never {@code null})
438         * @param containerType the type of the container that holds the annotations;
439         * may be {@code null} if the container type should be looked up via
440         * {@link java.lang.annotation.Repeatable}
441         * @return the set of all merged repeatable {@code Annotations} found,
442         * or an empty set if none were found
443         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
444         * is {@code null}, or if the container type cannot be resolved
445         * @throws AnnotationConfigurationException if the supplied {@code containerType}
446         * is not a valid container annotation for the supplied {@code annotationType}
447         * @since 4.3
448         * @see #getMergedAnnotation(AnnotatedElement, Class)
449         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
450         */
451        public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(
452                        AnnotatedElement element, Class<A> annotationType,
453                        @Nullable Class<? extends Annotation> containerType) {
454
455                return getRepeatableAnnotations(element, containerType, annotationType)
456                                .stream(annotationType)
457                                .collect(MergedAnnotationCollectors.toAnnotationSet());
458        }
459
460        /**
461         * Get the annotation attributes of <strong>all</strong> annotations of the specified
462         * {@code annotationName} in the annotation hierarchy above the supplied
463         * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}.
464         * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
465         * this method does <em>not</em> support attribute overrides.
466         * <p>This method follows <em>get semantics</em> as described in the
467         * {@linkplain AnnotatedElementUtils class-level javadoc}.
468         * @param element the annotated element
469         * @param annotationName the fully qualified class name of the annotation type to find
470         * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
471         * attributes from all annotations found, or {@code null} if not found
472         * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
473         */
474        @Nullable
475        public static MultiValueMap<String, Object> getAllAnnotationAttributes(
476                        AnnotatedElement element, String annotationName) {
477
478                return getAllAnnotationAttributes(element, annotationName, false, false);
479        }
480
481        /**
482         * Get the annotation attributes of <strong>all</strong> annotations of
483         * the specified {@code annotationName} in the annotation hierarchy above
484         * the supplied {@link AnnotatedElement} and store the results in a
485         * {@link MultiValueMap}.
486         * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)},
487         * this method does <em>not</em> support attribute overrides.
488         * <p>This method follows <em>get semantics</em> as described in the
489         * {@linkplain AnnotatedElementUtils class-level javadoc}.
490         * @param element the annotated element
491         * @param annotationName the fully qualified class name of the annotation type to find
492         * @param classValuesAsString whether to convert Class references into Strings or to
493         * preserve them as Class references
494         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
495         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
496         * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation
497         * attributes from all annotations found, or {@code null} if not found
498         */
499        @Nullable
500        public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
501                        String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
502
503                Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
504                return getAnnotations(element).stream(annotationName)
505                                .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
506                                .map(MergedAnnotation::withNonMergedAttributes)
507                                .collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
508        }
509
510        /**
511         * Determine if an annotation of the specified {@code annotationType}
512         * is <em>available</em> on the supplied {@link AnnotatedElement} or
513         * within the annotation hierarchy <em>above</em> the specified element.
514         * <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes}
515         * will return a non-null value.
516         * <p>This method follows <em>find semantics</em> as described in the
517         * {@linkplain AnnotatedElementUtils class-level javadoc}.
518         * @param element the annotated element
519         * @param annotationType the annotation type to find
520         * @return {@code true} if a matching annotation is present
521         * @since 4.3
522         * @see #isAnnotated(AnnotatedElement, Class)
523         */
524        public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
525                // Shortcut: directly present on the element, with no merging needed?
526                if (AnnotationFilter.PLAIN.matches(annotationType) ||
527                                AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
528                        return element.isAnnotationPresent(annotationType);
529                }
530                // Exhaustive retrieval of merged annotations...
531                return findAnnotations(element).isPresent(annotationType);
532        }
533
534        /**
535         * Find the first annotation of the specified {@code annotationType} within
536         * the annotation hierarchy <em>above</em> the supplied {@code element} and
537         * merge that annotation's attributes with <em>matching</em> attributes from
538         * annotations in lower levels of the annotation hierarchy.
539         * <p>Attributes from lower levels in the annotation hierarchy override
540         * attributes of the same name from higher levels, and
541         * {@link AliasFor @AliasFor} semantics are fully supported, both
542         * within a single annotation and within the annotation hierarchy.
543         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm
544         * used by this method will stop searching the annotation hierarchy once the
545         * first annotation of the specified {@code annotationType} has been found.
546         * As a consequence, additional annotations of the specified
547         * {@code annotationType} will be ignored.
548         * <p>This method follows <em>find semantics</em> as described in the
549         * {@linkplain AnnotatedElementUtils class-level javadoc}.
550         * @param element the annotated element
551         * @param annotationType the annotation type to find
552         * @param classValuesAsString whether to convert Class references into
553         * Strings or to preserve them as Class references
554         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
555         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
556         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
557         * @since 4.2
558         * @see #findMergedAnnotation(AnnotatedElement, Class)
559         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
560         */
561        @Nullable
562        public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
563                        Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
564
565                MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
566                                .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
567                return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
568        }
569
570        /**
571         * Find the first annotation of the specified {@code annotationName} within
572         * the annotation hierarchy <em>above</em> the supplied {@code element} and
573         * merge that annotation's attributes with <em>matching</em> attributes from
574         * annotations in lower levels of the annotation hierarchy.
575         * <p>Attributes from lower levels in the annotation hierarchy override
576         * attributes of the same name from higher levels, and
577         * {@link AliasFor @AliasFor} semantics are fully supported, both
578         * within a single annotation and within the annotation hierarchy.
579         * <p>In contrast to {@link #getAllAnnotationAttributes}, the search
580         * algorithm used by this method will stop searching the annotation
581         * hierarchy once the first annotation of the specified
582         * {@code annotationName} has been found. As a consequence, additional
583         * annotations of the specified {@code annotationName} will be ignored.
584         * <p>This method follows <em>find semantics</em> as described in the
585         * {@linkplain AnnotatedElementUtils class-level javadoc}.
586         * @param element the annotated element
587         * @param annotationName the fully qualified class name of the annotation type to find
588         * @param classValuesAsString whether to convert Class references into Strings or to
589         * preserve them as Class references
590         * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
591         * {@code AnnotationAttributes} maps or to preserve them as Annotation instances
592         * @return the merged {@code AnnotationAttributes}, or {@code null} if not found
593         * @since 4.2
594         * @see #findMergedAnnotation(AnnotatedElement, Class)
595         * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
596         */
597        @Nullable
598        public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
599                        String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
600
601                MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
602                                .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
603                return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
604        }
605
606        /**
607         * Find the first annotation of the specified {@code annotationType} within
608         * the annotation hierarchy <em>above</em> the supplied {@code element},
609         * merge that annotation's attributes with <em>matching</em> attributes from
610         * annotations in lower levels of the annotation hierarchy, and synthesize
611         * the result back into an annotation of the specified {@code annotationType}.
612         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both
613         * within a single annotation and within the annotation hierarchy.
614         * <p>This method follows <em>find semantics</em> as described in the
615         * {@linkplain AnnotatedElementUtils class-level javadoc}.
616         * @param element the annotated element
617         * @param annotationType the annotation type to find
618         * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
619         * @since 4.2
620         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
621         * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
622         * @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
623         */
624        @Nullable
625        public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
626                // Shortcut: directly present on the element, with no merging needed?
627                if (AnnotationFilter.PLAIN.matches(annotationType) ||
628                                AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
629                        return element.getDeclaredAnnotation(annotationType);
630                }
631                // Exhaustive retrieval of merged annotations...
632                return findAnnotations(element)
633                                .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
634                                .synthesize(MergedAnnotation::isPresent).orElse(null);
635        }
636
637        /**
638         * Find <strong>all</strong> annotations of the specified {@code annotationType}
639         * within the annotation hierarchy <em>above</em> the supplied {@code element};
640         * and for each annotation found, merge that annotation's attributes with
641         * <em>matching</em> attributes from annotations in lower levels of the annotation
642         * hierarchy and synthesize the results back into an annotation of the specified
643         * {@code annotationType}.
644         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
645         * single annotation and within annotation hierarchies.
646         * <p>This method follows <em>find semantics</em> as described in the
647         * {@linkplain AnnotatedElementUtils class-level javadoc}.
648         * @param element the annotated element (never {@code null})
649         * @param annotationType the annotation type to find (never {@code null})
650         * @return the set of all merged, synthesized {@code Annotations} found,
651         * or an empty set if none were found
652         * @since 4.3
653         * @see #findMergedAnnotation(AnnotatedElement, Class)
654         * @see #getAllMergedAnnotations(AnnotatedElement, Class)
655         */
656        public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
657                return findAnnotations(element).stream(annotationType)
658                                .sorted(highAggregateIndexesFirst())
659                                .collect(MergedAnnotationCollectors.toAnnotationSet());
660        }
661
662        /**
663         * Find <strong>all</strong> annotations of the specified {@code annotationTypes}
664         * within the annotation hierarchy <em>above</em> the supplied {@code element};
665         * and for each annotation found, merge that annotation's attributes with
666         * <em>matching</em> attributes from annotations in lower levels of the
667         * annotation hierarchy and synthesize the results back into an annotation
668         * of the corresponding {@code annotationType}.
669         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
670         * single annotation and within annotation hierarchies.
671         * <p>This method follows <em>find semantics</em> as described in the
672         * {@linkplain AnnotatedElementUtils class-level javadoc}.
673         * @param element the annotated element (never {@code null})
674         * @param annotationTypes the annotation types to find
675         * @return the set of all merged, synthesized {@code Annotations} found,
676         * or an empty set if none were found
677         * @since 5.1
678         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
679         */
680        public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) {
681                return findAnnotations(element).stream()
682                                .filter(MergedAnnotationPredicates.typeIn(annotationTypes))
683                                .sorted(highAggregateIndexesFirst())
684                                .collect(MergedAnnotationCollectors.toAnnotationSet());
685        }
686
687        /**
688         * Find all <em>repeatable annotations</em> of the specified {@code annotationType}
689         * within the annotation hierarchy <em>above</em> the supplied {@code element};
690         * and for each annotation found, merge that annotation's attributes with
691         * <em>matching</em> attributes from annotations in lower levels of the annotation
692         * hierarchy and synthesize the results back into an annotation of the specified
693         * {@code annotationType}.
694         * <p>The container type that holds the repeatable annotations will be looked up
695         * via {@link java.lang.annotation.Repeatable}.
696         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
697         * single annotation and within annotation hierarchies.
698         * <p>This method follows <em>find semantics</em> as described in the
699         * {@linkplain AnnotatedElementUtils class-level javadoc}.
700         * @param element the annotated element (never {@code null})
701         * @param annotationType the annotation type to find (never {@code null})
702         * @return the set of all merged repeatable {@code Annotations} found,
703         * or an empty set if none were found
704         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
705         * is {@code null}, or if the container type cannot be resolved
706         * @since 4.3
707         * @see #findMergedAnnotation(AnnotatedElement, Class)
708         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
709         * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
710         */
711        public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
712                        Class<A> annotationType) {
713
714                return findMergedRepeatableAnnotations(element, annotationType, null);
715        }
716
717        /**
718         * Find all <em>repeatable annotations</em> of the specified {@code annotationType}
719         * within the annotation hierarchy <em>above</em> the supplied {@code element};
720         * and for each annotation found, merge that annotation's attributes with
721         * <em>matching</em> attributes from annotations in lower levels of the annotation
722         * hierarchy and synthesize the results back into an annotation of the specified
723         * {@code annotationType}.
724         * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
725         * single annotation and within annotation hierarchies.
726         * <p>This method follows <em>find semantics</em> as described in the
727         * {@linkplain AnnotatedElementUtils class-level javadoc}.
728         * @param element the annotated element (never {@code null})
729         * @param annotationType the annotation type to find (never {@code null})
730         * @param containerType the type of the container that holds the annotations;
731         * may be {@code null} if the container type should be looked up via
732         * {@link java.lang.annotation.Repeatable}
733         * @return the set of all merged repeatable {@code Annotations} found,
734         * or an empty set if none were found
735         * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
736         * is {@code null}, or if the container type cannot be resolved
737         * @throws AnnotationConfigurationException if the supplied {@code containerType}
738         * is not a valid container annotation for the supplied {@code annotationType}
739         * @since 4.3
740         * @see #findMergedAnnotation(AnnotatedElement, Class)
741         * @see #findAllMergedAnnotations(AnnotatedElement, Class)
742         */
743        public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element,
744                        Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) {
745
746                return findRepeatableAnnotations(element, containerType, annotationType)
747                                .stream(annotationType)
748                                .sorted(highAggregateIndexesFirst())
749                                .collect(MergedAnnotationCollectors.toAnnotationSet());
750        }
751
752        private static MergedAnnotations getAnnotations(AnnotatedElement element) {
753                return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());
754        }
755
756        private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element,
757                        @Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
758
759                RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
760                return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers);
761        }
762
763        private static MergedAnnotations findAnnotations(AnnotatedElement element) {
764                return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
765        }
766
767        private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element,
768                        @Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
769
770                RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
771                return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers);
772        }
773
774        @Nullable
775        private static MultiValueMap<String, Object> nullIfEmpty(MultiValueMap<String, Object> map) {
776                return (map.isEmpty() ? null : map);
777        }
778
779        private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
780                return Comparator.<MergedAnnotation<A>> comparingInt(
781                                MergedAnnotation::getAggregateIndex).reversed();
782        }
783
784        @Nullable
785        private static AnnotationAttributes getAnnotationAttributes(MergedAnnotation<?> annotation,
786                        boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
787
788                if (!annotation.isPresent()) {
789                        return null;
790                }
791                return annotation.asAnnotationAttributes(
792                                Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
793        }
794
795
796        /**
797         * Adapted {@link AnnotatedElement} that hold specific annotations.
798         */
799        private static class AnnotatedElementForAnnotations implements AnnotatedElement {
800
801                private final Annotation[] annotations;
802
803                AnnotatedElementForAnnotations(Annotation... annotations) {
804                        this.annotations = annotations;
805                }
806
807                @Override
808                @SuppressWarnings("unchecked")
809                @Nullable
810                public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
811                        for (Annotation annotation : this.annotations) {
812                                if (annotation.annotationType() == annotationClass) {
813                                        return (T) annotation;
814                                }
815                        }
816                        return null;
817                }
818
819                @Override
820                public Annotation[] getAnnotations() {
821                        return this.annotations.clone();
822                }
823
824                @Override
825                public Annotation[] getDeclaredAnnotations() {
826                        return this.annotations.clone();
827                }
828
829        }
830
831}