org.springframework.core.annotation.TypeMappedAnnotations Maven / Gradle / Ivy
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.lang.Nullable;
/**
* {@link MergedAnnotations} implementation that searches for and adapts
* annotations and meta-annotations using {@link AnnotationTypeMappings}.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2
*/
final class TypeMappedAnnotations implements MergedAnnotations {
/**
* Shared instance that can be used when there are no annotations.
*/
static final MergedAnnotations NONE = new TypeMappedAnnotations(
null, new Annotation[0], RepeatableContainers.none(), AnnotationFilter.ALL);
@Nullable
private final Object source;
@Nullable
private final AnnotatedElement element;
@Nullable
private final SearchStrategy searchStrategy;
private final Predicate> searchEnclosingClass;
@Nullable
private final Annotation[] annotations;
private final RepeatableContainers repeatableContainers;
private final AnnotationFilter annotationFilter;
@Nullable
private volatile List aggregates;
private TypeMappedAnnotations(AnnotatedElement element, SearchStrategy searchStrategy,
Predicate> searchEnclosingClass, RepeatableContainers repeatableContainers,
AnnotationFilter annotationFilter) {
this.source = element;
this.element = element;
this.searchStrategy = searchStrategy;
this.searchEnclosingClass = searchEnclosingClass;
this.annotations = null;
this.repeatableContainers = repeatableContainers;
this.annotationFilter = annotationFilter;
}
private TypeMappedAnnotations(@Nullable Object source, Annotation[] annotations,
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
this.source = source;
this.element = null;
this.searchStrategy = null;
this.searchEnclosingClass = Search.never;
this.annotations = annotations;
this.repeatableContainers = repeatableContainers;
this.annotationFilter = annotationFilter;
}
@Override
public boolean isPresent(Class annotationType) {
if (this.annotationFilter.matches(annotationType)) {
return false;
}
return Boolean.TRUE.equals(scan(annotationType,
IsPresent.get(this.repeatableContainers, this.annotationFilter, false)));
}
@Override
public boolean isPresent(String annotationType) {
if (this.annotationFilter.matches(annotationType)) {
return false;
}
return Boolean.TRUE.equals(scan(annotationType,
IsPresent.get(this.repeatableContainers, this.annotationFilter, false)));
}
@Override
public boolean isDirectlyPresent(Class annotationType) {
if (this.annotationFilter.matches(annotationType)) {
return false;
}
return Boolean.TRUE.equals(scan(annotationType,
IsPresent.get(this.repeatableContainers, this.annotationFilter, true)));
}
@Override
public boolean isDirectlyPresent(String annotationType) {
if (this.annotationFilter.matches(annotationType)) {
return false;
}
return Boolean.TRUE.equals(scan(annotationType,
IsPresent.get(this.repeatableContainers, this.annotationFilter, true)));
}
@Override
public MergedAnnotation get(Class annotationType) {
return get(annotationType, null, null);
}
@Override
public MergedAnnotation get(Class annotationType,
@Nullable Predicate> predicate) {
return get(annotationType, predicate, null);
}
@Override
public MergedAnnotation get(Class annotationType,
@Nullable Predicate> predicate,
@Nullable MergedAnnotationSelector selector) {
if (this.annotationFilter.matches(annotationType)) {
return MergedAnnotation.missing();
}
MergedAnnotation result = scan(annotationType,
new MergedAnnotationFinder<>(annotationType, predicate, selector));
return (result != null ? result : MergedAnnotation.missing());
}
@Override
public MergedAnnotation get(String annotationType) {
return get(annotationType, null, null);
}
@Override
public MergedAnnotation get(String annotationType,
@Nullable Predicate> predicate) {
return get(annotationType, predicate, null);
}
@Override
public MergedAnnotation get(String annotationType,
@Nullable Predicate> predicate,
@Nullable MergedAnnotationSelector selector) {
if (this.annotationFilter.matches(annotationType)) {
return MergedAnnotation.missing();
}
MergedAnnotation result = scan(annotationType,
new MergedAnnotationFinder<>(annotationType, predicate, selector));
return (result != null ? result : MergedAnnotation.missing());
}
@Override
public Stream> stream(Class annotationType) {
if (this.annotationFilter == AnnotationFilter.ALL) {
return Stream.empty();
}
return StreamSupport.stream(spliterator(annotationType), false);
}
@Override
public Stream> stream(String annotationType) {
if (this.annotationFilter == AnnotationFilter.ALL) {
return Stream.empty();
}
return StreamSupport.stream(spliterator(annotationType), false);
}
@Override
public Stream> stream() {
if (this.annotationFilter == AnnotationFilter.ALL) {
return Stream.empty();
}
return StreamSupport.stream(spliterator(), false);
}
@Override
public Iterator> iterator() {
if (this.annotationFilter == AnnotationFilter.ALL) {
return Collections.emptyIterator();
}
return Spliterators.iterator(spliterator());
}
@Override
public Spliterator> spliterator() {
if (this.annotationFilter == AnnotationFilter.ALL) {
return Spliterators.emptySpliterator();
}
return spliterator(null);
}
private Spliterator> spliterator(@Nullable Object annotationType) {
return new AggregatesSpliterator<>(annotationType, getAggregates());
}
private List getAggregates() {
List aggregates = this.aggregates;
if (aggregates == null) {
aggregates = scan(this, new AggregatesCollector());
if (aggregates == null || aggregates.isEmpty()) {
aggregates = Collections.emptyList();
}
this.aggregates = aggregates;
}
return aggregates;
}
@Nullable
private R scan(C criteria, AnnotationsProcessor processor) {
if (this.annotations != null) {
R result = processor.doWithAnnotations(criteria, 0, this.source, this.annotations);
return processor.finish(result);
}
if (this.element != null && this.searchStrategy != null) {
return AnnotationsScanner.scan(criteria, this.element, this.searchStrategy,
this.searchEnclosingClass, processor);
}
return null;
}
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
Predicate> searchEnclosingClass, RepeatableContainers repeatableContainers,
AnnotationFilter annotationFilter) {
if (AnnotationsScanner.isKnownEmpty(element, searchStrategy, searchEnclosingClass)) {
return NONE;
}
return new TypeMappedAnnotations(element, searchStrategy, searchEnclosingClass, repeatableContainers, annotationFilter);
}
static MergedAnnotations from(@Nullable Object source, Annotation[] annotations,
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
if (annotations.length == 0) {
return NONE;
}
return new TypeMappedAnnotations(source, annotations, repeatableContainers, annotationFilter);
}
private static boolean isMappingForType(AnnotationTypeMapping mapping,
AnnotationFilter annotationFilter, @Nullable Object requiredType) {
Class actualType = mapping.getAnnotationType();
return (!annotationFilter.matches(actualType) &&
(requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType)));
}
/**
* {@link AnnotationsProcessor} used to detect if an annotation is directly
* present or meta-present.
*/
private static final class IsPresent implements AnnotationsProcessor