All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.core.annotation.AnnotatedElementUtils Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;

/**
 * General utility methods for finding annotations, meta-annotations, and
 * repeatable annotations on {@link AnnotatedElement AnnotatedElements}.
 *
 * 

{@code AnnotatedElementUtils} defines the public API for Spring's * meta-annotation programming model with support for annotation attribute * overrides. If you do not need support for annotation attribute * overrides, consider using {@link AnnotationUtils} instead. * *

Note that the features of this class are not provided by the JDK's * introspection facilities themselves. * *

Annotation Attribute Overrides

*

Support for meta-annotations with attribute overrides in * composed annotations is provided by all variants of the * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()}, * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()}, * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()}, * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()} * methods. * *

Find vs. Get Semantics

*

The search algorithms used by methods in this class follow either * find or get semantics. Consult the javadocs for each * individual method for details on which search algorithm is used. * *

Get semantics are limited to searching for annotations * that are either present on an {@code AnnotatedElement} (i.e. declared * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared * within the annotation hierarchy above the {@code AnnotatedElement}. * *

Find semantics are much more exhaustive, providing * get semantics plus support for the following: * *

    *
  • Searching on interfaces, if the annotated element is a class *
  • Searching on superclasses, if the annotated element is a class *
  • Resolving bridged methods, if the annotated element is a method *
  • Searching on methods in interfaces, if the annotated element is a method *
  • Searching on methods in superclasses, if the annotated element is a method *
* *

Support for {@code @Inherited}

*

Methods following get semantics will honor the contract of Java's * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally * declared annotations (including custom composed annotations) will be favored over * inherited annotations. In contrast, methods following find semantics * will completely ignore the presence of {@code @Inherited} since the find * search algorithm manually traverses type and method hierarchies and thereby * implicitly supports annotation inheritance without a need for {@code @Inherited}. * * @author Phillip Webb * @author Juergen Hoeller * @author Sam Brannen * @since 4.0 * @see AliasFor * @see AnnotationAttributes * @see AnnotationUtils * @see BridgeMethodResolver */ public abstract class AnnotatedElementUtils { /** * Build an adapted {@link AnnotatedElement} for the given annotations, * typically for use with other methods on {@link AnnotatedElementUtils}. * @param annotations the annotations to expose through the {@code AnnotatedElement} * @since 4.3 */ public static AnnotatedElement forAnnotations(Annotation... annotations) { return new AnnotatedElementForAnnotations(annotations); } /** * Get the fully qualified class names of all meta-annotation types * present on the annotation (of the specified {@code annotationType}) * on the supplied {@link AnnotatedElement}. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type on which to find meta-annotations * @return the names of all meta-annotations present on the annotation, * or {@code null} if not found * @since 4.2 * @see #getMetaAnnotationTypes(AnnotatedElement, String) * @see #hasMetaAnnotationTypes */ public static Set getMetaAnnotationTypes(AnnotatedElement element, Class annotationType) { return getMetaAnnotationTypes(element, element.getAnnotation(annotationType)); } /** * Get the fully qualified class names of all meta-annotation * types present on the annotation (of the specified * {@code annotationName}) on the supplied {@link AnnotatedElement}. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation * type on which to find meta-annotations * @return the names of all meta-annotations present on the annotation, * or an empty set if none found * @see #getMetaAnnotationTypes(AnnotatedElement, Class) * @see #hasMetaAnnotationTypes */ public static Set getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { for (Annotation annotation : element.getAnnotations()) { if (annotation.annotationType().getName().equals(annotationName)) { return getMetaAnnotationTypes(element, annotation); } } return Collections.emptySet(); } private static Set getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation annotation) { if (annotation == null) { return Collections.emptySet(); } return getAnnotations(annotation.annotationType()).stream() .map(mergedAnnotation -> mergedAnnotation.getType().getName()) .collect(Collectors.toCollection(LinkedHashSet::new)); } /** * Determine if the supplied {@link AnnotatedElement} is annotated with * a composed annotation that is meta-annotated with an * annotation of the specified {@code annotationType}. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the meta-annotation type to find * @return {@code true} if a matching meta-annotation is present * @since 4.2.3 * @see #getMetaAnnotationTypes */ public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class annotationType) { return getAnnotations(element).stream(annotationType).anyMatch(MergedAnnotation::isMetaPresent); } /** * Determine if the supplied {@link AnnotatedElement} is annotated with a * composed annotation that is meta-annotated with an annotation * of the specified {@code annotationName}. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the * meta-annotation type to find * @return {@code true} if a matching meta-annotation is present * @see #getMetaAnnotationTypes */ public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) { return getAnnotations(element).stream(annotationName).anyMatch(MergedAnnotation::isMetaPresent); } /** * Determine if an annotation of the specified {@code annotationType} * is present on the supplied {@link AnnotatedElement} or * within the annotation hierarchy above the specified element. *

If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} * will return a non-null value. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @return {@code true} if a matching annotation is present * @since 4.2.3 * @see #hasAnnotation(AnnotatedElement, Class) */ public static boolean isAnnotated(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.isAnnotationPresent(annotationType); } // Exhaustive retrieval of merged annotations... return getAnnotations(element).isPresent(annotationType); } /** * Determine if an annotation of the specified {@code annotationName} is * present on the supplied {@link AnnotatedElement} or within the * annotation hierarchy above the specified element. *

If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} * will return a non-null value. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @return {@code true} if a matching annotation is present */ public static boolean isAnnotated(AnnotatedElement element, String annotationName) { return getAnnotations(element).isPresent(annotationName); } /** * Get the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied {@code element} and * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy. *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}. * @param element the annotated element * @param annotationType the annotation type to find * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #findMergedAnnotation(AnnotatedElement, Class) */ @Nullable public static AnnotationAttributes getMergedAnnotationAttributes( AnnotatedElement element, Class annotationType) { MergedAnnotation mergedAnnotation = getAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()); return getAnnotationAttributes(mergedAnnotation, false, false); } /** * Get the first annotation of the specified {@code annotationName} within * the annotation hierarchy above the supplied {@code element} and * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy. *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getAllAnnotationAttributes(AnnotatedElement, String) */ @Nullable public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName) { return getMergedAnnotationAttributes(element, annotationName, false, false); } /** * Get the first annotation of the specified {@code annotationName} within * the annotation hierarchy above the supplied {@code element} and * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy. *

Attributes from lower levels in the annotation hierarchy override attributes * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are * fully supported, both within a single annotation and within the annotation hierarchy. *

In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by * this method will stop searching the annotation hierarchy once the first annotation * of the specified {@code annotationName} has been found. As a consequence, * additional annotations of the specified {@code annotationName} will be ignored. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @param classValuesAsString whether to convert Class references into Strings or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation instances * into {@code AnnotationAttributes} maps or to preserve them as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = getAnnotations(element) .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); } /** * Get the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied {@code element}, * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy, and synthesize * the result back into an annotation of the specified {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. * @param element the annotated element * @param annotationType the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) */ @Nullable public static A getMergedAnnotation(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.getDeclaredAnnotation(annotationType); } // Exhaustive retrieval of merged annotations... return getAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) .synthesize(MergedAnnotation::isPresent).orElse(null); } /** * Get all annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllAnnotationAttributes(AnnotatedElement, String) * @see #findAllMergedAnnotations(AnnotatedElement, Class) */ public static Set getAllMergedAnnotations( AnnotatedElement element, Class annotationType) { return getAnnotations(element).stream(annotationType) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Get all annotations of the specified {@code annotationTypes} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the * annotation hierarchy and synthesize the results back into an annotation * of the corresponding {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationTypes the annotation types to find * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 5.1 * @see #getAllMergedAnnotations(AnnotatedElement, Class) */ public static Set getAllMergedAnnotations(AnnotatedElement element, Set> annotationTypes) { return getAnnotations(element).stream() .filter(MergedAnnotationPredicates.typeIn(annotationTypes)) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Get all repeatable annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

The container type that holds the repeatable annotations will be looked up * via {@link java.lang.annotation.Repeatable}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) */ public static Set getMergedRepeatableAnnotations( AnnotatedElement element, Class annotationType) { return getMergedRepeatableAnnotations(element, annotationType, null); } /** * Get all repeatable annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @param containerType the type of the container that holds the annotations; * may be {@code null} if the container type should be looked up via * {@link java.lang.annotation.Repeatable} * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @throws AnnotationConfigurationException if the supplied {@code containerType} * is not a valid container annotation for the supplied {@code annotationType} * @since 4.3 * @see #getMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) */ public static Set getMergedRepeatableAnnotations( AnnotatedElement element, Class annotationType, @Nullable Class containerType) { return getRepeatableAnnotations(element, containerType, annotationType) .stream(annotationType) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Get the annotation attributes of all annotations of the specified * {@code annotationName} in the annotation hierarchy above the supplied * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}. *

Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * this method does not support attribute overrides. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation * attributes from all annotations found, or {@code null} if not found * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable public static MultiValueMap getAllAnnotationAttributes( AnnotatedElement element, String annotationName) { return getAllAnnotationAttributes(element, annotationName, false, false); } /** * Get the annotation attributes of all annotations of * the specified {@code annotationName} in the annotation hierarchy above * the supplied {@link AnnotatedElement} and store the results in a * {@link MultiValueMap}. *

Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, * this method does not support attribute overrides. *

This method follows get semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @param classValuesAsString whether to convert Class references into Strings or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into * {@code AnnotationAttributes} maps or to preserve them as Annotation instances * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation * attributes from all annotations found, or {@code null} if not found */ @Nullable public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap); return getAnnotations(element).stream(annotationName) .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)) .map(MergedAnnotation::withNonMergedAttributes) .collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations)); } /** * Determine if an annotation of the specified {@code annotationType} * is available on the supplied {@link AnnotatedElement} or * within the annotation hierarchy above the specified element. *

If this method returns {@code true}, then {@link #findMergedAnnotationAttributes} * will return a non-null value. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @return {@code true} if a matching annotation is present * @since 4.3 * @see #isAnnotated(AnnotatedElement, Class) */ public static boolean hasAnnotation(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.isAnnotationPresent(annotationType); } // Exhaustive retrieval of merged annotations... return findAnnotations(element).isPresent(annotationType); } /** * Find the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied {@code element} and * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy. *

Attributes from lower levels in the annotation hierarchy override * attributes of the same name from higher levels, and * {@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

In contrast to {@link #getAllAnnotationAttributes}, the search algorithm * used by this method will stop searching the annotation hierarchy once the * first annotation of the specified {@code annotationType} has been found. * As a consequence, additional annotations of the specified * {@code annotationType} will be ignored. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @param classValuesAsString whether to convert Class references into * Strings or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into * {@code AnnotationAttributes} maps or to preserve them as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = findAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()); return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); } /** * Find the first annotation of the specified {@code annotationName} within * the annotation hierarchy above the supplied {@code element} and * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy. *

Attributes from lower levels in the annotation hierarchy override * attributes of the same name from higher levels, and * {@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

In contrast to {@link #getAllAnnotationAttributes}, the search * algorithm used by this method will stop searching the annotation * hierarchy once the first annotation of the specified * {@code annotationName} has been found. As a consequence, additional * annotations of the specified {@code annotationName} will be ignored. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationName the fully qualified class name of the annotation type to find * @param classValuesAsString whether to convert Class references into Strings or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into * {@code AnnotationAttributes} maps or to preserve them as Annotation instances * @return the merged {@code AnnotationAttributes}, or {@code null} if not found * @since 4.2 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) */ @Nullable public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { MergedAnnotation mergedAnnotation = findAnnotations(element) .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); } /** * Find the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied {@code element}, * merge that annotation's attributes with matching attributes from * annotations in lower levels of the annotation hierarchy, and synthesize * the result back into an annotation of the specified {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both * within a single annotation and within the annotation hierarchy. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element * @param annotationType the annotation type to find * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @since 4.2 * @see #findAllMergedAnnotations(AnnotatedElement, Class) * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) */ @Nullable public static A findMergedAnnotation(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.getDeclaredAnnotation(annotationType); } // Exhaustive retrieval of merged annotations... return findAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) .synthesize(MergedAnnotation::isPresent).orElse(null); } /** * Find all annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #getAllMergedAnnotations(AnnotatedElement, Class) */ public static Set findAllMergedAnnotations(AnnotatedElement element, Class annotationType) { return findAnnotations(element).stream(annotationType) .sorted(highAggregateIndexesFirst()) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Find all annotations of the specified {@code annotationTypes} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the * annotation hierarchy and synthesize the results back into an annotation * of the corresponding {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationTypes the annotation types to find * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found * @since 5.1 * @see #findAllMergedAnnotations(AnnotatedElement, Class) */ public static Set findAllMergedAnnotations(AnnotatedElement element, Set> annotationTypes) { return findAnnotations(element).stream() .filter(MergedAnnotationPredicates.typeIn(annotationTypes)) .sorted(highAggregateIndexesFirst()) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Find all repeatable annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

The container type that holds the repeatable annotations will be looked up * via {@link java.lang.annotation.Repeatable}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findAllMergedAnnotations(AnnotatedElement, Class) * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class) */ public static Set findMergedRepeatableAnnotations(AnnotatedElement element, Class annotationType) { return findMergedRepeatableAnnotations(element, annotationType, null); } /** * Find all repeatable annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied {@code element}; * and for each annotation found, merge that annotation's attributes with * matching attributes from annotations in lower levels of the annotation * hierarchy and synthesize the results back into an annotation of the specified * {@code annotationType}. *

{@link AliasFor @AliasFor} semantics are fully supported, both within a * single annotation and within annotation hierarchies. *

This method follows find semantics as described in the * {@linkplain AnnotatedElementUtils class-level javadoc}. * @param element the annotated element (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @param containerType the type of the container that holds the annotations; * may be {@code null} if the container type should be looked up via * {@link java.lang.annotation.Repeatable} * @return the set of all merged repeatable {@code Annotations} found, * or an empty set if none were found * @throws IllegalArgumentException if the {@code element} or {@code annotationType} * is {@code null}, or if the container type cannot be resolved * @throws AnnotationConfigurationException if the supplied {@code containerType} * is not a valid container annotation for the supplied {@code annotationType} * @since 4.3 * @see #findMergedAnnotation(AnnotatedElement, Class) * @see #findAllMergedAnnotations(AnnotatedElement, Class) */ public static Set findMergedRepeatableAnnotations(AnnotatedElement element, Class annotationType, @Nullable Class containerType) { return findRepeatableAnnotations(element, containerType, annotationType) .stream(annotationType) .sorted(highAggregateIndexesFirst()) .collect(MergedAnnotationCollectors.toAnnotationSet()); } private static MergedAnnotations getAnnotations(AnnotatedElement element) { return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()); } private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element, @Nullable Class containerType, Class annotationType) { RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers); } private static MergedAnnotations findAnnotations(AnnotatedElement element) { return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()); } private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element, @Nullable Class containerType, Class annotationType) { RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers); } @Nullable private static MultiValueMap nullIfEmpty(MultiValueMap map) { return (map.isEmpty() ? null : map); } private static Comparator> highAggregateIndexesFirst() { return Comparator.> comparingInt( MergedAnnotation::getAggregateIndex).reversed(); } @Nullable private static AnnotationAttributes getAnnotationAttributes(MergedAnnotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { if (!annotation.isPresent()) { return null; } return annotation.asAnnotationAttributes( Adapt.values(classValuesAsString, nestedAnnotationsAsMap)); } /** * Adapted {@link AnnotatedElement} that hold specific annotations. */ private static class AnnotatedElementForAnnotations implements AnnotatedElement { private final Annotation[] annotations; AnnotatedElementForAnnotations(Annotation... annotations) { this.annotations = annotations; } @Override @SuppressWarnings("unchecked") @Nullable public T getAnnotation(Class annotationClass) { for (Annotation annotation : this.annotations) { if (annotation.annotationType() == annotationClass) { return (T) annotation; } } return null; } @Override public Annotation[] getAnnotations() { return this.annotations.clone(); } @Override public Annotation[] getDeclaredAnnotations() { return this.annotations.clone(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy