Nullability refinements (no IntelliJ warnings, fewer null checks)
Includes consistent ignoring of all java.lang (meta-)annotations. Closes gh-22586
This commit is contained in:
@@ -33,8 +33,7 @@ import org.springframework.util.Assert;
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
abstract class AbstractMergedAnnotation<A extends Annotation>
|
||||
implements MergedAnnotation<A> {
|
||||
abstract class AbstractMergedAnnotation<A extends Annotation> implements MergedAnnotation<A> {
|
||||
|
||||
@Nullable
|
||||
private volatile A synthesizedAnnotation;
|
||||
|
||||
@@ -828,13 +828,13 @@ public abstract class AnnotatedElementUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AnnotationAttributes getAnnotationAttributes(
|
||||
MergedAnnotation<?> annotation, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
private static AnnotationAttributes getAnnotationAttributes(MergedAnnotation<?> annotation,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
if (!annotation.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
return annotation.asMap((mergedAnnotation) -> new AnnotationAttributes(),
|
||||
return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(),
|
||||
MapValues.of(classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
|
||||
|
||||
@@ -68,14 +68,12 @@ public class AnnotationAwareOrderComparator extends OrderComparator {
|
||||
return findOrderFromAnnotation(obj);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Integer findOrderFromAnnotation(Object obj) {
|
||||
AnnotatedElement element = obj instanceof AnnotatedElement
|
||||
? (AnnotatedElement) obj
|
||||
: obj.getClass();
|
||||
MergedAnnotations annotations = MergedAnnotations.from(element,
|
||||
SearchStrategy.EXHAUSTIVE);
|
||||
AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
|
||||
MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.EXHAUSTIVE);
|
||||
Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
|
||||
if (order == null && obj instanceof DecoratingProxy) {
|
||||
if (order == null && obj instanceof DecoratingProxy) {
|
||||
return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
|
||||
}
|
||||
return order;
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -37,31 +36,27 @@ public interface AnnotationFilter {
|
||||
* {@code java.lang.*} or in the
|
||||
* {@code org.springframework.lang.*} package.
|
||||
*/
|
||||
static final AnnotationFilter PLAIN = packages("java.lang",
|
||||
"org.springframework.lang");
|
||||
AnnotationFilter PLAIN = packages("java.lang", "org.springframework.lang");
|
||||
|
||||
/**
|
||||
* {@link AnnotationFilter} that matches annotations in the
|
||||
* {@code java.lang.*} package.
|
||||
*/
|
||||
static final AnnotationFilter JAVA = packages("java.lang");
|
||||
AnnotationFilter JAVA = packages("java.lang");
|
||||
|
||||
/**
|
||||
* {@link AnnotationFilter} that never matches and can be used when no
|
||||
* filtering is needed.
|
||||
*/
|
||||
static final AnnotationFilter NONE = new AnnotationFilter() {
|
||||
|
||||
AnnotationFilter NONE = new AnnotationFilter() {
|
||||
@Override
|
||||
public boolean matches(@Nullable String typeName) {
|
||||
public boolean matches(String typeName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "No annotation filtering";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -70,8 +65,8 @@ public interface AnnotationFilter {
|
||||
* @param annotation the annotation to test
|
||||
* @return {@code true} if the annotation matches
|
||||
*/
|
||||
default boolean matches(@Nullable Annotation annotation) {
|
||||
return matches(annotation != null ? annotation.annotationType() : null);
|
||||
default boolean matches(Annotation annotation) {
|
||||
return matches(annotation.annotationType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,8 +74,8 @@ public interface AnnotationFilter {
|
||||
* @param type the annotation type to test
|
||||
* @return {@code true} if the annotation matches
|
||||
*/
|
||||
default boolean matches(@Nullable Class<?> type) {
|
||||
return matches(type != null ? type.getName() : null);
|
||||
default boolean matches(Class<?> type) {
|
||||
return matches(type.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +83,8 @@ public interface AnnotationFilter {
|
||||
* @param typeName the annotation type to test
|
||||
* @return {@code true} if the annotation matches
|
||||
*/
|
||||
boolean matches(@Nullable String typeName);
|
||||
boolean matches(String typeName);
|
||||
|
||||
|
||||
/**
|
||||
* Return a new {@link AnnotationFilter} that matches annotations in the
|
||||
@@ -107,8 +103,8 @@ public interface AnnotationFilter {
|
||||
* @param annotationType the annotation type to check
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(@Nullable Class<?> annotationType) {
|
||||
return PLAIN.matches(annotationType) ? NONE : PLAIN;
|
||||
static AnnotationFilter mostAppropriateFor(Class<?> annotationType) {
|
||||
return (PLAIN.matches(annotationType) ? NONE : PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,30 +115,6 @@ public interface AnnotationFilter {
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(Class<?>... annotationTypes) {
|
||||
Assert.notNull(annotationTypes, "AnnotationTypes must not be null");
|
||||
return mostAppropriateFor(Arrays.asList(annotationTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link AnnotationFilter} that is the most appropriate for, and
|
||||
* will always match all the given annotation type. Whenever possible,
|
||||
* {@link AnnotationFilter#PLAIN} will be returned.
|
||||
* @param annotationType the annotation type to check
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(@Nullable String annotationType) {
|
||||
return PLAIN.matches(annotationType) ? NONE : PLAIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link AnnotationFilter} that is the most appropriate for, and
|
||||
* will always match all the given annotation types. Whenever possible,
|
||||
* {@link AnnotationFilter#PLAIN} will be returned.
|
||||
* @param annotationTypes the annotation types to check
|
||||
* @return the most appropriate annotation filter
|
||||
*/
|
||||
static AnnotationFilter mostAppropriateFor(String... annotationTypes) {
|
||||
Assert.notNull(annotationTypes, "AnnotationTypes must not be null");
|
||||
return mostAppropriateFor(Arrays.asList(annotationTypes));
|
||||
}
|
||||
|
||||
@@ -156,20 +128,16 @@ public interface AnnotationFilter {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static AnnotationFilter mostAppropriateFor(Collection<?> annotationTypes) {
|
||||
Assert.notNull(annotationTypes, "AnnotationTypes must not be null");
|
||||
for (Object annotationType : annotationTypes) {
|
||||
if (annotationType == null) {
|
||||
continue;
|
||||
}
|
||||
Assert.isTrue(
|
||||
annotationType instanceof Class || annotationType instanceof String,
|
||||
Assert.isTrue(annotationType instanceof Class || annotationType instanceof String,
|
||||
"AnnotationType must be a Class or String");
|
||||
if (annotationType instanceof Class
|
||||
&& PLAIN.matches((Class<Annotation>) annotationType)) {
|
||||
if (annotationType instanceof Class && PLAIN.matches((Class<Annotation>) annotationType)) {
|
||||
return NONE;
|
||||
}
|
||||
if (annotationType instanceof String
|
||||
&& PLAIN.matches((String) annotationType)) {
|
||||
if (annotationType instanceof String && PLAIN.matches((String) annotationType)) {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import org.springframework.util.StringUtils;
|
||||
* @since 5.2
|
||||
* @see AnnotationTypeMappings
|
||||
*/
|
||||
class AnnotationTypeMapping {
|
||||
final class AnnotationTypeMapping {
|
||||
|
||||
@Nullable
|
||||
private final AnnotationTypeMapping parent;
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
@@ -75,11 +74,9 @@ final class AnnotationTypeMappings {
|
||||
}
|
||||
}
|
||||
|
||||
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue,
|
||||
AnnotationTypeMapping parent) {
|
||||
|
||||
Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(
|
||||
parent.getAnnotationType(), false);
|
||||
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping parent) {
|
||||
Annotation[] metaAnnotations =
|
||||
AnnotationsScanner.getDeclaredAnnotations(parent.getAnnotationType(), false);
|
||||
for (Annotation metaAnnotation : metaAnnotations) {
|
||||
if (!isMappable(parent, metaAnnotation)) {
|
||||
continue;
|
||||
@@ -125,19 +122,17 @@ final class AnnotationTypeMappings {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMappable(AnnotationTypeMapping parent, Annotation metaAnnotation) {
|
||||
return !this.filter.matches(metaAnnotation) &&
|
||||
private boolean isMappable(AnnotationTypeMapping parent, @Nullable Annotation metaAnnotation) {
|
||||
return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
|
||||
!AnnotationFilter.PLAIN.matches(parent.getAnnotationType()) &&
|
||||
!isAlreadyMapped(parent, metaAnnotation);
|
||||
!isAlreadyMapped(parent, metaAnnotation));
|
||||
}
|
||||
|
||||
private boolean isAlreadyMapped(AnnotationTypeMapping parent,
|
||||
Annotation metaAnnotation) {
|
||||
|
||||
private boolean isAlreadyMapped(AnnotationTypeMapping parent, Annotation metaAnnotation) {
|
||||
Class<? extends Annotation> annotationType = metaAnnotation.annotationType();
|
||||
AnnotationTypeMapping mapping = parent;
|
||||
while (mapping != null) {
|
||||
if (mapping.getAnnotationType().equals(annotationType)) {
|
||||
if (mapping.getAnnotationType() == annotationType) {
|
||||
return true;
|
||||
}
|
||||
mapping = mapping.getParent();
|
||||
@@ -171,11 +166,8 @@ final class AnnotationTypeMappings {
|
||||
* @param annotationType the source annotation type
|
||||
* @return type mappings for the annotation type
|
||||
*/
|
||||
static AnnotationTypeMappings forAnnotationType(
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return forAnnotationType(annotationType,
|
||||
AnnotationFilter.mostAppropriateFor(annotationType));
|
||||
static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
return forAnnotationType(annotationType, AnnotationFilter.mostAppropriateFor(annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,11 +178,8 @@ final class AnnotationTypeMappings {
|
||||
* @return type mappings for the annotation type
|
||||
*/
|
||||
static AnnotationTypeMappings forAnnotationType(
|
||||
Class<? extends Annotation> annotationType,
|
||||
AnnotationFilter annotationFilter) {
|
||||
Class<? extends Annotation> annotationType, AnnotationFilter annotationFilter) {
|
||||
|
||||
Assert.notNull(annotationType, "AnnotationType must not be null");
|
||||
Assert.notNull(annotationFilter, "AnnotationFilter must not be null");
|
||||
return cache.computeIfAbsent(annotationFilter, Cache::new).get(annotationType);
|
||||
}
|
||||
|
||||
|
||||
@@ -477,8 +477,10 @@ public abstract class AnnotationUtils {
|
||||
* @see #getAnnotation(Method, Class)
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A findAnnotation(Method method,
|
||||
@Nullable Class<A> annotationType) {
|
||||
public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE,
|
||||
@@ -510,9 +512,7 @@ public abstract class AnnotationUtils {
|
||||
* @return the first matching annotation, or {@code null} if not found
|
||||
*/
|
||||
@Nullable
|
||||
public static <A extends Annotation> A findAnnotation(Class<?> clazz,
|
||||
Class<A> annotationType) {
|
||||
|
||||
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
@@ -535,8 +535,8 @@ public abstract class AnnotationUtils {
|
||||
* @param annotationType the annotation type to look for
|
||||
* @param clazz the class to check for the annotation on (may be {@code null})
|
||||
* @return the first {@link Class} in the inheritance hierarchy that
|
||||
* declares an annotation of the specified {@code annotationType}, or
|
||||
* {@code null} if not found
|
||||
* declares an annotation of the specified {@code annotationType},
|
||||
* or {@code null} if not found
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see Class#getDeclaredAnnotations()
|
||||
* @see #findAnnotationDeclaringClassForTypes(List, Class)
|
||||
@@ -546,6 +546,10 @@ public abstract class AnnotationUtils {
|
||||
public static Class<?> findAnnotationDeclaringClass(
|
||||
Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) {
|
||||
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
@@ -567,7 +571,7 @@ public abstract class AnnotationUtils {
|
||||
* one of several candidate {@linkplain Annotation annotations}, so we
|
||||
* need to handle this explicitly.
|
||||
* @param annotationTypes the annotation types to look for
|
||||
* @param clazz the class to check for the annotations on, or {@code null}
|
||||
* @param clazz the class to check for the annotation on (may be {@code null})
|
||||
* @return the first {@link Class} in the inheritance hierarchy that
|
||||
* declares an annotation of at least one of the specified
|
||||
* {@code annotationTypes}, or {@code null} if not found
|
||||
@@ -581,6 +585,10 @@ public abstract class AnnotationUtils {
|
||||
public static Class<?> findAnnotationDeclaringClassForTypes(
|
||||
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
|
||||
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationTypes);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
@@ -608,9 +616,7 @@ public abstract class AnnotationUtils {
|
||||
* @see java.lang.Class#getDeclaredAnnotation(Class)
|
||||
* @see #isAnnotationInherited(Class, Class)
|
||||
*/
|
||||
public static boolean isAnnotationDeclaredLocally(
|
||||
Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
|
||||
public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
return MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent();
|
||||
}
|
||||
|
||||
@@ -633,9 +639,7 @@ public abstract class AnnotationUtils {
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
*/
|
||||
public static boolean isAnnotationInherited(
|
||||
Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
|
||||
public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
@@ -651,12 +655,10 @@ public abstract class AnnotationUtils {
|
||||
* @return {@code true} if such an annotation is meta-present
|
||||
* @since 4.2.1
|
||||
*/
|
||||
public static boolean isAnnotationMetaPresent(
|
||||
Class<? extends Annotation> annotationType,
|
||||
public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType,
|
||||
@Nullable Class<? extends Annotation> metaAnnotationType) {
|
||||
|
||||
return MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE)
|
||||
.isPresent(annotationType);
|
||||
return MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE).isPresent(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -666,7 +668,7 @@ public abstract class AnnotationUtils {
|
||||
* @return {@code true} if the annotation is in the {@code java.lang.annotation} package
|
||||
*/
|
||||
public static boolean isInJavaLangAnnotationPackage(@Nullable Annotation annotation) {
|
||||
return JAVA_LANG_ANNOTATION_FILTER.matches(annotation);
|
||||
return (annotation != null && JAVA_LANG_ANNOTATION_FILTER.matches(annotation));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -677,7 +679,7 @@ public abstract class AnnotationUtils {
|
||||
* @since 4.2
|
||||
*/
|
||||
public static boolean isInJavaLangAnnotationPackage(@Nullable String annotationType) {
|
||||
return JAVA_LANG_ANNOTATION_FILTER.matches(annotationType);
|
||||
return (annotationType != null && JAVA_LANG_ANNOTATION_FILTER.matches(annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,8 +695,7 @@ public abstract class AnnotationUtils {
|
||||
* @see #getAnnotationAttributes(Annotation)
|
||||
*/
|
||||
public static void validateAnnotation(Annotation annotation) {
|
||||
AttributeMethods.forAnnotationType(annotation.annotationType())
|
||||
.validate(annotation);
|
||||
AttributeMethods.forAnnotationType(annotation.annotationType()).validate(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,13 +890,15 @@ public abstract class AnnotationUtils {
|
||||
public static void postProcessAnnotationAttributes(@Nullable Object annotatedElement,
|
||||
@Nullable AnnotationAttributes attributes, boolean classValuesAsString) {
|
||||
|
||||
if (attributes == null || attributes.annotationType() == null) {
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
if (!attributes.validated) {
|
||||
Class<? extends Annotation> annotationType = attributes.annotationType();
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings
|
||||
.forAnnotationType(annotationType).get(0);
|
||||
if (annotationType == null) {
|
||||
return;
|
||||
}
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(annotationType).get(0);
|
||||
for (int i = 0; i < mapping.getMirrorSets().size(); i++) {
|
||||
MirrorSet mirrorSet = mapping.getMirrorSets().get(i);
|
||||
int resolved = mirrorSet.resolve(attributes.displayName, attributes,
|
||||
|
||||
@@ -55,13 +55,12 @@ interface AnnotationsProcessor<C, R> {
|
||||
* @return a {@code non-null} result if no further processing is required
|
||||
*/
|
||||
@Nullable
|
||||
R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source,
|
||||
Annotation[] annotations);
|
||||
R doWithAnnotations(C context, int aggregateIndex, @Nullable Object source, Annotation[] annotations);
|
||||
|
||||
/**
|
||||
* Return the final result to be returned. By default this method returns
|
||||
* the last process result.
|
||||
* @param result the last early exit result, or {@code null}.
|
||||
* @param result the last early exit result, or {@code null} if none
|
||||
* @return the final result to be returned to the caller
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -71,8 +71,9 @@ abstract class AnnotationsScanner {
|
||||
* @return the result of {@link AnnotationsProcessor#finish(Object)}
|
||||
*/
|
||||
@Nullable
|
||||
static <C, R> R scan(C context, AnnotatedElement source,
|
||||
SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor) {
|
||||
static <C, R> R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy,
|
||||
AnnotationsProcessor<C, R> processor) {
|
||||
|
||||
return scan(context, source, searchStrategy, processor, null);
|
||||
}
|
||||
|
||||
@@ -89,9 +90,8 @@ abstract class AnnotationsScanner {
|
||||
* @return the result of {@link AnnotationsProcessor#finish(Object)}
|
||||
*/
|
||||
@Nullable
|
||||
static <C, R> R scan(C context, AnnotatedElement source,
|
||||
SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
static <C, R> R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy,
|
||||
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
R result = process(context, source, searchStrategy, processor, classFilter);
|
||||
return processor.finish(result);
|
||||
@@ -135,8 +135,7 @@ abstract class AnnotationsScanner {
|
||||
|
||||
@Nullable
|
||||
private static <C, R> R processClassInheritedAnnotations(C context, Class<?> source,
|
||||
AnnotationsProcessor<C, R> processor,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
if (isWithoutHierarchy(source)) {
|
||||
return processElement(context, source, processor, classFilter);
|
||||
@@ -145,8 +144,8 @@ abstract class AnnotationsScanner {
|
||||
int remaining = Integer.MAX_VALUE;
|
||||
int aggregateIndex = 0;
|
||||
Class<?> root = source;
|
||||
while (source != null && source != Object.class && remaining > 0
|
||||
&& !hasPlainJavaAnnotationsOnly(source)) {
|
||||
while (source != null && source != Object.class && remaining > 0 &&
|
||||
!hasPlainJavaAnnotationsOnly(source)) {
|
||||
R result = processor.doWithAggregate(context, aggregateIndex);
|
||||
if (result != null) {
|
||||
return result;
|
||||
@@ -208,7 +207,7 @@ abstract class AnnotationsScanner {
|
||||
if (includeInterfaces) {
|
||||
for (Class<?> interfaceType : source.getInterfaces()) {
|
||||
R interfacesResult = processClassHierarchy(context, aggregateIndex,
|
||||
interfaceType, processor, classFilter, includeInterfaces);
|
||||
interfaceType, processor, classFilter, true);
|
||||
if (interfacesResult != null) {
|
||||
return interfacesResult;
|
||||
}
|
||||
@@ -233,13 +232,12 @@ abstract class AnnotationsScanner {
|
||||
switch (searchStrategy) {
|
||||
case DIRECT:
|
||||
case INHERITED_ANNOTATIONS:
|
||||
return processMethodInheritedAnnotations(context, source,
|
||||
processor, classFilter);
|
||||
return processMethodInheritedAnnotations(context, source, processor, classFilter);
|
||||
case SUPER_CLASS:
|
||||
return processMethodHierarchy(context, new int[] { 0 }, source.getDeclaringClass(),
|
||||
return processMethodHierarchy(context, new int[] {0}, source.getDeclaringClass(),
|
||||
processor, classFilter, source, false);
|
||||
case EXHAUSTIVE:
|
||||
return processMethodHierarchy(context, new int[] { 0 }, source.getDeclaringClass(),
|
||||
return processMethodHierarchy(context, new int[] {0}, source.getDeclaringClass(),
|
||||
processor, classFilter, source, true);
|
||||
}
|
||||
throw new IllegalStateException("Unsupported search strategy " + searchStrategy);
|
||||
@@ -247,12 +245,11 @@ abstract class AnnotationsScanner {
|
||||
|
||||
@Nullable
|
||||
private static <C, R> R processMethodInheritedAnnotations(C context, Method source,
|
||||
AnnotationsProcessor<C, R> processor,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
R result = processor.doWithAggregate(context, 0);
|
||||
return result != null ? result :
|
||||
processMethodAnnotations(context, 0, source, processor, classFilter);
|
||||
return (result != null ? result :
|
||||
processMethodAnnotations(context, 0, source, processor, classFilter));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -298,7 +295,7 @@ abstract class AnnotationsScanner {
|
||||
if (includeInterfaces) {
|
||||
for (Class<?> interfaceType : sourceClass.getInterfaces()) {
|
||||
R interfacesResult = processMethodHierarchy(context, aggregateIndex,
|
||||
interfaceType, processor, classFilter, rootMethod, includeInterfaces);
|
||||
interfaceType, processor, classFilter, rootMethod, true);
|
||||
if (interfacesResult != null) {
|
||||
return interfacesResult;
|
||||
}
|
||||
@@ -315,8 +312,8 @@ abstract class AnnotationsScanner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <C> Method[] getBaseTypeMethods(C context, Class<?> baseType,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
private static <C> Method[] getBaseTypeMethods(
|
||||
C context, Class<?> baseType, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
if (baseType == Object.class || hasPlainJavaAnnotationsOnly(baseType) ||
|
||||
isFiltered(baseType, context, classFilter)) {
|
||||
@@ -326,8 +323,7 @@ abstract class AnnotationsScanner {
|
||||
Method[] methods = baseTypeMethodsCache.get(baseType);
|
||||
if (methods == null) {
|
||||
boolean isInterface = baseType.isInterface();
|
||||
methods = isInterface ? baseType.getMethods()
|
||||
: ReflectionUtils.getDeclaredMethods(baseType);
|
||||
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
|
||||
int cleared = 0;
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
|
||||
@@ -346,14 +342,12 @@ abstract class AnnotationsScanner {
|
||||
}
|
||||
|
||||
private static boolean isOverride(Method rootMethod, Method candidateMethod) {
|
||||
return !Modifier.isPrivate(candidateMethod.getModifiers()) &&
|
||||
return (!Modifier.isPrivate(candidateMethod.getModifiers()) &&
|
||||
candidateMethod.getName().equals(rootMethod.getName()) &&
|
||||
hasSameParameterTypes(rootMethod, candidateMethod);
|
||||
hasSameParameterTypes(rootMethod, candidateMethod));
|
||||
}
|
||||
|
||||
private static boolean hasSameParameterTypes(Method rootMethod,
|
||||
Method candidateMethod) {
|
||||
|
||||
private static boolean hasSameParameterTypes(Method rootMethod, Method candidateMethod) {
|
||||
if (candidateMethod.getParameterCount() != rootMethod.getParameterCount()) {
|
||||
return false;
|
||||
}
|
||||
@@ -366,8 +360,8 @@ abstract class AnnotationsScanner {
|
||||
rootParameterTypes);
|
||||
}
|
||||
|
||||
private static boolean hasSameGenericTypeParameters(Method rootMethod,
|
||||
Method candidateMethod, Class<?>[] rootParameterTypes) {
|
||||
private static boolean hasSameGenericTypeParameters(
|
||||
Method rootMethod, Method candidateMethod, Class<?>[] rootParameterTypes) {
|
||||
|
||||
Class<?> sourceDeclaringClass = rootMethod.getDeclaringClass();
|
||||
Class<?> candidateDeclaringClass = candidateMethod.getDeclaringClass();
|
||||
@@ -385,53 +379,43 @@ abstract class AnnotationsScanner {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <C, R> R processMethodAnnotations(C context, int aggregateIndex,
|
||||
Method source, AnnotationsProcessor<C, R> processor,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
private static <C, R> R processMethodAnnotations(C context, int aggregateIndex, Method source,
|
||||
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
Annotation[] annotations = getDeclaredAnnotations(context, source, classFilter,
|
||||
false);
|
||||
R result = processor.doWithAnnotations(context, aggregateIndex, source,
|
||||
annotations);
|
||||
Annotation[] annotations = getDeclaredAnnotations(context, source, classFilter, false);
|
||||
R result = processor.doWithAnnotations(context, aggregateIndex, source, annotations);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(source);
|
||||
if (bridgedMethod != source) {
|
||||
Annotation[] bridgedAnnotations = getDeclaredAnnotations(context,
|
||||
bridgedMethod, classFilter, true);
|
||||
Annotation[] bridgedAnnotations = getDeclaredAnnotations(context, bridgedMethod, classFilter, true);
|
||||
for (int i = 0; i < bridgedAnnotations.length; i++) {
|
||||
if (ObjectUtils.containsElement(annotations, bridgedAnnotations[i])) {
|
||||
bridgedAnnotations[i] = null;
|
||||
}
|
||||
}
|
||||
return processor.doWithAnnotations(context, aggregateIndex, source,
|
||||
bridgedAnnotations);
|
||||
return processor.doWithAnnotations(context, aggregateIndex, source, bridgedAnnotations);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <C, R> R processElement(C context, AnnotatedElement source,
|
||||
AnnotationsProcessor<C, R> processor,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
R result = processor.doWithAggregate(context, 0);
|
||||
return result != null ? result
|
||||
: processor.doWithAnnotations(context, 0, source,
|
||||
getDeclaredAnnotations(context, source, classFilter, false));
|
||||
return (result != null ? result : processor.doWithAnnotations(context, 0, source,
|
||||
getDeclaredAnnotations(context, source, classFilter, false)));
|
||||
}
|
||||
|
||||
private static <C, R> Annotation[] getDeclaredAnnotations(C context,
|
||||
AnnotatedElement source, @Nullable BiPredicate<C, Class<?>> classFilter,
|
||||
boolean copy) {
|
||||
AnnotatedElement source, @Nullable BiPredicate<C, Class<?>> classFilter, boolean copy) {
|
||||
|
||||
if (source instanceof Class &&
|
||||
isFiltered((Class<?>) source, context, classFilter)) {
|
||||
if (source instanceof Class && isFiltered((Class<?>) source, context, classFilter)) {
|
||||
return NO_ANNOTATIONS;
|
||||
}
|
||||
if (source instanceof Method &&
|
||||
isFiltered(((Method) source).getDeclaringClass(), context, classFilter)) {
|
||||
if (source instanceof Method && isFiltered(((Method) source).getDeclaringClass(), context, classFilter)) {
|
||||
return NO_ANNOTATIONS;
|
||||
}
|
||||
return getDeclaredAnnotations(source, copy);
|
||||
@@ -439,40 +423,37 @@ abstract class AnnotationsScanner {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
static <A extends Annotation> A getDeclaredAnnotation(AnnotatedElement source,
|
||||
Class<A> annotationType) {
|
||||
|
||||
static <A extends Annotation> A getDeclaredAnnotation(AnnotatedElement source, Class<A> annotationType) {
|
||||
Annotation[] annotations = getDeclaredAnnotations(source, false);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if (annotations[i] != null &&
|
||||
annotationType.equals(annotations[i].annotationType())) {
|
||||
return (A) annotations[i];
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation != null && annotationType.equals(annotation.annotationType())) {
|
||||
return (A) annotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Annotation[] getDeclaredAnnotations(AnnotatedElement source,
|
||||
boolean defensive) {
|
||||
|
||||
boolean cached = true;
|
||||
static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {
|
||||
boolean cached = false;
|
||||
Annotation[] annotations = declaredAnnotationCache.get(source);
|
||||
if (annotations == null) {
|
||||
if (annotations != null) {
|
||||
cached = true;
|
||||
}
|
||||
else {
|
||||
annotations = source.getDeclaredAnnotations();
|
||||
if (annotations.length != 0) {
|
||||
boolean allIgnored = true;
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Annotation annotation = annotations[i];
|
||||
if (isIgnorable(annotation.annotationType()) ||
|
||||
!AttributeMethods.forAnnotationType(
|
||||
annotation.annotationType()).isValid(annotation)) {
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) {
|
||||
annotations[i] = null;
|
||||
}
|
||||
else {
|
||||
allIgnored = false;
|
||||
}
|
||||
}
|
||||
annotations = allIgnored ? NO_ANNOTATIONS : annotations;
|
||||
annotations = (allIgnored ? NO_ANNOTATIONS : annotations);
|
||||
if (source instanceof Class || source instanceof Member) {
|
||||
declaredAnnotationCache.put(source, annotations);
|
||||
cached = true;
|
||||
@@ -486,19 +467,17 @@ abstract class AnnotationsScanner {
|
||||
}
|
||||
|
||||
private static boolean isIgnorable(Class<?> annotationType) {
|
||||
return (annotationType == Nullable.class ||
|
||||
annotationType == FunctionalInterface.class);
|
||||
return AnnotationFilter.PLAIN.matches(annotationType);
|
||||
}
|
||||
|
||||
private static <C> boolean isFiltered(Class<?> sourceClass, C context,
|
||||
@Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
return classFilter != null && classFilter.test(context, sourceClass);
|
||||
private static <C> boolean isFiltered(
|
||||
Class<?> sourceClass, C context, @Nullable BiPredicate<C, Class<?>> classFilter) {
|
||||
|
||||
return (classFilter != null && classFilter.test(context, sourceClass));
|
||||
}
|
||||
|
||||
static boolean isKnownEmpty(AnnotatedElement source,
|
||||
SearchStrategy searchStrategy, AnnotationFilter annotationFilter) {
|
||||
if (annotationFilter == AnnotationFilter.PLAIN &&
|
||||
hasPlainJavaAnnotationsOnly(source)) {
|
||||
static boolean isKnownEmpty(AnnotatedElement source,SearchStrategy searchStrategy, AnnotationFilter annotationFilter) {
|
||||
if (annotationFilter == AnnotationFilter.PLAIN && hasPlainJavaAnnotationsOnly(source)) {
|
||||
return true;
|
||||
}
|
||||
if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source)) {
|
||||
@@ -511,7 +490,7 @@ abstract class AnnotationsScanner {
|
||||
}
|
||||
|
||||
static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) {
|
||||
Class<?> type = null;
|
||||
Class<?> type;
|
||||
if (annotatedElement instanceof Class) {
|
||||
type = (Class<?>) annotatedElement;
|
||||
}
|
||||
@@ -522,11 +501,11 @@ abstract class AnnotationsScanner {
|
||||
return false;
|
||||
}
|
||||
String name = type.getName();
|
||||
return type.equals(Ordered.class) ||
|
||||
return (type == Ordered.class ||
|
||||
name.startsWith("java") ||
|
||||
name.startsWith("org.springframework.lang.") ||
|
||||
name.startsWith("org.springframework.util.") ||
|
||||
(name.startsWith("com.sun") && !name.contains("Proxy"));
|
||||
(name.startsWith("com.sun") && !name.contains("Proxy")));
|
||||
}
|
||||
|
||||
private static boolean isWithoutHierarchy(AnnotatedElement source) {
|
||||
@@ -535,13 +514,13 @@ abstract class AnnotationsScanner {
|
||||
}
|
||||
if (source instanceof Class) {
|
||||
Class<?> sourceClass = (Class<?>) source;
|
||||
return sourceClass.getSuperclass() == Object.class &&
|
||||
sourceClass.getInterfaces().length == 0;
|
||||
return (sourceClass.getSuperclass() == Object.class &&
|
||||
sourceClass.getInterfaces().length == 0);
|
||||
}
|
||||
if (source instanceof Method) {
|
||||
Method sourceMethod = (Method) source;
|
||||
return Modifier.isPrivate(sourceMethod.getModifiers()) ||
|
||||
isWithoutHierarchy(sourceMethod.getDeclaringClass());
|
||||
return (Modifier.isPrivate(sourceMethod.getModifiers()) ||
|
||||
isWithoutHierarchy(sourceMethod.getDeclaringClass()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ final class AttributeMethods {
|
||||
return m1 != null ? -1 : 1;
|
||||
};
|
||||
|
||||
|
||||
@Nullable
|
||||
private final Class<? extends Annotation> annotationType;
|
||||
|
||||
@@ -60,8 +61,7 @@ final class AttributeMethods {
|
||||
private final boolean hasNestedAnnotation;
|
||||
|
||||
|
||||
private AttributeMethods(@Nullable Class<? extends Annotation> annotationType,
|
||||
Method[] attributeMethods) {
|
||||
private AttributeMethods(@Nullable Class<? extends Annotation> annotationType, Method[] attributeMethods) {
|
||||
this.annotationType = annotationType;
|
||||
this.attributeMethods = attributeMethods;
|
||||
this.canThrowTypeNotPresentException = new boolean[attributeMethods.length];
|
||||
@@ -94,8 +94,8 @@ final class AttributeMethods {
|
||||
* @return {@code true} if this is only a value attribute
|
||||
*/
|
||||
boolean isOnlyValueAttribute() {
|
||||
return this.attributeMethods.length == 1 &&
|
||||
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName());
|
||||
return (this.attributeMethods.length == 1 &&
|
||||
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName()));
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ final class AttributeMethods {
|
||||
* accessed without causing any {@link TypeNotPresentException
|
||||
* TypeNotPresentExceptions}.
|
||||
* @param annotation the annotation to check
|
||||
* @return {@true} if all values are present
|
||||
* @return {@code true} if all values are present
|
||||
* @see #validate(Annotation)
|
||||
*/
|
||||
boolean isValid(Annotation annotation) {
|
||||
@@ -123,14 +123,13 @@ final class AttributeMethods {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}. In
|
||||
* particular, this method is designed to cover Google App Engine's late
|
||||
* arrival of such exceptions for {@code Class} values (instead of the more
|
||||
* typical early {@code Class.getAnnotations() failure}.
|
||||
* Checks if values from the given annotation can be safely accessed without causing
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular,
|
||||
* this method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure}.
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could
|
||||
* not be read
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @see #isValid(Annotation)
|
||||
*/
|
||||
void validate(Annotation annotation) {
|
||||
@@ -141,11 +140,8 @@ final class AttributeMethods {
|
||||
get(i).invoke(annotation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not obtain annotation attribute value for "
|
||||
+ get(i).getName() + " declared on "
|
||||
+ annotation.annotationType(),
|
||||
ex);
|
||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||
get(i).getName() + " declared on " + annotation.annotationType(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,14 +242,13 @@ final class AttributeMethods {
|
||||
return this.hasNestedAnnotation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the attribute methods for the given annotation type.
|
||||
* @param annotationType the annotation type
|
||||
* @return the attribute methods for the annotation
|
||||
*/
|
||||
static AttributeMethods forAnnotationType(
|
||||
@Nullable Class<? extends Annotation> annotationType) {
|
||||
|
||||
static AttributeMethods forAnnotationType(@Nullable Class<? extends Annotation> annotationType) {
|
||||
if (annotationType == null) {
|
||||
return NONE;
|
||||
}
|
||||
@@ -279,7 +274,7 @@ final class AttributeMethods {
|
||||
}
|
||||
|
||||
private static boolean isAttributeMethod(Method method) {
|
||||
return method.getParameterCount() == 0 && method.getReturnType() != void.class;
|
||||
return (method.getParameterCount() == 0 && method.getReturnType() != void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,14 +297,11 @@ final class AttributeMethods {
|
||||
* @param attributeName the attribute name
|
||||
* @return a description of the attribute
|
||||
*/
|
||||
static String describe(@Nullable Class<?> annotationType,
|
||||
@Nullable String attributeName) {
|
||||
static String describe(@Nullable Class<?> annotationType, @Nullable String attributeName) {
|
||||
if (attributeName == null) {
|
||||
return "(none)";
|
||||
}
|
||||
String in = annotationType != null ?
|
||||
" in annotation [" + annotationType.getName() + "]" :
|
||||
"";
|
||||
String in = (annotationType != null ? " in annotation [" + annotationType.getName() + "]" : "");
|
||||
return "attribute '" + attributeName + "'" + in;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,10 @@ import org.springframework.lang.Nullable;
|
||||
enum IntrospectionFailureLogger {
|
||||
|
||||
DEBUG {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return getLogger().isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
getLogger().debug(message);
|
||||
@@ -46,12 +44,10 @@ enum IntrospectionFailureLogger {
|
||||
},
|
||||
|
||||
INFO {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return getLogger().isInfoEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
getLogger().info(message);
|
||||
@@ -63,15 +59,16 @@ enum IntrospectionFailureLogger {
|
||||
private static Log logger;
|
||||
|
||||
|
||||
abstract boolean isEnabled();
|
||||
|
||||
void log(String message, @Nullable Object source, Exception ex) {
|
||||
String on = source != null ? " on " + source : "";
|
||||
String on = (source != null ? " on " + source : "");
|
||||
log(message + on + ": " + ex);
|
||||
}
|
||||
|
||||
abstract boolean isEnabled();
|
||||
|
||||
abstract void log(String message);
|
||||
|
||||
|
||||
private static Log getLogger() {
|
||||
Log logger = IntrospectionFailureLogger.logger;
|
||||
if (logger == null) {
|
||||
|
||||
@@ -40,8 +40,8 @@ import org.springframework.lang.Nullable;
|
||||
* For example, to access an {@code int} attribute the {@link #getInt(String)}
|
||||
* method would be used.
|
||||
*
|
||||
* <p>Note that attribute values are <b>not</b> converted when accessed. For
|
||||
* example, it is not possible to call {@link #getString(String)} if the
|
||||
* <p>Note that attribute values are <b>not</b> converted when accessed.
|
||||
* For example, it is not possible to call {@link #getString(String)} if the
|
||||
* underlying attribute is an {@code int}. The only exception to this rule is
|
||||
* {@code Class} and {@code Class[]} values which may be accessed as
|
||||
* {@code String} and {@code String[]} respectively to prevent potential early
|
||||
@@ -63,6 +63,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
*/
|
||||
String VALUE = "value";
|
||||
|
||||
|
||||
/**
|
||||
* Return the class name of the actual annotation type.
|
||||
* @return the annotation type
|
||||
@@ -342,8 +343,8 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
* @return the value as a {@link MergedAnnotation}
|
||||
* @throws NoSuchElementException if there is no matching attribute
|
||||
*/
|
||||
<T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName,
|
||||
Class<T> type) throws NoSuchElementException;
|
||||
<T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName, Class<T> type)
|
||||
throws NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Return a required annotation array attribute value from the annotation.
|
||||
@@ -413,10 +414,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
MergedAnnotation<A> filterAttributes(Predicate<String> predicate);
|
||||
|
||||
/**
|
||||
* Return a new view of the annotation that exposes non-merged attribute
|
||||
* values. Methods from this view will return attribute values with only
|
||||
* alias mirroring rules applied. Aliases to parent attributes will not be
|
||||
* applied.
|
||||
* Return a new view of the annotation that exposes non-merged attribute values.
|
||||
* Methods from this view will return attribute values with only alias mirroring
|
||||
* rules applied. Aliases to parent attributes will not be applied.
|
||||
* @return a non-merged view of the annotation
|
||||
*/
|
||||
MergedAnnotation<A> withNonMergedAttributes();
|
||||
@@ -431,9 +431,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
Map<String, Object> asMap(MapValues... options);
|
||||
|
||||
/**
|
||||
* Return a {@link Map} of the supplied type that contains all the
|
||||
* annotation attributes. The {@link MapValues} options may be used to
|
||||
* change the way that values are added.
|
||||
* Return a {@link Map} of the supplied type that contains all the annotation
|
||||
* attributes. The {@link MapValues} options may be used to change the way
|
||||
* that values are added.
|
||||
* @param factory a map factory or {@code null} to return an immutable map.
|
||||
* @param options map value options
|
||||
* @return a map containing the attributes and values
|
||||
@@ -443,22 +443,19 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
|
||||
/**
|
||||
* Return a type-safe synthesized version of this annotation that can be
|
||||
* used directly in code. The result is synthesized using a JDK
|
||||
* {@link Proxy} and as a result may incur a computational cost when first
|
||||
* invoked.
|
||||
* @return a sythesized version of the annotation.
|
||||
* used directly in code. The result is synthesized using a JDK {@link Proxy}
|
||||
* and as a result may incur a computational cost when first invoked.
|
||||
* @return a synthesized version of the annotation.
|
||||
* @throws NoSuchElementException on a missing annotation
|
||||
*/
|
||||
A synthesize() throws NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Optionally return type-safe synthesized version of this annotation based
|
||||
* on a condition predicate. The result is synthesized using a JDK
|
||||
* {@link Proxy} and as a result may incur a computational cost when first
|
||||
* invoked.
|
||||
* @param condition the test to determine if the annotation can be
|
||||
* sythesized
|
||||
* @return a optional containing the sythesized version of the annotation or
|
||||
* on a condition predicate. The result is synthesized using a JDK {@link Proxy}
|
||||
* and as a result may incur a computational cost when first invoked.
|
||||
* @param condition the test to determine if the annotation can be synthesized
|
||||
* @return a optional containing the synthesized version of the annotation or
|
||||
* an empty optional if the condition doesn't match
|
||||
* @throws NoSuchElementException on a missing annotation
|
||||
* @see MergedAnnotationPredicates
|
||||
@@ -466,6 +463,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
Optional<A> synthesize(@Nullable Predicate<? super MergedAnnotation<A>> condition)
|
||||
throws NoSuchElementException;
|
||||
|
||||
|
||||
/**
|
||||
* Return an {@link MergedAnnotation} that represents a missing annotation
|
||||
* (i.e. one that is not present).
|
||||
@@ -494,8 +492,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
* @param annotation the annotation to include
|
||||
* @return a {@link MergedAnnotation} instance for the annotation
|
||||
*/
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source,
|
||||
A annotation) {
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
||||
return TypeMappedAnnotation.from(source, annotation);
|
||||
}
|
||||
|
||||
@@ -520,8 +517,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
* attributes
|
||||
* @see #from(AnnotatedElement, Class, Map)
|
||||
*/
|
||||
static <A extends Annotation> MergedAnnotation<A> from(Class<A> annotationType,
|
||||
@Nullable Map<String, ?> attributes) {
|
||||
static <A extends Annotation> MergedAnnotation<A> from(
|
||||
Class<A> annotationType, @Nullable Map<String, ?> attributes) {
|
||||
|
||||
return from(null, annotationType, attributes);
|
||||
}
|
||||
|
||||
@@ -538,8 +536,8 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
* attributes
|
||||
*/
|
||||
static <A extends Annotation> MergedAnnotation<A> from(
|
||||
@Nullable AnnotatedElement source, Class<A> annotationType,
|
||||
@Nullable Map<String, ?> attributes) {
|
||||
@Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String, ?> attributes) {
|
||||
|
||||
return TypeMappedAnnotation.from(source, annotationType, attributes);
|
||||
}
|
||||
|
||||
@@ -561,7 +559,6 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
*/
|
||||
ANNOTATION_TO_MAP;
|
||||
|
||||
|
||||
protected final boolean isIn(MapValues... options) {
|
||||
for (MapValues candidate : options) {
|
||||
if (candidate == this) {
|
||||
@@ -572,11 +569,9 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link MapValues} array from a set of
|
||||
* boolean flags.
|
||||
* Factory method to create a {@link MapValues} array from a set of boolean flags.
|
||||
* @param classToString if {@link MapValues#CLASS_TO_STRING} is included
|
||||
* @param annotationsToMap if {@link MapValues#ANNOTATION_TO_MAP} is
|
||||
* included
|
||||
* @param annotationsToMap if {@link MapValues#ANNOTATION_TO_MAP} is included
|
||||
* @return a new {@link MapValues} array
|
||||
*/
|
||||
public static MapValues[] of(boolean classToString, boolean annotationsToMap) {
|
||||
@@ -591,7 +586,6 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.stream.Collector;
|
||||
import java.util.stream.Collector.Characteristics;
|
||||
|
||||
import org.springframework.core.annotation.MergedAnnotation.MapValues;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -42,8 +41,7 @@ public abstract class MergedAnnotationCollectors {
|
||||
|
||||
private static final Characteristics[] NO_CHARACTERISTICS = {};
|
||||
|
||||
private static final Characteristics[] IDENTITY_FINISH_CHARACTERISTICS = {
|
||||
Characteristics.IDENTITY_FINISH };
|
||||
private static final Characteristics[] IDENTITY_FINISH_CHARACTERISTICS = {Characteristics.IDENTITY_FINISH};
|
||||
|
||||
|
||||
private MergedAnnotationCollectors() {
|
||||
@@ -128,15 +126,14 @@ public abstract class MergedAnnotationCollectors {
|
||||
Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher,
|
||||
MapValues... options) {
|
||||
|
||||
Assert.notNull(finisher, "Finisher must not be null");
|
||||
Characteristics[] characteristics = isSameInstance(finisher, Function.identity()) ?
|
||||
IDENTITY_FINISH_CHARACTERISTICS :
|
||||
NO_CHARACTERISTICS;
|
||||
Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ?
|
||||
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
|
||||
return Collector.of(LinkedMultiValueMap::new,
|
||||
(map, annotation) -> annotation.asMap(options).forEach(map::add),
|
||||
MergedAnnotationCollectors::merge, finisher, characteristics);
|
||||
}
|
||||
|
||||
|
||||
private static boolean isSameInstance(Object instance, Object candidate) {
|
||||
return instance == candidate;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,7 @@ public abstract class MergedAnnotationPredicates {
|
||||
* @param typeNames the names that should be matched
|
||||
* @return a {@link Predicate} to test the annotation type
|
||||
*/
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(
|
||||
String... typeNames) {
|
||||
|
||||
Assert.notNull(typeNames, "TypeNames must not be null");
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(String... typeNames) {
|
||||
return annotation -> ObjectUtils.containsElement(typeNames, annotation.getType());
|
||||
}
|
||||
|
||||
@@ -64,12 +61,8 @@ public abstract class MergedAnnotationPredicates {
|
||||
* @param types the types that should be matched
|
||||
* @return a {@link Predicate} to test the annotation type
|
||||
*/
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(
|
||||
Class<?>... types) {
|
||||
|
||||
Assert.notNull(types, "Types must not be null");
|
||||
return annotation -> Arrays.stream(types)
|
||||
.anyMatch(type -> type.getName().equals(annotation.getType()));
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(Class<?>... types) {
|
||||
return annotation -> Arrays.stream(types).anyMatch(type -> type.getName().equals(annotation.getType()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,10 +73,7 @@ public abstract class MergedAnnotationPredicates {
|
||||
* @param types the type names or classes that should be matched
|
||||
* @return a {@link Predicate} to test the annotation type
|
||||
*/
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(
|
||||
Collection<?> types) {
|
||||
|
||||
Assert.notNull(types, "Types must not be null");
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<? extends A>> typeIn(Collection<?> types) {
|
||||
return annotation -> types.stream()
|
||||
.map(type -> type instanceof Class ? ((Class<?>) type).getName() : type.toString())
|
||||
.anyMatch(typeName -> typeName.equals(annotation.getType()));
|
||||
@@ -103,7 +93,6 @@ public abstract class MergedAnnotationPredicates {
|
||||
public static <A extends Annotation> Predicate<MergedAnnotation<A>> firstRunOf(
|
||||
Function<? super MergedAnnotation<A>, ?> valueExtractor) {
|
||||
|
||||
Assert.notNull(valueExtractor, "ValueExtractor must not be null");
|
||||
return new FirstRunOfPredicate<>(valueExtractor);
|
||||
}
|
||||
|
||||
@@ -120,7 +109,6 @@ public abstract class MergedAnnotationPredicates {
|
||||
public static <A extends Annotation, K> Predicate<MergedAnnotation<A>> unique(
|
||||
Function<? super MergedAnnotation<A>, K> keyExtractor) {
|
||||
|
||||
Assert.notNull(keyExtractor, "KeyExtractor must not be null");
|
||||
return new UniquePredicate<>(keyExtractor);
|
||||
}
|
||||
|
||||
@@ -129,8 +117,7 @@ public abstract class MergedAnnotationPredicates {
|
||||
* {@link Predicate} implementation used for
|
||||
* {@link MergedAnnotationPredicates#firstRunOf(Function)}.
|
||||
*/
|
||||
private static class FirstRunOfPredicate<A extends Annotation>
|
||||
implements Predicate<MergedAnnotation<A>> {
|
||||
private static class FirstRunOfPredicate<A extends Annotation> implements Predicate<MergedAnnotation<A>> {
|
||||
|
||||
private final Function<? super MergedAnnotation<A>, ?> valueExtractor;
|
||||
|
||||
@@ -139,13 +126,11 @@ public abstract class MergedAnnotationPredicates {
|
||||
@Nullable
|
||||
private Object lastValue;
|
||||
|
||||
|
||||
FirstRunOfPredicate(
|
||||
Function<? super MergedAnnotation<A>, ?> valueExtractor) {
|
||||
FirstRunOfPredicate(Function<? super MergedAnnotation<A>, ?> valueExtractor) {
|
||||
Assert.notNull(valueExtractor, "Value extractor must not be null");
|
||||
this.valueExtractor = valueExtractor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable MergedAnnotation<A> annotation) {
|
||||
if (!this.hasLastValue) {
|
||||
@@ -164,19 +149,17 @@ public abstract class MergedAnnotationPredicates {
|
||||
* {@link Predicate} implementation used for
|
||||
* {@link MergedAnnotationPredicates#unique(Function)}.
|
||||
*/
|
||||
private static class UniquePredicate<A extends Annotation, K>
|
||||
implements Predicate<MergedAnnotation<A>> {
|
||||
private static class UniquePredicate<A extends Annotation, K> implements Predicate<MergedAnnotation<A>> {
|
||||
|
||||
private final Function<? super MergedAnnotation<A>, K> keyExtractor;
|
||||
|
||||
private final Set<K> seen = new HashSet<>();
|
||||
|
||||
|
||||
UniquePredicate(Function<? super MergedAnnotation<A>, K> keyExtractor) {
|
||||
Assert.notNull(keyExtractor, "Key extractor must not be null");
|
||||
this.keyExtractor = keyExtractor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable MergedAnnotation<A> annotation) {
|
||||
K key = this.keyExtractor.apply(annotation);
|
||||
|
||||
@@ -47,7 +47,6 @@ public interface MergedAnnotationSelector<A extends Annotation> {
|
||||
* @return the most appropriate annotation from the {@code existing} or
|
||||
* {@code candidate}
|
||||
*/
|
||||
MergedAnnotation<A> select(MergedAnnotation<A> existing,
|
||||
MergedAnnotation<A> candidate);
|
||||
MergedAnnotation<A> select(MergedAnnotation<A> existing, MergedAnnotation<A> candidate);
|
||||
|
||||
}
|
||||
|
||||
@@ -70,8 +70,9 @@ public abstract class MergedAnnotationSelectors {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MergedAnnotation<Annotation> select(MergedAnnotation<Annotation> existing,
|
||||
MergedAnnotation<Annotation> candidate) {
|
||||
public MergedAnnotation<Annotation> select(
|
||||
MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {
|
||||
|
||||
if (candidate.getDepth() < existing.getDepth()) {
|
||||
return candidate;
|
||||
}
|
||||
@@ -93,8 +94,9 @@ public abstract class MergedAnnotationSelectors {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MergedAnnotation<Annotation> select(MergedAnnotation<Annotation> existing,
|
||||
MergedAnnotation<Annotation> candidate) {
|
||||
public MergedAnnotation<Annotation> select(
|
||||
MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {
|
||||
|
||||
if (existing.getDepth() > 0 && candidate.getDepth() == 0) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to check
|
||||
* @return {@code true} if the annotation is present
|
||||
*/
|
||||
<A extends Annotation> boolean isPresent(@Nullable Class<A> annotationType);
|
||||
<A extends Annotation> boolean isPresent(Class<A> annotationType);
|
||||
|
||||
/**
|
||||
* Return if the specified annotation is directly present. Equivalent to
|
||||
@@ -141,7 +141,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to check
|
||||
* @return {@code true} if the annotation is present
|
||||
*/
|
||||
boolean isPresent(@Nullable String annotationType);
|
||||
boolean isPresent(String annotationType);
|
||||
|
||||
/**
|
||||
* Return if the specified annotation is directly present. Equivalent to
|
||||
@@ -149,7 +149,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to check
|
||||
* @return {@code true} if the annotation is present
|
||||
*/
|
||||
<A extends Annotation> boolean isDirectlyPresent(@Nullable Class<A> annotationType);
|
||||
<A extends Annotation> boolean isDirectlyPresent(Class<A> annotationType);
|
||||
|
||||
/**
|
||||
* Return if the specified annotation is either directly present, or
|
||||
@@ -158,7 +158,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to check
|
||||
* @return {@code true} if the annotation is present
|
||||
*/
|
||||
boolean isDirectlyPresent(@Nullable String annotationType);
|
||||
boolean isDirectlyPresent(String annotationType);
|
||||
|
||||
/**
|
||||
* Return the {@link MergedAnnotationSelectors#nearest() nearest} matching
|
||||
@@ -167,7 +167,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to get
|
||||
* @return a {@link MergedAnnotation} instance
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable Class<A> annotationType);
|
||||
<A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType);
|
||||
|
||||
/**
|
||||
* Return the {@link MergedAnnotationSelectors#nearest() nearest} matching
|
||||
@@ -179,7 +179,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotation} instance
|
||||
* @see MergedAnnotationPredicates
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable Class<A> annotationType,
|
||||
<A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate);
|
||||
|
||||
/**
|
||||
@@ -195,7 +195,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @see MergedAnnotationPredicates
|
||||
* @see MergedAnnotationSelectors
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable Class<A> annotationType,
|
||||
<A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector);
|
||||
|
||||
@@ -206,7 +206,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to get
|
||||
* @return a {@link MergedAnnotation} instance
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable String annotationType);
|
||||
<A extends Annotation> MergedAnnotation<A> get(String annotationType);
|
||||
|
||||
/**
|
||||
* Return the {@link MergedAnnotationSelectors#nearest() nearest} matching
|
||||
@@ -218,7 +218,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotation} instance
|
||||
* @see MergedAnnotationPredicates
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable String annotationType,
|
||||
<A extends Annotation> MergedAnnotation<A> get(String annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate);
|
||||
|
||||
/**
|
||||
@@ -234,7 +234,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @see MergedAnnotationPredicates
|
||||
* @see MergedAnnotationSelectors
|
||||
*/
|
||||
<A extends Annotation> MergedAnnotation<A> get(@Nullable String annotationType,
|
||||
<A extends Annotation> MergedAnnotation<A> get(String annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector);
|
||||
|
||||
@@ -245,8 +245,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to match
|
||||
* @return a stream of matching annotations
|
||||
*/
|
||||
<A extends Annotation> Stream<MergedAnnotation<A>> stream(
|
||||
@Nullable Class<A> annotationType);
|
||||
<A extends Annotation> Stream<MergedAnnotation<A>> stream(Class<A> annotationType);
|
||||
|
||||
/**
|
||||
* Stream all annotations and meta-annotations that match the specified
|
||||
@@ -255,8 +254,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @param annotationType the annotation type to match
|
||||
* @return a stream of matching annotations
|
||||
*/
|
||||
<A extends Annotation> Stream<MergedAnnotation<A>> stream(
|
||||
@Nullable String annotationType);
|
||||
<A extends Annotation> Stream<MergedAnnotation<A>> stream(String annotationType);
|
||||
|
||||
/**
|
||||
* Stream all contained annotations and meta-annotations contained in this
|
||||
@@ -269,6 +267,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
*/
|
||||
Stream<MergedAnnotation<Annotation>> stream();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link MergedAnnotations} instance containing all
|
||||
* annotations and meta-annotations from the specified element. The
|
||||
@@ -280,7 +279,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotations} instance containing the element
|
||||
* annotations
|
||||
*/
|
||||
static MergedAnnotations from(@Nullable AnnotatedElement element) {
|
||||
static MergedAnnotations from(AnnotatedElement element) {
|
||||
return from(element, SearchStrategy.DIRECT);
|
||||
}
|
||||
|
||||
@@ -293,10 +292,8 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotations} instance containing the merged
|
||||
* element annotations
|
||||
*/
|
||||
static MergedAnnotations from(@Nullable AnnotatedElement element,
|
||||
SearchStrategy searchStrategy) {
|
||||
return from(element, searchStrategy, RepeatableContainers.standardRepeatables(),
|
||||
AnnotationFilter.PLAIN);
|
||||
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy) {
|
||||
return from(element, searchStrategy, RepeatableContainers.standardRepeatables(), AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,11 +309,10 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotations} instance containing the merged
|
||||
* element annotations
|
||||
*/
|
||||
static MergedAnnotations from(@Nullable AnnotatedElement element,
|
||||
SearchStrategy searchStrategy, RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter) {
|
||||
return TypeMappedAnnotations.from(element, searchStrategy, repeatableContainers,
|
||||
annotationFilter);
|
||||
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
return TypeMappedAnnotations.from(element, searchStrategy, repeatableContainers, annotationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,8 +338,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @see #from(AnnotatedElement)
|
||||
*/
|
||||
static MergedAnnotations from(@Nullable Object source, Annotation... annotations) {
|
||||
return from(source, annotations, RepeatableContainers.standardRepeatables(),
|
||||
AnnotationFilter.PLAIN);
|
||||
return from(source, annotations, RepeatableContainers.standardRepeatables(), AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,10 +355,9 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @return a {@link MergedAnnotations} instance containing the annotations
|
||||
*/
|
||||
static MergedAnnotations from(@Nullable Object source, Annotation[] annotations,
|
||||
RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter) {
|
||||
return TypeMappedAnnotations.from(source, annotations, repeatableContainers,
|
||||
annotationFilter);
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
return TypeMappedAnnotations.from(source, annotations, repeatableContainers, annotationFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +400,6 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* not need to be meta-annotated with {@link Inherited @Inherited}.
|
||||
*/
|
||||
EXHAUSTIVE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ import org.springframework.lang.Nullable;
|
||||
* @since 5.2
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
final class MissingMergedAnnotation<A extends Annotation>
|
||||
extends AbstractMergedAnnotation<A> {
|
||||
final class MissingMergedAnnotation<A extends Annotation> extends AbstractMergedAnnotation<A> {
|
||||
|
||||
private static final MissingMergedAnnotation<?> INSTANCE = new MissingMergedAnnotation<>();
|
||||
|
||||
@@ -153,6 +152,7 @@ final class MissingMergedAnnotation<A extends Annotation>
|
||||
throw new NoSuchElementException("Unable to synthesize missing annotation");
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <A extends Annotation> MergedAnnotation<A> getInstance() {
|
||||
return (MergedAnnotation<A>) INSTANCE;
|
||||
|
||||
@@ -92,8 +92,8 @@ public abstract class OrderUtils {
|
||||
* @param annotations the annotation to consider
|
||||
* @return the order value, or {@code null} if none can be found
|
||||
*/
|
||||
static Integer getOrderFromAnnotations(AnnotatedElement element,
|
||||
MergedAnnotations annotations) {
|
||||
@Nullable
|
||||
static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) {
|
||||
if (!(element instanceof Class)) {
|
||||
return findOrder(annotations);
|
||||
}
|
||||
@@ -106,6 +106,7 @@ public abstract class OrderUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Integer findOrder(MergedAnnotations annotations) {
|
||||
MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
|
||||
if (orderAnnotation.isPresent()) {
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Phillip Webb
|
||||
* @since 5.2
|
||||
*/
|
||||
class PackagesAnnotationFilter implements AnnotationFilter {
|
||||
final class PackagesAnnotationFilter implements AnnotationFilter {
|
||||
|
||||
private final String[] prefixes;
|
||||
|
||||
@@ -40,8 +40,9 @@ class PackagesAnnotationFilter implements AnnotationFilter {
|
||||
Assert.notNull(packages, "Packages must not be null");
|
||||
this.prefixes = new String[packages.length];
|
||||
for (int i = 0; i < packages.length; i++) {
|
||||
Assert.hasText(packages[i], "Package must not have empty elements");
|
||||
this.prefixes[i] = packages[i] + ".";
|
||||
String pkg = packages[i];
|
||||
Assert.hasText(pkg, "Package must not have empty elements");
|
||||
this.prefixes[i] = pkg + ".";
|
||||
}
|
||||
Arrays.sort(this.prefixes);
|
||||
this.hashCode = Arrays.hashCode(this.prefixes);
|
||||
@@ -49,27 +50,25 @@ class PackagesAnnotationFilter implements AnnotationFilter {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean matches(@Nullable String annotationType) {
|
||||
if (annotationType != null) {
|
||||
for (String prefix : this.prefixes) {
|
||||
if (annotationType.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
public boolean matches(String annotationType) {
|
||||
for (String prefix : this.prefixes) {
|
||||
if (annotationType.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PackagesAnnotationFilter other = (PackagesAnnotationFilter) obj;
|
||||
return Arrays.equals(this.prefixes, other.prefixes);
|
||||
return Arrays.equals(this.prefixes, ((PackagesAnnotationFilter) other).prefixes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -73,16 +73,16 @@ public abstract class RepeatableContainers {
|
||||
return this.parent.findRepeatedAnnotations(annotation);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj == this) {
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
RepeatableContainers other = (RepeatableContainers) obj;
|
||||
return Objects.equals(this.parent, other.parent);
|
||||
return Objects.equals(this.parent, ((RepeatableContainers) other).parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,6 +90,7 @@ public abstract class RepeatableContainers {
|
||||
return ObjectUtils.nullSafeHashCode(this.parent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link RepeatableContainers} instance that searches using Java's
|
||||
* {@link Repeatable @Repeatable} annotation.
|
||||
@@ -198,10 +199,8 @@ public abstract class RepeatableContainers {
|
||||
|
||||
private final Method valueMethod;
|
||||
|
||||
|
||||
ExplicitRepeatableContainer(@Nullable RepeatableContainers parent,
|
||||
Class<? extends Annotation> repeatable,
|
||||
@Nullable Class<? extends Annotation> container) {
|
||||
Class<? extends Annotation> repeatable, @Nullable Class<? extends Annotation> container) {
|
||||
|
||||
super(parent);
|
||||
Assert.notNull(repeatable, "Repeatable must not be null");
|
||||
@@ -235,11 +234,9 @@ public abstract class RepeatableContainers {
|
||||
this.valueMethod = valueMethod;
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> deduceContainer(
|
||||
Class<? extends Annotation> repeatable) {
|
||||
|
||||
private Class<? extends Annotation> deduceContainer(Class<? extends Annotation> repeatable) {
|
||||
Repeatable annotation = repeatable.getAnnotation(Repeatable.class);
|
||||
Assert.notNull(annotation, "Annotation type must be a repeatable annotation: " +
|
||||
Assert.notNull(annotation, () -> "Annotation type must be a repeatable annotation: " +
|
||||
"failed to resolve container type for " + repeatable.getName());
|
||||
return annotation.value();
|
||||
}
|
||||
@@ -254,13 +251,12 @@ public abstract class RepeatableContainers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (super.equals(obj)) {
|
||||
ExplicitRepeatableContainer other = (ExplicitRepeatableContainer) obj;
|
||||
return this.container.equals(other.container) &&
|
||||
this.repeatable.equals(other.repeatable);
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
ExplicitRepeatableContainer otherErc = (ExplicitRepeatableContainer) other;
|
||||
return (this.container.equals(otherErc.container) && this.repeatable.equals(otherErc.repeatable));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -270,7 +266,6 @@ public abstract class RepeatableContainers {
|
||||
hashCode = 31 * hashCode + this.repeatable.hashCode();
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +279,6 @@ public abstract class RepeatableContainers {
|
||||
NoRepeatableContainers() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} for an {@link Annotation} that Spring has
|
||||
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
|
||||
* functionality.
|
||||
* <em>synthesized</em> (i.e. wrapped in a dynamic proxy) with additional
|
||||
* functionality such as attribute alias handling.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
@@ -44,8 +44,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @see AnnotationAttributeExtractor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
*/
|
||||
class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation>
|
||||
implements InvocationHandler {
|
||||
final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> implements InvocationHandler {
|
||||
|
||||
private final MergedAnnotation<?> annotation;
|
||||
|
||||
@@ -57,10 +56,8 @@ class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation>
|
||||
private volatile Integer hashCode;
|
||||
|
||||
|
||||
private SynthesizedMergedAnnotationInvocationHandler(MergedAnnotation<A> annotation,
|
||||
Class<A> type) {
|
||||
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
private SynthesizedMergedAnnotationInvocationHandler(MergedAnnotation<A> annotation, Class<A> type) {
|
||||
Assert.notNull(annotation, "MergedAnnotation must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.isTrue(type.isAnnotation(), "Type must be an annotation");
|
||||
this.annotation = annotation;
|
||||
@@ -90,18 +87,15 @@ class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation>
|
||||
return getAttributeValue(method);
|
||||
}
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"Method [%s] is unsupported for synthesized annotation type [%s]", method,
|
||||
this.type));
|
||||
"Method [%s] is unsupported for synthesized annotation type [%s]", method, this.type));
|
||||
}
|
||||
|
||||
private boolean isAnnotationTypeMethod(Method method) {
|
||||
return Objects.equals(method.getName(), "annotationType")
|
||||
&& method.getParameterCount() == 0;
|
||||
return (Objects.equals(method.getName(), "annotationType") && method.getParameterCount() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Annotation#equals(Object)} for a definition of the required
|
||||
* algorithm.
|
||||
* See {@link Annotation#equals(Object)} for a definition of the required algorithm.
|
||||
* @param other the other object to compare against
|
||||
*/
|
||||
private boolean annotationEquals(Object other) {
|
||||
@@ -123,8 +117,7 @@ class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation>
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Annotation#hashCode()} for a definition of the required
|
||||
* algorithm.
|
||||
* See {@link Annotation#hashCode()} for a definition of the required algorithm.
|
||||
*/
|
||||
private int annotationHashCode() {
|
||||
Integer hashCode = this.hashCode;
|
||||
@@ -182,26 +175,22 @@ class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation>
|
||||
String name = method.getName();
|
||||
Class<?> type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType());
|
||||
return this.annotation.getValue(name, type).orElseThrow(
|
||||
() -> new NoSuchElementException("No value found for attribute named '"
|
||||
+ name + "' in merged annotation " + this.annotation.getType()));
|
||||
() -> new NoSuchElementException("No value found for attribute named '" + name +
|
||||
"' in merged annotation " + this.annotation.getType()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation,
|
||||
Class<A> type) {
|
||||
static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation, Class<A> type) {
|
||||
ClassLoader classLoader = type.getClassLoader();
|
||||
InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(
|
||||
annotation, type);
|
||||
Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class)
|
||||
? new Class<?>[] { type, SynthesizedAnnotation.class }
|
||||
: new Class<?>[] { type };
|
||||
InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(annotation, type);
|
||||
Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class) ?
|
||||
new Class<?>[] {type, SynthesizedAnnotation.class} : new Class<?>[] {type};
|
||||
return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||
}
|
||||
|
||||
private static boolean isVisible(ClassLoader classLoader, Class<?> interfaceClass) {
|
||||
try {
|
||||
return Class.forName(interfaceClass.getName(), false,
|
||||
classLoader) == interfaceClass;
|
||||
return Class.forName(interfaceClass.getName(), false, classLoader) == interfaceClass;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return false;
|
||||
|
||||
@@ -190,13 +190,12 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName,
|
||||
Class<T> type) throws NoSuchElementException {
|
||||
public <T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName, Class<T> type)
|
||||
throws NoSuchElementException {
|
||||
|
||||
Assert.notNull(attributeName, "AttributeName must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
int attributeIndex = getAttributeIndex(attributeName, true);
|
||||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.isAssignable(type, attribute.getReturnType(),
|
||||
"Attribute " + attributeName + " type mismatch:");
|
||||
return (MergedAnnotation<T>) getRequiredValue(attributeIndex, Object.class);
|
||||
@@ -207,13 +206,12 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
public <T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(
|
||||
String attributeName, Class<T> type) throws NoSuchElementException {
|
||||
|
||||
Assert.notNull(attributeName, "AttributeName must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
int attributeIndex = getAttributeIndex(attributeName, true);
|
||||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||
Class<?> componentType = attribute.getReturnType().getComponentType();
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.notNull(componentType, () -> "Attribute " + attributeName + " is not an array");
|
||||
Assert.isAssignable(type, componentType, "Attribute " + attributeName + " component type mismatch:");
|
||||
Assert.isAssignable(type, componentType, () -> "Attribute " + attributeName + " component type mismatch:");
|
||||
return (MergedAnnotation<T>[]) getRequiredValue(attributeIndex, Object.class);
|
||||
}
|
||||
|
||||
@@ -229,7 +227,6 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
|
||||
@Override
|
||||
public MergedAnnotation<A> filterAttributes(Predicate<String> predicate) {
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
if (this.attributeFilter != null) {
|
||||
predicate = this.attributeFilter.and(predicate);
|
||||
}
|
||||
@@ -250,9 +247,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
public <T extends Map<String, Object>> T asMap(
|
||||
@Nullable Function<MergedAnnotation<?>, T> factory, MapValues... options) {
|
||||
|
||||
T map = factory != null ? factory.apply(this) : (T) new LinkedHashMap<String, Object>();
|
||||
Assert.state(map != null,
|
||||
"Factory used to create MergedAnnotation Map must not return null;");
|
||||
T map = (factory != null ? factory.apply(this) : (T) new LinkedHashMap<String, Object>());
|
||||
Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null");
|
||||
AttributeMethods attributes = this.mapping.getAttributes();
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
Method attribute = attributes.get(i);
|
||||
@@ -361,14 +357,13 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
@Nullable
|
||||
protected <T> T getAttributeValue(String attributeName, Class<T> type) {
|
||||
int attributeIndex = getAttributeIndex(attributeName, false);
|
||||
return attributeIndex != -1 ? getValue(attributeIndex, type) : null;
|
||||
return (attributeIndex != -1 ? getValue(attributeIndex, type) : null);
|
||||
}
|
||||
|
||||
protected final <T> T getRequiredValue(int attributeIndex, Class<T> type) {
|
||||
T value = getValue(attributeIndex, type);
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException(
|
||||
"No element at attribute index " + attributeIndex);
|
||||
throw new NoSuchElementException("No element at attribute index " + attributeIndex);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -457,8 +452,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
MergedAnnotation<?> annotation = (MergedAnnotation<?>) value;
|
||||
value = annotation.synthesize();
|
||||
}
|
||||
else if (value instanceof MergedAnnotation[] && type.isArray()
|
||||
&& type.getComponentType().isAnnotation()) {
|
||||
else if (value instanceof MergedAnnotation[] && type.isArray() && type.getComponentType().isAnnotation()) {
|
||||
MergedAnnotation<?>[] annotations = (MergedAnnotation<?>[]) value;
|
||||
Object array = Array.newInstance(type.getComponentType(), annotations.length);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
@@ -484,8 +478,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
if (attributeType.isAnnotation()) {
|
||||
return adaptToMergedAnnotation(value,(Class<? extends Annotation>) attributeType);
|
||||
}
|
||||
if (attributeType.isArray() &&
|
||||
attributeType.getComponentType().isAnnotation() &&
|
||||
if (attributeType.isArray() && attributeType.getComponentType().isAnnotation() &&
|
||||
value.getClass().isArray()) {
|
||||
MergedAnnotation<?>[] result = new MergedAnnotation<?>[Array.getLength(value)];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
@@ -539,10 +532,9 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
}
|
||||
|
||||
private int getAttributeIndex(String attributeName, boolean required) {
|
||||
Assert.hasText(attributeName, "AttributeName must not be null");
|
||||
int attributeIndex = isFiltered(attributeName) ?
|
||||
-1 :
|
||||
this.mapping.getAttributes().indexOf(attributeName);
|
||||
Assert.hasText(attributeName, "Attribute name must not be null");
|
||||
int attributeIndex = (isFiltered(attributeName) ? -1 :
|
||||
this.mapping.getAttributes().indexOf(attributeName));
|
||||
if (attributeIndex == -1 && required) {
|
||||
throw new NoSuchElementException("No attribute named '" + attributeName +
|
||||
"' present in merged annotation " + getType());
|
||||
@@ -562,20 +554,16 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
return (Class<A>) this.mapping.getAnnotationType();
|
||||
}
|
||||
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source,
|
||||
A annotation) {
|
||||
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType());
|
||||
return new TypeMappedAnnotation<>(mappings.get(0), source, annotation,
|
||||
ReflectionUtils::invokeMethod, 0);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
|
||||
return new TypeMappedAnnotation<>(mappings.get(0), source, annotation, ReflectionUtils::invokeMethod, 0);
|
||||
}
|
||||
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source,
|
||||
Class<A> annotationType, @Nullable Map<String, ?> attributes) {
|
||||
|
||||
Assert.notNull(annotationType, "AnnotationType must not be null");
|
||||
Assert.notNull(annotationType, "Annotation type must not be null");
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotationType);
|
||||
return new TypeMappedAnnotation<>(mappings.get(0), source, attributes,
|
||||
TypeMappedAnnotation::extractFromMap, 0);
|
||||
@@ -596,9 +584,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
}
|
||||
if (logger.isEnabled()) {
|
||||
String type = mapping.getAnnotationType().getName();
|
||||
String item = mapping.getDepth() == 0 ?
|
||||
"annotation " + type :
|
||||
"meta-annotation " + type + " from " + mapping.getRoot().getAnnotationType().getName();
|
||||
String item = (mapping.getDepth() == 0 ? "annotation " + type :
|
||||
"meta-annotation " + type + " from " + mapping.getRoot().getAnnotationType().getName());
|
||||
logger.log("Failed to introspect " + item, source, ex);
|
||||
}
|
||||
return null;
|
||||
@@ -608,7 +595,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private static Object extractFromMap(Method attribute, @Nullable Object map) {
|
||||
return map != null ? ((Map<String, ?>) map).get(attribute.getName()) : null;
|
||||
return (map != null ? ((Map<String, ?>) map).get(attribute.getName()) : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ import org.springframework.lang.Nullable;
|
||||
* annotations and meta-annotations using {@link AnnotationTypeMappings}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 5.1
|
||||
* @since 5.2
|
||||
*/
|
||||
final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
private static final AnnotationFilter FILTER_ALL = annotationType -> true;
|
||||
|
||||
private static final MergedAnnotations NONE = new TypeMappedAnnotations(null,
|
||||
new Annotation[0], RepeatableContainers.none(), FILTER_ALL);
|
||||
private static final MergedAnnotations NONE = new TypeMappedAnnotations(
|
||||
null, new Annotation[0], RepeatableContainers.none(), FILTER_ALL);
|
||||
|
||||
|
||||
@Nullable
|
||||
@@ -67,8 +67,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
|
||||
private TypeMappedAnnotations(AnnotatedElement element, SearchStrategy searchStrategy,
|
||||
RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter) {
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
this.source = element;
|
||||
this.element = element;
|
||||
@@ -79,8 +78,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
private TypeMappedAnnotations(@Nullable Object source, Annotation[] annotations,
|
||||
RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter) {
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
this.source = source;
|
||||
this.element = null;
|
||||
@@ -92,8 +90,8 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> boolean isPresent(@Nullable Class<A> annotationType) {
|
||||
if (annotationType == null || this.annotationFilter.matches(annotationType)) {
|
||||
public <A extends Annotation> boolean isPresent(Class<A> annotationType) {
|
||||
if (this.annotationFilter.matches(annotationType)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.TRUE.equals(scan(annotationType,
|
||||
@@ -101,8 +99,8 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent(@Nullable String annotationType) {
|
||||
if (annotationType == null || this.annotationFilter.matches(annotationType)) {
|
||||
public boolean isPresent(String annotationType) {
|
||||
if (this.annotationFilter.matches(annotationType)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.TRUE.equals(scan(annotationType,
|
||||
@@ -119,8 +117,8 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectlyPresent(@Nullable String annotationType) {
|
||||
if (annotationType == null || this.annotationFilter.matches(annotationType)) {
|
||||
public boolean isDirectlyPresent(String annotationType) {
|
||||
if (this.annotationFilter.matches(annotationType)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.TRUE.equals(scan(annotationType,
|
||||
@@ -128,57 +126,53 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(
|
||||
@Nullable Class<A> annotationType) {
|
||||
|
||||
public <A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType) {
|
||||
return get(annotationType, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(
|
||||
@Nullable Class<A> annotationType,
|
||||
public <A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate) {
|
||||
|
||||
return get(annotationType, predicate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(
|
||||
@Nullable Class<A> annotationType,
|
||||
public <A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector) {
|
||||
|
||||
if (annotationType == null || this.annotationFilter.matches(annotationType)) {
|
||||
if (this.annotationFilter.matches(annotationType)) {
|
||||
return MergedAnnotation.missing();
|
||||
}
|
||||
MergedAnnotation<A> result = scan(annotationType,
|
||||
new MergedAnnotationFinder<>(annotationType, predicate, selector));
|
||||
return result != null ? result : MergedAnnotation.missing();
|
||||
return (result != null ? result : MergedAnnotation.missing());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(
|
||||
@Nullable String annotationType) {
|
||||
public <A extends Annotation> MergedAnnotation<A> get(String annotationType) {
|
||||
return get(annotationType, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(@Nullable String annotationType,
|
||||
public <A extends Annotation> MergedAnnotation<A> get(String annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate) {
|
||||
|
||||
return get(annotationType, predicate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(@Nullable String annotationType,
|
||||
public <A extends Annotation> MergedAnnotation<A> get(String annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector) {
|
||||
|
||||
if (annotationType == null || this.annotationFilter.matches(annotationType)) {
|
||||
if (this.annotationFilter.matches(annotationType)) {
|
||||
return MergedAnnotation.missing();
|
||||
}
|
||||
MergedAnnotation<A> result = scan(annotationType,
|
||||
new MergedAnnotationFinder<>(annotationType, predicate, selector));
|
||||
return result != null ? result : MergedAnnotation.missing();
|
||||
return (result != null ? result : MergedAnnotation.missing());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -261,13 +255,11 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
if (element == null || AnnotationsScanner.isKnownEmpty(element, searchStrategy, annotationFilter)) {
|
||||
return NONE;
|
||||
}
|
||||
return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers,
|
||||
annotationFilter);
|
||||
return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers, annotationFilter);
|
||||
}
|
||||
|
||||
static MergedAnnotations from(@Nullable Object source,
|
||||
Annotation[] annotations, RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter) {
|
||||
static MergedAnnotations from(@Nullable Object source, Annotation[] annotations,
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
if (annotations.length == 0) {
|
||||
return NONE;
|
||||
@@ -282,8 +274,8 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
return false;
|
||||
}
|
||||
Class<? extends Annotation> actualType = mapping.getAnnotationType();
|
||||
return !annotationFilter.matches(actualType) &&
|
||||
(requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType));
|
||||
return (!annotationFilter.matches(actualType) &&
|
||||
(requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType)));
|
||||
}
|
||||
|
||||
|
||||
@@ -291,8 +283,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
* {@link AnnotationsProcessor} used to detect if an annotation is directly
|
||||
* or meta-present.
|
||||
*/
|
||||
private static final class IsPresent
|
||||
implements AnnotationsProcessor<Object, Boolean> {
|
||||
private static final class IsPresent implements AnnotationsProcessor<Object, Boolean> {
|
||||
|
||||
/**
|
||||
* Shared instances that save us needing to create a new processor for
|
||||
@@ -307,14 +298,12 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
SHARED[3] = new IsPresent(RepeatableContainers.standardRepeatables(), AnnotationFilter.PLAIN, false);
|
||||
}
|
||||
|
||||
|
||||
private final RepeatableContainers repeatableContainers;
|
||||
|
||||
private final AnnotationFilter annotationFilter;
|
||||
|
||||
private final boolean directOnly;
|
||||
|
||||
|
||||
private IsPresent(RepeatableContainers repeatableContainers,
|
||||
AnnotationFilter annotationFilter, boolean directOnly) {
|
||||
|
||||
@@ -323,7 +312,6 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
this.directOnly = directOnly;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Boolean doWithAnnotations(Object requiredType, int aggregateIndex,
|
||||
@@ -374,7 +362,6 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
return new IsPresent(repeatableContainers, annotationFilter, directOnly);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -395,18 +382,14 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
@Nullable
|
||||
private MergedAnnotation<A> result;
|
||||
|
||||
|
||||
MergedAnnotationFinder(Object requiredType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
MergedAnnotationFinder(Object requiredType, @Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector) {
|
||||
|
||||
this.requiredType = requiredType;
|
||||
this.predicate = predicate;
|
||||
this.selector = selector != null ? selector
|
||||
: MergedAnnotationSelectors.nearest();
|
||||
this.selector = (selector != null ? selector : MergedAnnotationSelectors.nearest());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MergedAnnotation<A> doWithAggregate(Object context, int aggregateIndex) {
|
||||
@@ -431,26 +414,21 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MergedAnnotation<A> process(Object type, int aggregateIndex,
|
||||
@Nullable Object source, Annotation annotation) {
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(
|
||||
annotation);
|
||||
private MergedAnnotation<A> process(
|
||||
Object type, int aggregateIndex, @Nullable Object source, Annotation annotation) {
|
||||
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
return doWithAnnotations(type, aggregateIndex, source,
|
||||
repeatedAnnotations);
|
||||
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
}
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType(),
|
||||
annotationFilter);
|
||||
annotation.annotationType(), annotationFilter);
|
||||
for (int i = 0; i < mappings.size(); i++) {
|
||||
AnnotationTypeMapping mapping = mappings.get(i);
|
||||
if (isMappingForType(mapping, annotationFilter,
|
||||
this.requiredType)) {
|
||||
if (isMappingForType(mapping, annotationFilter, this.requiredType)) {
|
||||
MergedAnnotation<A> candidate = TypeMappedAnnotation.createIfPossible(
|
||||
mapping, source, annotation, aggregateIndex,
|
||||
IntrospectionFailureLogger.INFO);
|
||||
if (candidate != null && (this.predicate == null
|
||||
|| this.predicate.test(candidate))) {
|
||||
mapping, source, annotation, aggregateIndex, IntrospectionFailureLogger.INFO);
|
||||
if (candidate != null && (this.predicate == null || this.predicate.test(candidate))) {
|
||||
if (this.selector.isBestCandidate(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
@@ -463,24 +441,21 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
private void updateLastResult(MergedAnnotation<A> candidate) {
|
||||
MergedAnnotation<A> lastResult = this.result;
|
||||
this.result = lastResult != null
|
||||
? this.selector.select(lastResult, candidate)
|
||||
: candidate;
|
||||
this.result = (lastResult != null ? this.selector.select(lastResult, candidate) : candidate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MergedAnnotation<A> finish(@Nullable MergedAnnotation<A> result) {
|
||||
return result != null ? result : this.result;
|
||||
return (result != null ? result : this.result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link AnnotationsProcessor} that collects {@link Aggregate} instances.
|
||||
*/
|
||||
private class AggregatesCollector
|
||||
implements AnnotationsProcessor<Object, List<Aggregate>> {
|
||||
private class AggregatesCollector implements AnnotationsProcessor<Object, List<Aggregate>> {
|
||||
|
||||
private final List<Aggregate> aggregates = new ArrayList<>();
|
||||
|
||||
@@ -488,12 +463,12 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
@Nullable
|
||||
public List<Aggregate> doWithAnnotations(Object criteria, int aggregateIndex,
|
||||
@Nullable Object source, Annotation[] annotations) {
|
||||
|
||||
this.aggregates.add(createAggregate(aggregateIndex, source, annotations));
|
||||
return null;
|
||||
}
|
||||
|
||||
private Aggregate createAggregate(int aggregateIndex, @Nullable Object source,
|
||||
Annotation[] annotations) {
|
||||
private Aggregate createAggregate(int aggregateIndex, @Nullable Object source, Annotation[] annotations) {
|
||||
List<Annotation> aggregateAnnotations = getAggregateAnnotations(annotations);
|
||||
return new Aggregate(aggregateIndex, source, aggregateAnnotations);
|
||||
}
|
||||
@@ -504,17 +479,12 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addAggregateAnnotations(List<Annotation> aggregateAnnotations,
|
||||
Annotation[] annotations) {
|
||||
private void addAggregateAnnotations(List<Annotation> aggregateAnnotations, Annotation[] annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation != null
|
||||
&& !annotationFilter.matches(
|
||||
annotation)) {
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(
|
||||
annotation);
|
||||
if (annotation != null && !annotationFilter.matches(annotation)) {
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
addAggregateAnnotations(aggregateAnnotations,
|
||||
repeatedAnnotations);
|
||||
addAggregateAnnotations(aggregateAnnotations, repeatedAnnotations);
|
||||
}
|
||||
else {
|
||||
aggregateAnnotations.add(annotation);
|
||||
@@ -527,9 +497,9 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
public List<Aggregate> finish(@Nullable List<Aggregate> processResult) {
|
||||
return this.aggregates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class Aggregate {
|
||||
|
||||
private final int aggregateIndex;
|
||||
@@ -541,8 +511,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
private final AnnotationTypeMappings[] mappings;
|
||||
|
||||
Aggregate(int aggregateIndex, @Nullable Object source,
|
||||
List<Annotation> annotations) {
|
||||
Aggregate(int aggregateIndex, @Nullable Object source, List<Annotation> annotations) {
|
||||
this.aggregateIndex = aggregateIndex;
|
||||
this.source = source;
|
||||
this.annotations = annotations;
|
||||
@@ -575,15 +544,14 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
this.mappings[annotationIndex].get(mappingIndex), this.source,
|
||||
this.annotations.get(annotationIndex), this.aggregateIndex, logger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link Spliterator} used to consume merged annotations from the
|
||||
* aggregates in depth fist order.
|
||||
*/
|
||||
private class AggregatesSpliterator<A extends Annotation>
|
||||
implements Spliterator<MergedAnnotation<A>> {
|
||||
private class AggregatesSpliterator<A extends Annotation> implements Spliterator<MergedAnnotation<A>> {
|
||||
|
||||
@Nullable
|
||||
private final Object requiredType;
|
||||
@@ -595,8 +563,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
@Nullable
|
||||
private int[] mappingCursors;
|
||||
|
||||
AggregatesSpliterator(@Nullable Object requiredType,
|
||||
List<Aggregate> aggregates) {
|
||||
AggregatesSpliterator(@Nullable Object requiredType, List<Aggregate> aggregates) {
|
||||
this.requiredType = requiredType;
|
||||
this.aggregates = aggregates;
|
||||
this.aggregateCursor = 0;
|
||||
@@ -614,8 +581,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean tryAdvance(Aggregate aggregate,
|
||||
Consumer<? super MergedAnnotation<A>> action) {
|
||||
private boolean tryAdvance(Aggregate aggregate, Consumer<? super MergedAnnotation<A>> action) {
|
||||
if (this.mappingCursors == null) {
|
||||
this.mappingCursors = new int[aggregate.size()];
|
||||
}
|
||||
@@ -646,8 +612,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AnnotationTypeMapping getNextSuitableMapping(Aggregate aggregate,
|
||||
int annotationIndex) {
|
||||
private AnnotationTypeMapping getNextSuitableMapping(Aggregate aggregate, int annotationIndex) {
|
||||
int[] cursors = this.mappingCursors;
|
||||
if (cursors != null) {
|
||||
AnnotationTypeMapping mapping;
|
||||
@@ -691,7 +656,6 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
public int characteristics() {
|
||||
return NONNULL | IMMUTABLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.Reference;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap.Restructure;
|
||||
|
||||
/**
|
||||
* A {@link ConcurrentHashMap} that uses {@link ReferenceType#SOFT soft} or
|
||||
@@ -579,7 +577,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
}
|
||||
}
|
||||
|
||||
private void restructure(boolean allowResize, Reference<K, V> ref) {
|
||||
private void restructure(boolean allowResize, @Nullable Reference<K, V> ref) {
|
||||
boolean needsResize;
|
||||
lock();
|
||||
try {
|
||||
|
||||
@@ -700,17 +700,12 @@ public class AnnotatedElementUtilsTests {
|
||||
assertArrayEquals("path attribute: ", asArray("/test"), webMapping.path());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
Constructor<?> deprecatedCtor = Date.class.getConstructor(String.class);
|
||||
assertEquals(deprecatedCtor.getAnnotation(Deprecated.class), findMergedAnnotation(deprecatedCtor, Deprecated.class));
|
||||
assertEquals(Date.class.getAnnotation(Deprecated.class), findMergedAnnotation(Date.class, Deprecated.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaxAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
assertEquals(ResourceHolder.class.getAnnotation(Resource.class), findMergedAnnotation(ResourceHolder.class, Resource.class));
|
||||
assertEquals(SpringAppConfigClass.class.getAnnotation(Resource.class), findMergedAnnotation(SpringAppConfigClass.class, Resource.class));
|
||||
assertEquals(ResourceHolder.class.getAnnotation(Resource.class),
|
||||
findMergedAnnotation(ResourceHolder.class, Resource.class));
|
||||
assertEquals(SpringAppConfigClass.class.getAnnotation(Resource.class),
|
||||
findMergedAnnotation(SpringAppConfigClass.class, Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,19 +16,16 @@
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnotationFilter}.
|
||||
@@ -37,35 +34,22 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*/
|
||||
public class AnnotationFilterTests {
|
||||
|
||||
private static final AnnotationFilter FILTER = annotationType -> ObjectUtils.nullSafeEquals(
|
||||
annotationType, TestAnnotation.class.getName());
|
||||
private static final AnnotationFilter FILTER = annotationType ->
|
||||
ObjectUtils.nullSafeEquals(annotationType, TestAnnotation.class.getName());
|
||||
|
||||
@Test
|
||||
public void matchesAnnotationWhenAnnotationIsNullReturnsFalse() {
|
||||
TestAnnotation annotation = null;
|
||||
assertThat(FILTER.matches(annotation)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesAnnotationWhenMatchReturnsTrue() {
|
||||
TestAnnotation annotation = WithTestAnnotation.class.getDeclaredAnnotation(
|
||||
TestAnnotation.class);
|
||||
TestAnnotation annotation = WithTestAnnotation.class.getDeclaredAnnotation(TestAnnotation.class);
|
||||
assertThat(FILTER.matches(annotation)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesAnnotationWhenNoMatchReturnsFalse() {
|
||||
OtherAnnotation annotation = WithOtherAnnotation.class.getDeclaredAnnotation(
|
||||
OtherAnnotation.class);
|
||||
OtherAnnotation annotation = WithOtherAnnotation.class.getDeclaredAnnotation(OtherAnnotation.class);
|
||||
assertThat(FILTER.matches(annotation)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesAnnotationClassWhenAnnotationClassIsNullReturnsFalse() {
|
||||
Class<Annotation> annotationType = null;
|
||||
assertThat(FILTER.matches(annotationType)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesAnnotationClassWhenMatchReturnsTrue() {
|
||||
Class<TestAnnotation> annotationType = TestAnnotation.class;
|
||||
@@ -109,27 +93,15 @@ public class AnnotationFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noneWhenNonNullReturnsFalse() {
|
||||
public void noneReturnsFalse() {
|
||||
assertThat(AnnotationFilter.NONE.matches(Retention.class)).isFalse();
|
||||
assertThat(AnnotationFilter.NONE.matches(Nullable.class)).isFalse();
|
||||
assertThat(AnnotationFilter.NONE.matches(TestAnnotation.class)).isFalse();
|
||||
assertThat(AnnotationFilter.NONE.matches((Annotation) null)).isFalse();
|
||||
assertThat(AnnotationFilter.NONE.matches((Class<Annotation>) null)).isFalse();
|
||||
assertThat(AnnotationFilter.NONE.matches((String) null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pacakgesReturnsPackagesAnnotationFilter() {
|
||||
assertThat(AnnotationFilter.packages("com.example")).isInstanceOf(
|
||||
PackagesAnnotationFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForCollectionWhenAnnotationTypesIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> AnnotationFilter.mostAppropriateFor(
|
||||
(Collection<Class<? extends Annotation>>) null)).withMessage(
|
||||
"AnnotationTypes must not be null");
|
||||
assertThat(AnnotationFilter.packages("com.example")).isInstanceOf(PackagesAnnotationFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,14 +118,6 @@ public class AnnotationFilterTests {
|
||||
assertThat(filter).isSameAs(AnnotationFilter.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForArrayWhenAnnotationTypesIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> AnnotationFilter.mostAppropriateFor(
|
||||
(Class<? extends Annotation>[]) null)).withMessage(
|
||||
"AnnotationTypes must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mostAppropriateForArrayReturnsPlainWhenPossible() {
|
||||
AnnotationFilter filter = AnnotationFilter.mostAppropriateFor(
|
||||
@@ -168,24 +132,21 @@ public class AnnotationFilterTests {
|
||||
assertThat(filter).isSameAs(AnnotationFilter.NONE);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TestAnnotation {
|
||||
}
|
||||
|
||||
@TestAnnotation
|
||||
static class WithTestAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface OtherAnnotation {
|
||||
|
||||
@interface OtherAnnotation {
|
||||
}
|
||||
|
||||
@OtherAnnotation
|
||||
static class WithOtherAnnotation {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -47,35 +46,24 @@ import static org.assertj.core.api.Assertions.*;
|
||||
*/
|
||||
public class AnnotationTypeMappingsTests {
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenAnnotationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> AnnotationTypeMappings.forAnnotationType(null)).withMessage(
|
||||
"AnnotationType must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenNoMetaAnnotationsReturnsMappings() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
SimpleAnnotation.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(SimpleAnnotation.class);
|
||||
assertThat(mappings.size()).isEqualTo(1);
|
||||
assertThat(mappings.get(0).getAnnotationType()).isEqualTo(SimpleAnnotation.class);
|
||||
assertThat(getAll(mappings)).flatExtracting(
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(
|
||||
SimpleAnnotation.class);
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(SimpleAnnotation.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forAnnotationWhenHasSpringAnnotationReturnsFilteredMappings() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
WithSpringLangAnnotation.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(WithSpringLangAnnotation.class);
|
||||
assertThat(mappings.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenMetaAnnotationsReturnsMappings() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
MetaAnnotated.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(MetaAnnotated.class);
|
||||
assertThat(mappings.size()).isEqualTo(6);
|
||||
assertThat(getAll(mappings)).flatExtracting(
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(
|
||||
@@ -85,33 +73,27 @@ public class AnnotationTypeMappingsTests {
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenHasRepeatingMetaAnnotationReturnsMapping() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
WithRepeatedMetaAnnotations.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(WithRepeatedMetaAnnotations.class);
|
||||
assertThat(mappings.size()).isEqualTo(3);
|
||||
assertThat(getAll(mappings)).flatExtracting(
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(
|
||||
WithRepeatedMetaAnnotations.class, Repeating.class,
|
||||
Repeating.class);
|
||||
WithRepeatedMetaAnnotations.class, Repeating.class, Repeating.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenSelfAnnotatedReturnsMapping() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
SelfAnnotated.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(SelfAnnotated.class);
|
||||
assertThat(mappings.size()).isEqualTo(1);
|
||||
assertThat(getAll(mappings)).flatExtracting(
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(
|
||||
SelfAnnotated.class);
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(SelfAnnotated.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forAnnotationTypeWhenFormsLoopReturnsMapping() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
LoopA.class);
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(LoopA.class);
|
||||
assertThat(mappings.size()).isEqualTo(2);
|
||||
assertThat(getAll(mappings)).flatExtracting(
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(LoopA.class,
|
||||
LoopB.class);
|
||||
AnnotationTypeMapping::getAnnotationType).containsExactly(LoopA.class, LoopB.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -540,13 +540,6 @@ public class AnnotationUtilsTests {
|
||||
assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findRepeatableAnnotationOnComposedAnnotation() {
|
||||
Repeatable repeatable = findAnnotation(MyRepeatableMeta1.class, Repeatable.class);
|
||||
assertNotNull(repeatable);
|
||||
assertEquals(MyRepeatableContainer.class, repeatable.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableAnnotationsDeclaredOnMethod() throws Exception {
|
||||
Method method = InterfaceWithRepeated.class.getMethod("foo");
|
||||
|
||||
@@ -511,6 +511,7 @@ public class AnnotationsScannerTests {
|
||||
assertThat(result).isEqualTo("OK");
|
||||
}
|
||||
|
||||
|
||||
private Method methodFrom(Class<?> type) {
|
||||
return ReflectionUtils.findMethod(type, "method");
|
||||
}
|
||||
@@ -532,81 +533,68 @@ public class AnnotationsScannerTests {
|
||||
return result.stream();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation1 {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TestAnnotation1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation2 {
|
||||
|
||||
@interface TestAnnotation2 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation3 {
|
||||
|
||||
@interface TestAnnotation3 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation4 {
|
||||
|
||||
@interface TestAnnotation4 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation5 {
|
||||
|
||||
@interface TestAnnotation5 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotation6 {
|
||||
|
||||
@interface TestAnnotation6 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
static @interface TestInheritedAnnotation1 {
|
||||
|
||||
@interface TestInheritedAnnotation1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
static @interface TestInheritedAnnotation2 {
|
||||
|
||||
@interface TestInheritedAnnotation2 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
static @interface TestInheritedAnnotation3 {
|
||||
|
||||
@interface TestInheritedAnnotation3 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
static @interface TestInheritedAnnotation4 {
|
||||
|
||||
@interface TestInheritedAnnotation4 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
static @interface TestInheritedAnnotation5 {
|
||||
|
||||
@interface TestInheritedAnnotation5 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface OnSuperClass {
|
||||
|
||||
@interface OnSuperClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface OnInterface {
|
||||
|
||||
@interface OnInterface {
|
||||
}
|
||||
|
||||
static class WithNoAnnotations {
|
||||
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation1
|
||||
@@ -615,7 +603,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation1
|
||||
@@ -626,7 +613,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation2
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation2
|
||||
@@ -637,7 +623,6 @@ public class AnnotationsScannerTests {
|
||||
@TestInheritedAnnotation2
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation1
|
||||
@@ -646,7 +631,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestInheritedAnnotation2
|
||||
@@ -655,7 +639,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation1
|
||||
@@ -664,17 +647,15 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation2
|
||||
@TestInheritedAnnotation2
|
||||
static interface SingleInterface {
|
||||
interface SingleInterface {
|
||||
|
||||
@TestAnnotation2
|
||||
@TestInheritedAnnotation2
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
@TestAnnotation1
|
||||
@@ -683,7 +664,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation2
|
||||
@@ -694,7 +674,6 @@ public class AnnotationsScannerTests {
|
||||
@TestInheritedAnnotation2
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation3
|
||||
@@ -703,33 +682,29 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation3
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation4
|
||||
static interface HierarchySuperSuperclassInterface {
|
||||
interface HierarchySuperSuperclassInterface {
|
||||
|
||||
@TestAnnotation4
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
@TestAnnotation5
|
||||
@TestInheritedAnnotation5
|
||||
static interface HierarchyInterface extends HierarchyInterfaceInterface {
|
||||
interface HierarchyInterface extends HierarchyInterfaceInterface {
|
||||
|
||||
@TestAnnotation5
|
||||
@TestInheritedAnnotation5
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
@TestAnnotation6
|
||||
static interface HierarchyInterfaceInterface {
|
||||
interface HierarchyInterfaceInterface {
|
||||
|
||||
@TestAnnotation6
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
static class BridgedMethod implements BridgeMethod<String> {
|
||||
@@ -738,37 +713,32 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method(String arg) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface BridgeMethod<T> {
|
||||
interface BridgeMethod<T> {
|
||||
|
||||
@TestAnnotation2
|
||||
void method(T arg);
|
||||
|
||||
}
|
||||
|
||||
static class Ignoreable implements IgnoreableOverrideInterface1,
|
||||
IgnoreableOverrideInterface2, Serializable {
|
||||
@SuppressWarnings("serial")
|
||||
static class Ignoreable implements IgnoreableOverrideInterface1, IgnoreableOverrideInterface2, Serializable {
|
||||
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface IgnoreableOverrideInterface1 {
|
||||
interface IgnoreableOverrideInterface1 {
|
||||
|
||||
@Nullable
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
static interface IgnoreableOverrideInterface2 {
|
||||
interface IgnoreableOverrideInterface2 {
|
||||
|
||||
@Nullable
|
||||
public void method();
|
||||
|
||||
void method();
|
||||
}
|
||||
|
||||
static abstract class MultipleMethods implements MultipleMethodsInterface {
|
||||
@@ -776,7 +746,6 @@ public class AnnotationsScannerTests {
|
||||
@TestAnnotation1
|
||||
public void method() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface MultipleMethodsInterface {
|
||||
@@ -786,23 +755,19 @@ public class AnnotationsScannerTests {
|
||||
|
||||
@TestAnnotation2
|
||||
void method1();
|
||||
|
||||
}
|
||||
|
||||
static class GenericOverride implements GenericOverrideInterface<String> {
|
||||
|
||||
@TestAnnotation1
|
||||
public void method(String argument) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface GenericOverrideInterface<T extends CharSequence> {
|
||||
interface GenericOverrideInterface<T extends CharSequence> {
|
||||
|
||||
@TestAnnotation2
|
||||
void method(T argument);
|
||||
|
||||
}
|
||||
|
||||
static abstract class GenericNonOverride
|
||||
@@ -810,16 +775,13 @@ public class AnnotationsScannerTests {
|
||||
|
||||
@TestAnnotation1
|
||||
public void method(StringBuilder argument) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface GenericNonOverrideInterface<T extends CharSequence> {
|
||||
interface GenericNonOverrideInterface<T extends CharSequence> {
|
||||
|
||||
@TestAnnotation2
|
||||
void method(T argument);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -31,8 +30,7 @@ import org.junit.Test;
|
||||
import org.springframework.core.annotation.MergedAnnotation.MapValues;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link MergedAnnotationCollectors}.
|
||||
@@ -93,18 +91,10 @@ public class MergedAnnotationCollectorsTests {
|
||||
assertThat(map.get("finished")).containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void toFinishedMultiValueMapWhenFinisherIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> stream().collect(
|
||||
MergedAnnotationCollectors.toMultiValueMap((Function) null))).withMessage(
|
||||
"Finisher must not be null");
|
||||
private Stream<MergedAnnotation<TestAnnotation>> stream() {
|
||||
return MergedAnnotations.from(WithTestAnnotations.class).stream(TestAnnotation.class);
|
||||
}
|
||||
|
||||
private Stream<MergedAnnotation<TestAnnotation>> stream() {
|
||||
return MergedAnnotations.from(WithTestAnnotations.class).stream(
|
||||
TestAnnotation.class);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(TestAnnotations.class)
|
||||
@@ -124,14 +114,12 @@ public class MergedAnnotationCollectorsTests {
|
||||
@interface TestAnnotations {
|
||||
|
||||
TestAnnotation[] value();
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation("a")
|
||||
@TestAnnotation(name = "b", extra = String.class)
|
||||
@TestAnnotation(name = "c", extra = Integer.class)
|
||||
static class WithTestAnnotations {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,20 +16,17 @@
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link MergedAnnotationPredicates}.
|
||||
@@ -54,35 +51,18 @@ public class MergedAnnotationPredicatesTests {
|
||||
MissingAnnotation.class.getName())).rejects(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeInStringArrayWhenStringArraysIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationPredicates.typeIn((String[]) null)).withMessage(
|
||||
"TypeNames must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeInClassArrayWhenNameMatchesAccepts() {
|
||||
MergedAnnotation<TestAnnotation> annotation = MergedAnnotations.from(
|
||||
WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(TestAnnotation.class)).accepts(
|
||||
annotation);
|
||||
MergedAnnotation<TestAnnotation> annotation =
|
||||
MergedAnnotations.from(WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(TestAnnotation.class)).accepts(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeInClassArrayWhenNameDoesNotMatchRejects() {
|
||||
MergedAnnotation<TestAnnotation> annotation = MergedAnnotations.from(
|
||||
WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(MissingAnnotation.class)).rejects(
|
||||
annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeInClassArrayWhenClassArraysIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationPredicates.typeIn(
|
||||
(Class<Annotation>[]) null)).withMessage(
|
||||
"Types must not be null");
|
||||
MergedAnnotation<TestAnnotation> annotation =
|
||||
MergedAnnotations.from(WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(MissingAnnotation.class)).rejects(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,8 +70,7 @@ public class MergedAnnotationPredicatesTests {
|
||||
MergedAnnotation<TestAnnotation> annotation = MergedAnnotations.from(
|
||||
WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(
|
||||
Collections.singleton(TestAnnotation.class.getName()))).accepts(
|
||||
annotation);
|
||||
Collections.singleton(TestAnnotation.class.getName()))).accepts(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,15 +86,7 @@ public class MergedAnnotationPredicatesTests {
|
||||
MergedAnnotation<TestAnnotation> annotation = MergedAnnotations.from(
|
||||
WithTestAnnotation.class).get(TestAnnotation.class);
|
||||
assertThat(MergedAnnotationPredicates.typeIn(Arrays.asList(
|
||||
MissingAnnotation.class.getName(), MissingAnnotation.class))).rejects(
|
||||
annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeInCollectionWhenCollectionIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationPredicates.typeIn(
|
||||
(Collection<?>) null)).withMessage("Types must not be null");
|
||||
MissingAnnotation.class.getName(), MissingAnnotation.class))).rejects(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -125,15 +96,13 @@ public class MergedAnnotationPredicatesTests {
|
||||
MergedAnnotationPredicates.firstRunOf(
|
||||
this::firstCharOfValue)).collect(Collectors.toList());
|
||||
assertThat(filtered.stream().map(
|
||||
annotation -> annotation.getString("value"))).containsExactly("a1", "a2",
|
||||
"a3");
|
||||
annotation -> annotation.getString("value"))).containsExactly("a1", "a2", "a3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstRunOfWhenValueExtractorIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationPredicates.firstRunOf(null)).withMessage(
|
||||
"ValueExtractor must not be null");
|
||||
() -> MergedAnnotationPredicates.firstRunOf(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,42 +112,38 @@ public class MergedAnnotationPredicatesTests {
|
||||
MergedAnnotationPredicates.unique(
|
||||
this::firstCharOfValue)).collect(Collectors.toList());
|
||||
assertThat(filtered.stream().map(
|
||||
annotation -> annotation.getString("value"))).containsExactly("a1", "b1",
|
||||
"c1");
|
||||
annotation -> annotation.getString("value"))).containsExactly("a1", "b1", "c1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uniqueWhenKeyExtractorIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationPredicates.unique(null)).withMessage(
|
||||
"KeyExtractor must not be null");
|
||||
() -> MergedAnnotationPredicates.unique(null));
|
||||
}
|
||||
|
||||
private char firstCharOfValue(MergedAnnotation<TestAnnotation> annotation) {
|
||||
return annotation.getString("value").charAt(0);
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(TestAnnotations.class)
|
||||
static @interface TestAnnotation {
|
||||
@interface TestAnnotation {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface TestAnnotations {
|
||||
@interface TestAnnotations {
|
||||
|
||||
TestAnnotation[] value();
|
||||
|
||||
}
|
||||
|
||||
static @interface MissingAnnotation {
|
||||
|
||||
@interface MissingAnnotation {
|
||||
}
|
||||
|
||||
@TestAnnotation("test")
|
||||
static class WithTestAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@TestAnnotation("a1")
|
||||
@@ -191,7 +156,6 @@ public class MergedAnnotationPredicatesTests {
|
||||
@TestAnnotation("c2")
|
||||
@TestAnnotation("c3")
|
||||
static class WithMultipleTestAnnotation {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,19 +24,16 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -74,44 +71,41 @@ public class MergedAnnotationsTests {
|
||||
|
||||
@Test
|
||||
public void streamWhenFromNonAnnotatedClass() {
|
||||
assertThat(MergedAnnotations.from(NonAnnotatedClass.class).stream(
|
||||
TransactionalComponent.class)).isEmpty();
|
||||
assertThat(MergedAnnotations.from(NonAnnotatedClass.class).
|
||||
stream(TransactionalComponent.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWhenFromClassWithMetaDepth1() {
|
||||
Stream<String> names = MergedAnnotations.from(
|
||||
TransactionalComponent.class).stream().map(MergedAnnotation::getType);
|
||||
Stream<String> names = MergedAnnotations.from(TransactionalComponent.class)
|
||||
.stream().map(MergedAnnotation::getType);
|
||||
assertThat(names).containsExactly(Transactional.class.getName(),
|
||||
Component.class.getName(), Indexed.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWhenFromClassWithMetaDepth2() {
|
||||
Stream<String> names = MergedAnnotations.from(
|
||||
ComposedTransactionalComponent.class).stream().map(
|
||||
MergedAnnotation::getType);
|
||||
Stream<String> names = MergedAnnotations.from(ComposedTransactionalComponent.class)
|
||||
.stream().map(MergedAnnotation::getType);
|
||||
assertThat(names).containsExactly(TransactionalComponent.class.getName(),
|
||||
Transactional.class.getName(), Component.class.getName(),
|
||||
Indexed.class.getName());
|
||||
Transactional.class.getName(), Component.class.getName(), Indexed.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenFromNonAnnotatedClass() {
|
||||
assertThat(MergedAnnotations.from(NonAnnotatedClass.class).isPresent(
|
||||
Transactional.class)).isFalse();
|
||||
assertThat(MergedAnnotations.from(NonAnnotatedClass.class).
|
||||
isPresent(Transactional.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenFromAnnotationClassWithMetaDepth0() {
|
||||
assertThat(MergedAnnotations.from(TransactionalComponent.class).isPresent(
|
||||
TransactionalComponent.class)).isFalse();
|
||||
assertThat(MergedAnnotations.from(TransactionalComponent.class).
|
||||
isPresent(TransactionalComponent.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenFromAnnotationClassWithMetaDepth1() {
|
||||
MergedAnnotations annotations = MergedAnnotations.from(
|
||||
TransactionalComponent.class);
|
||||
MergedAnnotations annotations = MergedAnnotations.from(TransactionalComponent.class);
|
||||
assertThat(annotations.isPresent(Transactional.class)).isTrue();
|
||||
assertThat(annotations.isPresent(Component.class)).isTrue();
|
||||
}
|
||||
@@ -204,8 +198,6 @@ public class MergedAnnotationsTests {
|
||||
* type within the class hierarchy. Such undesirable behavior would cause
|
||||
* the logic in
|
||||
* {@link org.springframework.context.annotation.ProfileCondition} to fail.
|
||||
*
|
||||
* @see org.springframework.core.env.EnvironmentSystemIntegrationTests#mostSpecificDerivedClassDrivesEnvironment_withDevEnvAndDerivedDevConfigClass
|
||||
*/
|
||||
@Test
|
||||
public void collectMultiValueMapFromClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() {
|
||||
@@ -218,8 +210,6 @@ public class MergedAnnotationsTests {
|
||||
/**
|
||||
* Note: this functionality is required by
|
||||
* {@link org.springframework.context.annotation.ProfileCondition}.
|
||||
*
|
||||
* @see org.springframework.core.env.EnvironmentSystemIntegrationTests
|
||||
*/
|
||||
@Test
|
||||
public void collectMultiValueMapFromClassWithMultipleComposedAnnotations() {
|
||||
@@ -629,38 +619,26 @@ public class MergedAnnotationsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithExhaustiveOnMethodWithSingleElementOverridingAnArrayViaConvention()
|
||||
throws Exception {
|
||||
public void getWithExhaustiveOnMethodWithSingleElementOverridingAnArrayViaConvention() throws Exception {
|
||||
testGetWithExhaustiveWebMapping(
|
||||
WebController.class.getMethod("postMappedWithPathAttribute"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithExhaustiveOnMethodWithSingleElementOverridingAnArrayViaAliasFor()
|
||||
throws Exception {
|
||||
public void getWithExhaustiveOnMethodWithSingleElementOverridingAnArrayViaAliasFor() throws Exception {
|
||||
testGetWithExhaustiveWebMapping(
|
||||
WebController.class.getMethod("getMappedWithValueAttribute"));
|
||||
testGetWithExhaustiveWebMapping(
|
||||
WebController.class.getMethod("getMappedWithPathAttribute"));
|
||||
}
|
||||
|
||||
private void testGetWithExhaustiveWebMapping(AnnotatedElement element)
|
||||
throws ArrayComparisonFailure {
|
||||
private void testGetWithExhaustiveWebMapping(AnnotatedElement element) throws ArrayComparisonFailure {
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(element,
|
||||
SearchStrategy.EXHAUSTIVE).get(RequestMapping.class);
|
||||
assertThat(annotation.getStringArray("value")).containsExactly("/test");
|
||||
assertThat(annotation.getStringArray("path")).containsExactly("/test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDirectWithJavaLangAnnotationType() throws Exception {
|
||||
Constructor<?> deprecatedConstructor = Date.class.getConstructor(String.class);
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(deprecatedConstructor,
|
||||
SearchStrategy.DIRECT, RepeatableContainers.standardRepeatables(),
|
||||
AnnotationFilter.NONE).get(Deprecated.class);
|
||||
assertThat(annotation.isPresent()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDirectWithJavaxAnnotationType() throws Exception {
|
||||
assertThat(MergedAnnotations.from(ResourceHolder.class).get(
|
||||
@@ -1267,14 +1245,6 @@ public class MergedAnnotationsTests {
|
||||
Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableOnComposedAnnotation() {
|
||||
MergedAnnotation<?> annotation = MergedAnnotations.from(MyRepeatableMeta1.class,
|
||||
SearchStrategy.EXHAUSTIVE, RepeatableContainers.none(),
|
||||
AnnotationFilter.NONE).get(Repeatable.class);
|
||||
assertThat(annotation.getClass("value")).isEqualTo(MyRepeatableContainer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableDeclaredOnMethod() throws Exception {
|
||||
Method method = InterfaceWithRepeated.class.getMethod("foo");
|
||||
@@ -1286,8 +1256,7 @@ public class MergedAnnotationsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableDeclaredOnClassWithMissingAttributeAliasDeclaration()
|
||||
throws Exception {
|
||||
public void getRepeatableDeclaredOnClassWithMissingAttributeAliasDeclaration() {
|
||||
RepeatableContainers containers = RepeatableContainers.of(
|
||||
BrokenContextConfiguration.class, BrokenHierarchy.class);
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(
|
||||
|
||||
Reference in New Issue
Block a user