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:
		
							parent
							
								
									75b52309a7
								
							
						
					
					
						commit
						5e15338a09
					
				| 
						 | 
				
			
			@ -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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue