Simplify search algorithms in AnnotatedElementUtils
This commit introduces a boolean alwaysProcesses() method in the Processor API which allows for simplification across all search algorithms within AnnotatedElementUtils. Specifically, it is no longer necessary for process() methods to verify that the supplied annotation is actually the sought target annotation. In addition, duplicated code has been extracted into common methods (e.g., hasMetaAnnotationTypes()) and common Processor implementations (e.g., AlwaysTrueBooleanAnnotationProcessor).
This commit is contained in:
parent
c6f6e192c0
commit
fee056a1b1
|
|
@ -96,10 +96,16 @@ import org.springframework.util.MultiValueMap;
|
||||||
*/
|
*/
|
||||||
public class AnnotatedElementUtils {
|
public class AnnotatedElementUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code null} constant used to denote that the search algorithm should continue.
|
||||||
|
*/
|
||||||
private static final Boolean CONTINUE = null;
|
private static final Boolean CONTINUE = null;
|
||||||
|
|
||||||
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
||||||
|
|
||||||
|
private static final Processor<Boolean> alwaysTrueAnnotationProcessor = new AlwaysTrueBooleanAnnotationProcessor();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an adapted {@link AnnotatedElement} for the given annotations,
|
* Build an adapted {@link AnnotatedElement} for the given annotations,
|
||||||
* typically for use with other methods on {@link AnnotatedElementUtils}.
|
* typically for use with other methods on {@link AnnotatedElementUtils}.
|
||||||
|
|
@ -146,27 +152,8 @@ public class AnnotatedElementUtils {
|
||||||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
final Set<String> types = new LinkedHashSet<String>();
|
|
||||||
|
|
||||||
try {
|
return getMetaAnnotationTypes(element, element.getAnnotation(annotationType));
|
||||||
Annotation annotation = element.getAnnotation(annotationType);
|
|
||||||
if (annotation != null) {
|
|
||||||
searchWithGetSemantics(annotation.annotationType(), annotationType, null, null,
|
|
||||||
new SimpleAnnotationProcessor<Object>() {
|
|
||||||
@Override
|
|
||||||
public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
types.add(annotation.annotationType().getName());
|
|
||||||
return CONTINUE;
|
|
||||||
}
|
|
||||||
}, new HashSet<AnnotatedElement>(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable ex) {
|
|
||||||
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
|
||||||
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (!types.isEmpty() ? types : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,27 +173,30 @@ public class AnnotatedElementUtils {
|
||||||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
|
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
||||||
final Set<String> types = new LinkedHashSet<String>();
|
|
||||||
|
return getMetaAnnotationTypes(element, AnnotationUtils.getAnnotation(element, annotationName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Annotation composed) {
|
||||||
|
if (composed == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Annotation annotation = AnnotationUtils.getAnnotation(element, annotationName);
|
final Set<String> types = new LinkedHashSet<String>();
|
||||||
if (annotation != null) {
|
searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor<Object>(true) {
|
||||||
searchWithGetSemantics(annotation.annotationType(), null, annotationName, null,
|
|
||||||
new SimpleAnnotationProcessor<Object>() {
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
types.add(annotation.annotationType().getName());
|
types.add(annotation.annotationType().getName());
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
}, new HashSet<AnnotatedElement>(), 1);
|
}, new HashSet<AnnotatedElement>(), 1);
|
||||||
}
|
return (!types.isEmpty() ? types : null);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
||||||
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
|
throw new IllegalStateException("Failed to introspect annotations on " + element, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!types.isEmpty() ? types : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -221,17 +211,11 @@ public class AnnotatedElementUtils {
|
||||||
* @since 4.2.3
|
* @since 4.2.3
|
||||||
* @see #getMetaAnnotationTypes
|
* @see #getMetaAnnotationTypes
|
||||||
*/
|
*/
|
||||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, final Class<? extends Annotation> annotationType) {
|
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
|
|
||||||
return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, new SimpleAnnotationProcessor<Boolean>() {
|
return hasMetaAnnotationTypes(element, annotationType, null);
|
||||||
@Override
|
|
||||||
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
boolean found = (annotation.annotationType() == annotationType);
|
|
||||||
return (found && metaDepth > 0 ? Boolean.TRUE : CONTINUE);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -246,17 +230,24 @@ public class AnnotatedElementUtils {
|
||||||
* @return {@code true} if a matching meta-annotation is present
|
* @return {@code true} if a matching meta-annotation is present
|
||||||
* @see #getMetaAnnotationTypes
|
* @see #getMetaAnnotationTypes
|
||||||
*/
|
*/
|
||||||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, final String annotationName) {
|
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
||||||
|
|
||||||
return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
return hasMetaAnnotationTypes(element, null, annotationName);
|
||||||
@Override
|
}
|
||||||
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
boolean found = annotation.annotationType().getName().equals(annotationName);
|
private static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType,
|
||||||
return (found && metaDepth > 0 ? Boolean.TRUE : CONTINUE);
|
String annotationName) {
|
||||||
}
|
|
||||||
}));
|
return Boolean.TRUE.equals(
|
||||||
|
searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
|
return (metaDepth > 0 ? Boolean.TRUE : CONTINUE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -273,7 +264,7 @@ public class AnnotatedElementUtils {
|
||||||
* @since 4.2.3
|
* @since 4.2.3
|
||||||
* @see #hasAnnotation(AnnotatedElement, Class)
|
* @see #hasAnnotation(AnnotatedElement, Class)
|
||||||
*/
|
*/
|
||||||
public static boolean isAnnotated(AnnotatedElement element, final Class<? extends Annotation> annotationType) {
|
public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
|
|
||||||
|
|
@ -282,13 +273,7 @@ public class AnnotatedElementUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, new SimpleAnnotationProcessor<Boolean>() {
|
return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
|
||||||
@Override
|
|
||||||
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
boolean found = (annotation.annotationType() == annotationType);
|
|
||||||
return (found ? Boolean.TRUE : CONTINUE);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -303,17 +288,11 @@ public class AnnotatedElementUtils {
|
||||||
* @param annotationName the fully qualified class name of the annotation type to find
|
* @param annotationName the fully qualified class name of the annotation type to find
|
||||||
* @return {@code true} if a matching annotation is present
|
* @return {@code true} if a matching annotation is present
|
||||||
*/
|
*/
|
||||||
public static boolean isAnnotated(AnnotatedElement element, final String annotationName) {
|
public static boolean isAnnotated(AnnotatedElement element, String annotationName) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
||||||
|
|
||||||
return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Boolean>() {
|
return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, alwaysTrueAnnotationProcessor));
|
||||||
@Override
|
|
||||||
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
boolean found = annotation.annotationType().getName().equals(annotationName);
|
|
||||||
return (found ? Boolean.TRUE : CONTINUE);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -356,7 +335,7 @@ public class AnnotatedElementUtils {
|
||||||
|
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null,
|
AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null,
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, false, false));
|
new MergedAnnotationAttributesProcessor());
|
||||||
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
|
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
@ -414,7 +393,7 @@ public class AnnotatedElementUtils {
|
||||||
|
|
||||||
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
||||||
AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName,
|
AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName,
|
||||||
new MergedAnnotationAttributesProcessor(null, annotationName, classValuesAsString, nestedAnnotationsAsMap));
|
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
|
||||||
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
@ -481,8 +460,7 @@ public class AnnotatedElementUtils {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor processor =
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
|
|
||||||
searchWithGetSemantics(element, annotationType, null, processor);
|
searchWithGetSemantics(element, annotationType, null, processor);
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
@ -553,11 +531,10 @@ public class AnnotatedElementUtils {
|
||||||
containerType = resolveContainerType(annotationType);
|
containerType = resolveContainerType(annotationType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
validateRepeatableContainerType(annotationType, containerType);
|
validateContainerType(annotationType, containerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor processor =
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
|
|
||||||
searchWithGetSemantics(element, annotationType, null, containerType, processor);
|
searchWithGetSemantics(element, annotationType, null, containerType, processor);
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
@ -599,23 +576,19 @@ public class AnnotatedElementUtils {
|
||||||
* attributes from all annotations found, or {@code null} if not found
|
* attributes from all annotations found, or {@code null} if not found
|
||||||
*/
|
*/
|
||||||
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
|
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
|
||||||
final String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
|
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
|
||||||
|
|
||||||
final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<String, Object>();
|
final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<String, Object>();
|
||||||
|
|
||||||
searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Void>() {
|
searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Void process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
boolean found = annotation.annotationType().getName().equals(annotationName);
|
AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(
|
||||||
if (found) {
|
annotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||||
AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(
|
for (Map.Entry<String, Object> entry : annotationAttributes.entrySet()) {
|
||||||
annotation, classValuesAsString, nestedAnnotationsAsMap);
|
attributesMap.add(entry.getKey(), entry.getValue());
|
||||||
for (Map.Entry<String, Object> entry : annotationAttributes.entrySet()) {
|
|
||||||
attributesMap.add(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Continue searching...
|
return CONTINUE;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -636,7 +609,7 @@ public class AnnotatedElementUtils {
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
* @see #isAnnotated(AnnotatedElement, Class)
|
* @see #isAnnotated(AnnotatedElement, Class)
|
||||||
*/
|
*/
|
||||||
public static boolean hasAnnotation(AnnotatedElement element, final Class<? extends Annotation> annotationType) {
|
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
|
|
||||||
|
|
@ -645,14 +618,7 @@ public class AnnotatedElementUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, annotationType.getName(),
|
return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor));
|
||||||
new SimpleAnnotationProcessor<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
|
||||||
boolean found = (annotation.annotationType() == annotationType);
|
|
||||||
return (found ? Boolean.TRUE : CONTINUE);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -687,8 +653,8 @@ public class AnnotatedElementUtils {
|
||||||
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
|
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
|
||||||
Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||||
|
|
||||||
AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, annotationType.getName(),
|
AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null,
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, classValuesAsString, nestedAnnotationsAsMap));
|
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
|
||||||
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
@ -724,7 +690,7 @@ public class AnnotatedElementUtils {
|
||||||
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||||
|
|
||||||
AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName,
|
AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName,
|
||||||
new MergedAnnotationAttributesProcessor(null, annotationName, classValuesAsString, nestedAnnotationsAsMap));
|
new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
|
||||||
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
@ -819,9 +785,8 @@ public class AnnotatedElementUtils {
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.notNull(annotationType, "annotationType must not be null");
|
Assert.notNull(annotationType, "annotationType must not be null");
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor processor =
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
|
searchWithFindSemantics(element, annotationType, null, processor);
|
||||||
searchWithFindSemantics(element, annotationType, annotationType.getName(), processor);
|
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,12 +856,11 @@ public class AnnotatedElementUtils {
|
||||||
containerType = resolveContainerType(annotationType);
|
containerType = resolveContainerType(annotationType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
validateRepeatableContainerType(annotationType, containerType);
|
validateContainerType(annotationType, containerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor processor =
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
|
searchWithFindSemantics(element, annotationType, null, containerType, processor);
|
||||||
searchWithFindSemantics(element, annotationType, annotationType.getName(), containerType, processor);
|
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1010,7 +974,7 @@ public class AnnotatedElementUtils {
|
||||||
* local annotations to take precedence over inherited annotations.
|
* local annotations to take precedence over inherited annotations.
|
||||||
* <p>The {@code metaDepth} parameter is explained in the
|
* <p>The {@code metaDepth} parameter is explained in the
|
||||||
* {@link Processor#process process()} method of the {@link Processor} API.
|
* {@link Processor#process process()} method of the {@link Processor} API.
|
||||||
* @param annotatedElement the element that is annotated with the supplied
|
* @param element the element that is annotated with the supplied
|
||||||
* annotations, used for contextual logging; may be {@code null} if unknown
|
* annotations, used for contextual logging; may be {@code null} if unknown
|
||||||
* @param annotations the annotations to search in
|
* @param annotations the annotations to search in
|
||||||
* @param annotationType the annotation type to find
|
* @param annotationType the annotation type to find
|
||||||
|
|
@ -1024,7 +988,7 @@ public class AnnotatedElementUtils {
|
||||||
* @return the result of the processor, potentially {@code null}
|
* @return the result of the processor, potentially {@code null}
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement annotatedElement,
|
private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element,
|
||||||
List<Annotation> annotations, Class<? extends Annotation> annotationType, String annotationName,
|
List<Annotation> annotations, Class<? extends Annotation> annotationType, String annotationName,
|
||||||
Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited,
|
Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited,
|
||||||
int metaDepth) {
|
int metaDepth) {
|
||||||
|
|
@ -1032,26 +996,11 @@ public class AnnotatedElementUtils {
|
||||||
// Search in annotations
|
// Search in annotations
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||||
|
if ((annotation.annotationType() == annotationType
|
||||||
|
|| annotation.annotationType().getName().equals(annotationName))
|
||||||
|
|| processor.alwaysProcesses()) {
|
||||||
|
|
||||||
// TODO Check non-repeatable annotations first, once we have sorted out
|
T result = processor.process(element, annotation, metaDepth);
|
||||||
// the metaDepth nuances of getMetaAnnotationTypes().
|
|
||||||
|
|
||||||
// Repeatable annotations in container?
|
|
||||||
if (annotation.annotationType() == containerType) {
|
|
||||||
for (Annotation contained : getRawAnnotationsFromContainer(annotatedElement, annotation)) {
|
|
||||||
T result = processor.process(annotatedElement, contained, metaDepth);
|
|
||||||
if (result != null) {
|
|
||||||
// No need to post-process since repeatable annotations within a
|
|
||||||
// container cannot be composed annotations.
|
|
||||||
processor.getAggregatedResults().add(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((annotation.annotationType() == annotationType
|
|
||||||
|| annotation.annotationType().getName().equals(annotationName)) || metaDepth > 0) {
|
|
||||||
|
|
||||||
// Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
|
|
||||||
T result = processor.process(annotatedElement, annotation, metaDepth);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (processor.aggregates() && metaDepth == 0) {
|
if (processor.aggregates() && metaDepth == 0) {
|
||||||
processor.getAggregatedResults().add(result);
|
processor.getAggregatedResults().add(result);
|
||||||
|
|
@ -1061,6 +1010,17 @@ public class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Repeatable annotations in container?
|
||||||
|
else if (annotation.annotationType() == containerType) {
|
||||||
|
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
|
||||||
|
T result = processor.process(element, contained, metaDepth);
|
||||||
|
if (result != null) {
|
||||||
|
// No need to post-process since repeatable annotations within a
|
||||||
|
// container cannot be composed annotations.
|
||||||
|
processor.getAggregatedResults().add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1070,7 +1030,7 @@ public class AnnotatedElementUtils {
|
||||||
T result = searchWithGetSemantics(annotation.annotationType(), annotationType,
|
T result = searchWithGetSemantics(annotation.annotationType(), annotationType,
|
||||||
annotationName, containerType, processor, visited, metaDepth + 1);
|
annotationName, containerType, processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
processor.postProcess(annotatedElement, annotation, result);
|
processor.postProcess(element, annotation, result);
|
||||||
if (processor.aggregates() && metaDepth == 0) {
|
if (processor.aggregates() && metaDepth == 0) {
|
||||||
processor.getAggregatedResults().add(result);
|
processor.getAggregatedResults().add(result);
|
||||||
}
|
}
|
||||||
|
|
@ -1114,7 +1074,7 @@ public class AnnotatedElementUtils {
|
||||||
* annotations, or {@code null} if the annotation is not repeatable
|
* annotations, or {@code null} if the annotation is not repeatable
|
||||||
* @param processor the processor to delegate to
|
* @param processor the processor to delegate to
|
||||||
* @return the result of the processor, potentially {@code null}
|
* @return the result of the processor, potentially {@code null}
|
||||||
* @since 4.2
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
|
private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType,
|
||||||
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) {
|
String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) {
|
||||||
|
|
@ -1140,7 +1100,7 @@ public class AnnotatedElementUtils {
|
||||||
* have already been <em>visited</em>.
|
* have already been <em>visited</em>.
|
||||||
* <p>The {@code metaDepth} parameter is explained in the
|
* <p>The {@code metaDepth} parameter is explained in the
|
||||||
* {@link Processor#process process()} method of the {@link Processor} API.
|
* {@link Processor#process process()} method of the {@link Processor} API.
|
||||||
* @param element the annotated element
|
* @param element the annotated element; never {@code null}
|
||||||
* @param annotationType the annotation type to find
|
* @param annotationType the annotation type to find
|
||||||
* @param annotationName the fully qualified class name of the annotation
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
|
|
@ -1157,7 +1117,6 @@ public class AnnotatedElementUtils {
|
||||||
Set<AnnotatedElement> visited, int metaDepth) {
|
Set<AnnotatedElement> visited, int metaDepth) {
|
||||||
|
|
||||||
Assert.notNull(element, "AnnotatedElement must not be null");
|
Assert.notNull(element, "AnnotatedElement must not be null");
|
||||||
Assert.hasLength(annotationName, "annotationName must not be null or empty");
|
|
||||||
|
|
||||||
if (visited.add(element)) {
|
if (visited.add(element)) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1169,7 +1128,8 @@ public class AnnotatedElementUtils {
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||||
if (annotation.annotationType() == annotationType
|
if (annotation.annotationType() == annotationType
|
||||||
|| annotation.annotationType().getName().equals(annotationName)) {
|
|| annotation.annotationType().getName().equals(annotationName)
|
||||||
|
|| processor.alwaysProcesses()) {
|
||||||
|
|
||||||
T result = processor.process(element, annotation, metaDepth);
|
T result = processor.process(element, annotation, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
@ -1265,8 +1225,7 @@ public class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (element instanceof Class) {
|
||||||
if (element instanceof Class) {
|
|
||||||
Class<?> clazz = (Class<?>) element;
|
Class<?> clazz = (Class<?>) element;
|
||||||
|
|
||||||
// Search on interfaces
|
// Search on interfaces
|
||||||
|
|
@ -1365,7 +1324,7 @@ public class AnnotatedElementUtils {
|
||||||
* @throws AnnotationConfigurationException if the supplied {@code containerType}
|
* @throws AnnotationConfigurationException if the supplied {@code containerType}
|
||||||
* is not a valid container annotation for the supplied {@code annotationType}
|
* is not a valid container annotation for the supplied {@code annotationType}
|
||||||
*/
|
*/
|
||||||
private static void validateRepeatableContainerType(Class<? extends Annotation> annotationType,
|
private static void validateContainerType(Class<? extends Annotation> annotationType,
|
||||||
Class<? extends Annotation> containerType) {
|
Class<? extends Annotation> containerType) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -1430,11 +1389,12 @@ public class AnnotatedElementUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the supplied annotation.
|
* Process the supplied annotation.
|
||||||
* <p>Depending on the use case, the supplied annotation may be an
|
* <p>The supplied annotation will be an actual target annotation
|
||||||
* actual target annotation that has been found by the search
|
* that has been found by the search algorithm, unless this processor
|
||||||
* algorithm, or it may be some other annotation within the
|
* is configured to {@linkplain #alwaysProcesses always process}
|
||||||
|
* annotations in which case it may be some other annotation within an
|
||||||
* annotation hierarchy. In the latter case, the {@code metaDepth}
|
* annotation hierarchy. In the latter case, the {@code metaDepth}
|
||||||
* should have a value greater than {@code 0}. In any case, it is
|
* will have a value greater than {@code 0}. In any case, it is
|
||||||
* up to concrete implementations of this method to decide what to
|
* up to concrete implementations of this method to decide what to
|
||||||
* do with the supplied annotation.
|
* do with the supplied annotation.
|
||||||
* <p>The {@code metaDepth} parameter represents the depth of the
|
* <p>The {@code metaDepth} parameter represents the depth of the
|
||||||
|
|
@ -1467,6 +1427,14 @@ public class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);
|
void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this processor always processes annotations regardless of
|
||||||
|
* whether or not the target annotation has been found.
|
||||||
|
* @return {@code true} if this processor always processes annotations
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
boolean alwaysProcesses();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this processor aggregates the results returned by {@link #process}.
|
* Determine if this processor aggregates the results returned by {@link #process}.
|
||||||
* <p>If this method returns {@code true}, then {@link #getAggregatedResults()}
|
* <p>If this method returns {@code true}, then {@link #getAggregatedResults()}
|
||||||
|
|
@ -1500,6 +1468,22 @@ public class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> {
|
private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> {
|
||||||
|
|
||||||
|
private final boolean alwaysProcesses;
|
||||||
|
|
||||||
|
|
||||||
|
public SimpleAnnotationProcessor() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleAnnotationProcessor(boolean alwaysProcesses) {
|
||||||
|
this.alwaysProcesses = alwaysProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean alwaysProcesses() {
|
||||||
|
return this.alwaysProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result) {
|
public final void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result) {
|
||||||
// no-op
|
// no-op
|
||||||
|
|
@ -1511,11 +1495,24 @@ public class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> getAggregatedResults() {
|
public final List<T> getAggregatedResults() {
|
||||||
throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results");
|
throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SimpleAnnotationProcessor} that always returns {@link Boolean#TRUE} when
|
||||||
|
* asked to {@linkplain #process(AnnotatedElement, Annotation, int) process} an
|
||||||
|
* annotation.
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
static class AlwaysTrueBooleanAnnotationProcessor extends SimpleAnnotationProcessor<Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Processor} that gets the {@code AnnotationAttributes} for the
|
* {@link Processor} that gets the {@code AnnotationAttributes} for the
|
||||||
|
|
@ -1530,10 +1527,6 @@ public class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {
|
private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {
|
||||||
|
|
||||||
private final Class<? extends Annotation> annotationType;
|
|
||||||
|
|
||||||
private final String annotationName;
|
|
||||||
|
|
||||||
private final boolean classValuesAsString;
|
private final boolean classValuesAsString;
|
||||||
|
|
||||||
private final boolean nestedAnnotationsAsMap;
|
private final boolean nestedAnnotationsAsMap;
|
||||||
|
|
@ -1543,23 +1536,28 @@ public class AnnotatedElementUtils {
|
||||||
private final List<AnnotationAttributes> aggregatedResults;
|
private final List<AnnotationAttributes> aggregatedResults;
|
||||||
|
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor(Class<? extends Annotation> annotationType, String annotationName,
|
MergedAnnotationAttributesProcessor() {
|
||||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
this(false, false, false);
|
||||||
|
|
||||||
this(annotationType, annotationName, classValuesAsString, nestedAnnotationsAsMap, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MergedAnnotationAttributesProcessor(Class<? extends Annotation> annotationType, String annotationName,
|
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean aggregates) {
|
this(classValuesAsString, nestedAnnotationsAsMap, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
|
||||||
|
boolean aggregates) {
|
||||||
|
|
||||||
this.annotationType = annotationType;
|
|
||||||
this.annotationName = annotationName;
|
|
||||||
this.classValuesAsString = classValuesAsString;
|
this.classValuesAsString = classValuesAsString;
|
||||||
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
|
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
|
||||||
this.aggregates = aggregates;
|
this.aggregates = aggregates;
|
||||||
this.aggregatedResults = (aggregates ? new ArrayList<AnnotationAttributes>() : null);
|
this.aggregatedResults = (aggregates ? new ArrayList<AnnotationAttributes>() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean alwaysProcesses() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean aggregates() {
|
public boolean aggregates() {
|
||||||
return this.aggregates;
|
return this.aggregates;
|
||||||
|
|
@ -1572,10 +1570,8 @@ public class AnnotatedElementUtils {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
boolean found = (this.annotationType != null ? annotation.annotationType() == this.annotationType :
|
return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
|
||||||
annotation.annotationType().getName().equals(this.annotationName));
|
this.classValuesAsString, this.nestedAnnotationsAsMap);
|
||||||
return (found ? AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
|
|
||||||
this.classValuesAsString, this.nestedAnnotationsAsMap) : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -70,18 +70,25 @@ public class AnnotatedElementUtilsTests {
|
||||||
@Test
|
@Test
|
||||||
public void getMetaAnnotationTypesOnNonAnnotatedClass() {
|
public void getMetaAnnotationTypesOnNonAnnotatedClass() {
|
||||||
assertNull(getMetaAnnotationTypes(NonAnnotatedClass.class, TransactionalComponent.class));
|
assertNull(getMetaAnnotationTypes(NonAnnotatedClass.class, TransactionalComponent.class));
|
||||||
|
assertNull(getMetaAnnotationTypes(NonAnnotatedClass.class, TransactionalComponent.class.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getMetaAnnotationTypesOnClassWithMetaDepth1() {
|
public void getMetaAnnotationTypesOnClassWithMetaDepth1() {
|
||||||
Set<String> names = getMetaAnnotationTypes(TransactionalComponentClass.class, TransactionalComponent.class);
|
Set<String> names = getMetaAnnotationTypes(TransactionalComponentClass.class, TransactionalComponent.class);
|
||||||
assertEquals(names(Transactional.class, Component.class), names);
|
assertEquals(names(Transactional.class, Component.class), names);
|
||||||
|
|
||||||
|
names = getMetaAnnotationTypes(TransactionalComponentClass.class, TransactionalComponent.class.getName());
|
||||||
|
assertEquals(names(Transactional.class, Component.class), names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getMetaAnnotationTypesOnClassWithMetaDepth2() {
|
public void getMetaAnnotationTypesOnClassWithMetaDepth2() {
|
||||||
Set<String> names = getMetaAnnotationTypes(ComposedTransactionalComponentClass.class, ComposedTransactionalComponent.class);
|
Set<String> names = getMetaAnnotationTypes(ComposedTransactionalComponentClass.class, ComposedTransactionalComponent.class);
|
||||||
assertEquals(names(TransactionalComponent.class, Transactional.class, Component.class), names);
|
assertEquals(names(TransactionalComponent.class, Transactional.class, Component.class), names);
|
||||||
|
|
||||||
|
names = getMetaAnnotationTypes(ComposedTransactionalComponentClass.class, ComposedTransactionalComponent.class.getName());
|
||||||
|
assertEquals(names(TransactionalComponent.class, Transactional.class, Component.class), names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue