diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2015-07-15 23:21:27 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2015-07-15 23:21:27 +0200 |
commit | da46d30e80e4c59a41cf52055d06faa1dbb7e383 (patch) | |
tree | 52b707fbbccd5b6100088913f32c1cbd00568790 /spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java | |
parent | c03c348db4e91c613982cbe6c99d0cf04ea14fe3 (diff) |
Imported Upstream version 4.0.9
Diffstat (limited to 'spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java')
-rw-r--r-- | spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java | 466 |
1 files changed, 357 insertions, 109 deletions
diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 3b4def63..45d7c48c 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -19,31 +19,44 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.core.BridgeMethodResolver; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; /** - * General utility methods for working with annotations, handling bridge methods (which the compiler - * generates for generic declarations) as well as super methods (for optional "annotation inheritance"). - * Note that none of this is provided by the JDK's introspection facilities themselves. + * General utility methods for working with annotations, handling bridge methods + * (which the compiler generates for generic declarations) as well as super methods + * (for optional "annotation inheritance"). Note that none of this is + * provided by the JDK's introspection facilities themselves. * - * <p>As a general rule for runtime-retained annotations (e.g. for transaction control, authorization or service - * exposure), always use the lookup methods on this class (e.g., {@link #findAnnotation(Method, Class)}, {@link - * #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) instead of the plain annotation lookup - * methods in the JDK. You can still explicitly choose between lookup on the given class level only ({@link - * #getAnnotation(Method, Class)}) and lookup in the entire inheritance hierarchy of the given method ({@link - * #findAnnotation(Method, Class)}). + * <p>As a general rule for runtime-retained annotations (e.g. for transaction + * control, authorization, or service exposure), always use the lookup methods + * on this class (e.g., {@link #findAnnotation(Method, Class)}, + * {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) + * instead of the plain annotation lookup methods in the JDK. You can still + * explicitly choose between a <em>get</em> lookup on the given class level only + * ({@link #getAnnotation(Method, Class)}) and a <em>find</em> lookup in the entire + * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}). * * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen * @author Mark Fisher * @author Chris Beams + * @author Phillip Webb * @since 2.0 * @see java.lang.reflect.Method#getAnnotations() * @see java.lang.reflect.Method#getAnnotation(Class) @@ -51,42 +64,100 @@ import org.springframework.util.ReflectionUtils; public abstract class AnnotationUtils { /** The attribute name for annotations with a single element */ - static final String VALUE = "value"; + public static final String VALUE = "value"; + private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>(); + private static transient Log logger; + + + /** + * Get a single {@link Annotation} of {@code annotationType} from the supplied + * annotation: either the given annotation itself or a meta-annotation thereof. + * @param ann the Annotation to check + * @param annotationType the annotation type to look for, both locally and as a meta-annotation + * @return the matching annotation, or {@code null} if none found + * @since 4.0 + */ + @SuppressWarnings("unchecked") + public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> annotationType) { + if (annotationType.isInstance(ann)) { + return (T) ann; + } + try { + return ann.annotationType().getAnnotation(annotationType); + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(ann.annotationType(), ex); + return null; + } + } /** * Get a single {@link Annotation} of {@code annotationType} from the supplied * Method, Constructor or Field. Meta-annotations will be searched if the annotation * is not declared locally on the supplied element. - * @param ae the Method, Constructor or Field from which to get the annotation + * @param annotatedElement the Method, Constructor or Field from which to get the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the matching annotation, or {@code null} if none found * @since 3.1 */ - public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) { - T ann = ae.getAnnotation(annotationType); - if (ann == null) { - for (Annotation metaAnn : ae.getAnnotations()) { - ann = metaAnn.annotationType().getAnnotation(annotationType); - if (ann != null) { - break; + public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedElement, Class<T> annotationType) { + try { + T ann = annotatedElement.getAnnotation(annotationType); + if (ann == null) { + for (Annotation metaAnn : annotatedElement.getAnnotations()) { + ann = metaAnn.annotationType().getAnnotation(annotationType); + if (ann != null) { + break; + } } } + return ann; + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(annotatedElement, ex); + return null; + } + } + + /** + * Get all {@link Annotation Annotations} from the supplied Method, Constructor or Field. + * @param annotatedElement the Method, Constructor or Field to retrieve annotations from + * @return the annotations found, or {@code null} if not resolvable (e.g. because nested + * Class values in annotation attributes failed to resolve at runtime) + * @since 4.0.8 + */ + public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { + try { + return annotatedElement.getAnnotations(); + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(annotatedElement, ex); + return null; } - return ann; } /** * Get all {@link Annotation Annotations} from the supplied {@link Method}. * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. - * @param method the method to look for annotations on + * @param method the Method to retrieve annotations from * @return the annotations found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) */ public static Annotation[] getAnnotations(Method method) { - return BridgeMethodResolver.findBridgedMethod(method).getAnnotations(); + try { + return BridgeMethodResolver.findBridgedMethod(method).getAnnotations(); + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(method, ex); + return null; + } } /** @@ -99,22 +170,61 @@ public abstract class AnnotationUtils { */ public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); - A ann = resolvedMethod.getAnnotation(annotationType); - if (ann == null) { - for (Annotation metaAnn : resolvedMethod.getAnnotations()) { - ann = metaAnn.annotationType().getAnnotation(annotationType); - if (ann != null) { - break; - } + return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); + } + + /** + * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the + * supplied {@link Method}. Deals with both a single direct annotation and repeating + * annotations nested within a containing annotation. + * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. + * @param method the method to look for annotations on + * @param containerAnnotationType the class of the container that holds the annotations + * @param annotationType the annotation type to look for + * @return the annotations found + * @since 4.0 + * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + */ + public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method, + Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) { + + Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); + return getRepeatableAnnotation((AnnotatedElement) resolvedMethod, containerAnnotationType, annotationType); + } + + /** + * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the + * supplied {@link AnnotatedElement}. Deals with both a single direct annotation and + * repeating annotations nested within a containing annotation. + * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. + * @param annotatedElement the element to look for annotations on + * @param containerAnnotationType the class of the container that holds the annotations + * @param annotationType the annotation type to look for + * @return the annotations found + * @since 4.0 + * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) + */ + public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement, + Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) { + + try { + if (annotatedElement.getAnnotations().length > 0) { + return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement); } } - return ann; + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(annotatedElement, ex); + } + return Collections.emptySet(); } /** - * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}, - * traversing its super methods if no annotation can be found on the given method itself. - * <p>Annotations on methods are not inherited by default, so we need to handle this explicitly. + * Find a single {@link Annotation} of {@code annotationType} from the supplied + * {@link Method}, traversing its super methods (i.e., from superclasses and + * interfaces) if no annotation can be found on the given method itself. + * <p>Annotations on methods are not inherited by default, so we need to handle + * this explicitly. * @param method the method to look for annotations on * @param annotationType the annotation type to look for * @return the annotation found, or {@code null} if none @@ -171,9 +281,15 @@ public abstract class AnnotationUtils { } boolean found = false; for (Method ifcMethod : iface.getMethods()) { - if (ifcMethod.getAnnotations().length > 0) { - found = true; - break; + try { + if (ifcMethod.getAnnotations().length > 0) { + found = true; + break; + } + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(ifcMethod, ex); } } annotatedInterfaceCache.put(iface, found); @@ -182,45 +298,82 @@ public abstract class AnnotationUtils { } /** - * Find a single {@link Annotation} of {@code annotationType} from the supplied {@link Class}, - * traversing its interfaces and superclasses if no annotation can be found on the given class itself. - * <p>This method explicitly handles class-level annotations which are not declared as - * {@link java.lang.annotation.Inherited inherited} <i>as well as annotations on interfaces</i>. - * <p>The algorithm operates as follows: Searches for an annotation on the given class and returns - * it if found. Else searches all interfaces that the given class declares, returning the annotation - * from the first matching candidate, if any. Else proceeds with introspection of the superclass - * of the given class, checking the superclass itself; if no annotation found there, proceeds - * with the interfaces that the superclass declares. Recursing up through the entire superclass - * hierarchy if no match is found. + * Find a single {@link Annotation} of {@code annotationType} on the + * supplied {@link Class}, traversing its interfaces, annotations, and + * superclasses if the annotation is not <em>present</em> on the given class + * itself. + * <p>This method explicitly handles class-level annotations which are not + * declared as {@link java.lang.annotation.Inherited inherited} <em>as well + * as meta-annotations and annotations on interfaces</em>. + * <p>The algorithm operates as follows: + * <ol> + * <li>Search for the annotation on the given class and return it if found. + * <li>Recursively search through all interfaces that the given class declares. + * <li>Recursively search through all annotations that the given class declares. + * <li>Recursively search through the superclass hierarchy of the given class. + * </ol> + * <p>Note: in this context, the term <em>recursively</em> means that the search + * process continues by returning to step #1 with the current interface, + * annotation, or superclass as the class to look for annotations on. * @param clazz the class to look for annotations on - * @param annotationType the annotation type to look for - * @return the annotation found, or {@code null} if none found + * @param annotationType the type of annotation to look for + * @return the annotation if found, or {@code null} if not found */ public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) { + return findAnnotation(clazz, annotationType, new HashSet<Annotation>()); + } + + /** + * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, + * avoiding endless recursion by tracking which annotations have already + * been <em>visited</em>. + * @param clazz the class to look for annotations on + * @param annotationType the type of annotation to look for + * @param visited the set of annotations that have already been visited + * @return the annotation if found, or {@code null} if not found + */ + private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) { Assert.notNull(clazz, "Class must not be null"); - A annotation = clazz.getAnnotation(annotationType); - if (annotation != null) { - return annotation; + + if (isAnnotationDeclaredLocally(annotationType, clazz)) { + try { + return clazz.getAnnotation(annotationType); + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(clazz, ex); + return null; + } } + for (Class<?> ifc : clazz.getInterfaces()) { - annotation = findAnnotation(ifc, annotationType); + A annotation = findAnnotation(ifc, annotationType, visited); if (annotation != null) { return annotation; } } - if (!Annotation.class.isAssignableFrom(clazz)) { - for (Annotation ann : clazz.getAnnotations()) { - annotation = findAnnotation(ann.annotationType(), annotationType); - if (annotation != null) { - return annotation; + + try { + for (Annotation ann : clazz.getDeclaredAnnotations()) { + if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { + A annotation = findAnnotation(ann.annotationType(), annotationType, visited); + if (annotation != null) { + return annotation; + } } } } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(clazz, ex); + return null; + } + Class<?> superclass = clazz.getSuperclass(); if (superclass == null || superclass.equals(Object.class)) { return null; } - return findAnnotation(superclass, annotationType); + return findAnnotation(superclass, annotationType, visited); } /** @@ -312,12 +465,18 @@ public abstract class AnnotationUtils { Assert.notNull(annotationType, "Annotation type must not be null"); Assert.notNull(clazz, "Class must not be null"); boolean declaredLocally = false; - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - if (annotation.annotationType().equals(annotationType)) { - declaredLocally = true; - break; + try { + for (Annotation ann : clazz.getDeclaredAnnotations()) { + if (ann.annotationType().equals(annotationType)) { + declaredLocally = true; + break; + } } } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(clazz, ex); + } return declaredLocally; } @@ -343,11 +502,21 @@ public abstract class AnnotationUtils { } /** - * Retrieve the given annotation's attributes as a Map, preserving all attribute types - * as-is. - * <p>Note: As of Spring 3.1.1, the returned map is actually an - * {@link AnnotationAttributes} instance, however the Map signature of this method has - * been preserved for binary compatibility. + * Determine if the supplied {@link Annotation} is defined in the core JDK + * {@code java.lang.annotation} package. + * @param annotation the annotation to check (never {@code null}) + * @return {@code true} if the annotation is in the {@code java.lang.annotation} package + */ + public static boolean isInJavaLangAnnotationPackage(Annotation annotation) { + Assert.notNull(annotation, "Annotation must not be null"); + return annotation.annotationType().getName().startsWith("java.lang.annotation"); + } + + /** + * Retrieve the given annotation's attributes as a {@link Map}, preserving all + * attribute types as-is. + * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. + * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for * @return the Map of annotation attributes, with attribute names as keys and * corresponding attribute values as values @@ -357,16 +526,15 @@ public abstract class AnnotationUtils { } /** - * Retrieve the given annotation's attributes as a Map. Equivalent to calling - * {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with + * Retrieve the given annotation's attributes as a {@link Map}. Equivalent to + * calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with * the {@code nestedAnnotationsAsMap} parameter set to {@code false}. - * <p>Note: As of Spring 3.1.1, the returned map is actually an - * {@link AnnotationAttributes} instance, however the Map signature of this method has - * been preserved for binary compatibility. + * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. + * However, the {@code Map} signature has been preserved for binary compatibility. * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to turn Class references into Strings (for - * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to - * preserve them as Class references + * compatibility with {@link org.springframework.core.type.AnnotationMetadata} + * or to preserve them as Class references * @return the Map of annotation attributes, with attribute names as keys and * corresponding attribute values as values */ @@ -376,13 +544,13 @@ public abstract class AnnotationUtils { /** * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} - * map structure. Implemented in Spring 3.1.1 to provide fully recursive annotation - * reading capabilities on par with that of the reflection-based - * {@link org.springframework.core.type.StandardAnnotationMetadata}. + * map structure. + * <p>This method provides fully recursive annotation reading capabilities on par with + * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to turn Class references into Strings (for - * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to - * preserve them as Class references + * compatibility with {@link org.springframework.core.type.AnnotationMetadata} + * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as @@ -400,34 +568,7 @@ public abstract class AnnotationUtils { if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { try { Object value = method.invoke(annotation); - if (classValuesAsString) { - if (value instanceof Class) { - value = ((Class<?>) value).getName(); - } - else if (value instanceof Class[]) { - Class<?>[] clazzArray = (Class[]) value; - String[] newValue = new String[clazzArray.length]; - for (int i = 0; i < clazzArray.length; i++) { - newValue[i] = clazzArray[i].getName(); - } - value = newValue; - } - } - if (nestedAnnotationsAsMap && value instanceof Annotation) { - attrs.put(method.getName(), - getAnnotationAttributes((Annotation) value, classValuesAsString, true)); - } - else if (nestedAnnotationsAsMap && value instanceof Annotation[]) { - Annotation[] realAnnotations = (Annotation[]) value; - AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; - for (int i = 0; i < realAnnotations.length; i++) { - mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true); - } - attrs.put(method.getName(), mappedAnnotations); - } - else { - attrs.put(method.getName(), value); - } + attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap)); } catch (Exception ex) { throw new IllegalStateException("Could not obtain annotation attribute values", ex); @@ -438,6 +579,48 @@ public abstract class AnnotationUtils { } /** + * Adapt the given value according to the given class and nested annotation settings. + * @param value the annotation attribute value + * @param classValuesAsString whether to turn Class references into Strings (for + * compatibility with {@link org.springframework.core.type.AnnotationMetadata} + * or to preserve them as Class references + * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into + * {@link AnnotationAttributes} maps (for compatibility with + * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as + * Annotation instances + * @return the adapted value, or the original value if no adaptation is needed + */ + static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + if (classValuesAsString) { + if (value instanceof Class) { + value = ((Class<?>) value).getName(); + } + else if (value instanceof Class[]) { + Class<?>[] clazzArray = (Class[]) value; + String[] newValue = new String[clazzArray.length]; + for (int i = 0; i < clazzArray.length; i++) { + newValue[i] = clazzArray[i].getName(); + } + value = newValue; + } + } + if (nestedAnnotationsAsMap && value instanceof Annotation) { + return getAnnotationAttributes((Annotation) value, classValuesAsString, true); + } + else if (nestedAnnotationsAsMap && value instanceof Annotation[]) { + Annotation[] realAnnotations = (Annotation[]) value; + AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; + for (int i = 0; i < realAnnotations.length; i++) { + mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true); + } + return mappedAnnotations; + } + else { + return value; + } + } + + /** * Retrieve the <em>value</em> of the {@code "value"} attribute of a * single-element Annotation, given an annotation instance. * @param annotation the annotation instance from which to retrieve the value @@ -449,7 +632,7 @@ public abstract class AnnotationUtils { } /** - * Retrieve the <em>value</em> of a named Annotation attribute, given an annotation instance. + * Retrieve the <em>value</em> of a named attribute, given an annotation instance. * @param annotation the annotation instance from which to retrieve the value * @param attributeName the name of the attribute value to retrieve * @return the attribute value, or {@code null} if not found @@ -478,7 +661,7 @@ public abstract class AnnotationUtils { } /** - * Retrieve the <em>default value</em> of a named Annotation attribute, given an annotation instance. + * Retrieve the <em>default value</em> of a named attribute, given an annotation instance. * @param annotation the annotation instance from which to retrieve the default value * @param attributeName the name of the attribute value to retrieve * @return the default value of the named attribute, or {@code null} if not found @@ -500,7 +683,8 @@ public abstract class AnnotationUtils { } /** - * Retrieve the <em>default value</em> of a named Annotation attribute, given the {@link Class annotation type}. + * Retrieve the <em>default value</em> of a named attribute, given the + * {@link Class annotation type}. * @param annotationType the <em>annotation type</em> for which the default value should be retrieved * @param attributeName the name of the attribute value to retrieve. * @return the default value of the named attribute, or {@code null} if not found @@ -515,4 +699,68 @@ public abstract class AnnotationUtils { } } + + private static void logIntrospectionFailure(AnnotatedElement annotatedElement, Exception ex) { + Log loggerToUse = logger; + if (loggerToUse == null) { + loggerToUse = LogFactory.getLog(AnnotationUtils.class); + logger = loggerToUse; + } + if (loggerToUse.isInfoEnabled()) { + loggerToUse.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex); + } + } + + + private static class AnnotationCollector<A extends Annotation> { + + private final Class<? extends Annotation> containerAnnotationType; + + private final Class<A> annotationType; + + private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>(); + + private final Set<A> result = new LinkedHashSet<A>(); + + public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) { + this.containerAnnotationType = containerAnnotationType; + this.annotationType = annotationType; + } + + public Set<A> getResult(AnnotatedElement element) { + process(element); + return Collections.unmodifiableSet(this.result); + } + + @SuppressWarnings("unchecked") + private void process(AnnotatedElement annotatedElement) { + if (this.visited.add(annotatedElement)) { + for (Annotation ann : annotatedElement.getAnnotations()) { + if (ObjectUtils.nullSafeEquals(this.annotationType, ann.annotationType())) { + this.result.add((A) ann); + } + else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, ann.annotationType())) { + this.result.addAll(getValue(ann)); + } + else if (!isInJavaLangAnnotationPackage(ann)) { + process(ann.annotationType()); + } + } + } + } + + @SuppressWarnings("unchecked") + private List<A> getValue(Annotation annotation) { + try { + Method method = annotation.annotationType().getDeclaredMethod("value"); + ReflectionUtils.makeAccessible(method); + return Arrays.asList((A[]) method.invoke(annotation)); + } + catch (Exception ex) { + // Unable to read value from repeating annotation container -> ignore it. + return Collections.emptyList(); + } + } + } + } |