Add MergedAnnotations.of method
Add a factory method to `MergedAnnotation` that allows an instance to be created for an explicit collection of root annotations. This method will allow ASM based readers to expose a `MergedAnnotation` instance that has root annotations loaded from bytecode, and meta-annotations loaded using reflection. See gh-22884
This commit is contained in:
committed by
Juergen Hoeller
parent
daec3531b3
commit
9d6cf57cb7
@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -368,6 +369,26 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
return TypeMappedAnnotations.from(source, annotations, repeatableContainers, annotationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MergedAnnotations} instance from the specified
|
||||
* collection of directly present annotations. This method allows a
|
||||
* {@link MergedAnnotations} instance to be created from annotations that
|
||||
* are not necessarily loaded using reflection. The provided annotations
|
||||
* must all be {@link MergedAnnotation#isDirectlyPresent() directly present}
|
||||
* and must have a {@link MergedAnnotation#getAggregateIndex() aggregate
|
||||
* index} of {@code 0}.
|
||||
* <p>
|
||||
* The resulting {@link MergedAnnotations} instance will contain both the
|
||||
* specified annotations, and any meta-annotations that can be read using
|
||||
* reflection.
|
||||
* @param annotations the annotations to include
|
||||
* @return a {@link MergedAnnotations} instance containing the annotations
|
||||
* @see MergedAnnotation#of(ClassLoader, Object, Class, java.util.Map)
|
||||
*/
|
||||
static MergedAnnotations of(Collection<MergedAnnotation<?>> annotations) {
|
||||
return MergedAnnotationsCollection.of(annotations);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search strategies supported by
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.util.Collection;
|
||||
import java.util.Iterator;
|
||||
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;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link MergedAnnotations} implementation backed by a {@link Collection}
|
||||
* {@link MergedAnnotation} instances that represent direct annotations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 5.2
|
||||
* @see MergedAnnotations#of(Collection)
|
||||
*/
|
||||
final class MergedAnnotationsCollection implements MergedAnnotations {
|
||||
|
||||
private final MergedAnnotation<?>[] annotations;
|
||||
|
||||
private final AnnotationTypeMappings[] mappings;
|
||||
|
||||
private MergedAnnotationsCollection(Collection<MergedAnnotation<?>> annotations) {
|
||||
Assert.notNull(annotations, "Annotations must not be null");
|
||||
this.annotations = annotations.toArray(new MergedAnnotation<?>[0]);
|
||||
this.mappings = new AnnotationTypeMappings[this.annotations.length];
|
||||
for (int i = 0; i < this.annotations.length; i++) {
|
||||
MergedAnnotation<?> annotation = this.annotations[i];
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
Assert.isTrue(annotation.isDirectlyPresent(), "Annotation must be directly present");
|
||||
Assert.isTrue(annotation.getAggregateIndex() == 0, "Annotation must have aggregate index of zero");
|
||||
this.mappings[i] = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MergedAnnotation<Annotation>> iterator() {
|
||||
return Spliterators.iterator(spliterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<MergedAnnotation<Annotation>> spliterator() {
|
||||
return spliterator(null);
|
||||
}
|
||||
|
||||
private <A extends Annotation> Spliterator<MergedAnnotation<A>> spliterator(
|
||||
@Nullable Object annotationType) {
|
||||
return new AnnotationsSpliterator<>(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> boolean isPresent(Class<A> annotationType) {
|
||||
return isPresent(annotationType, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent(String annotationType) {
|
||||
return isPresent(annotationType, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> boolean isDirectlyPresent(Class<A> annotationType) {
|
||||
return isPresent(annotationType, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectlyPresent(String annotationType) {
|
||||
return isPresent(annotationType, true);
|
||||
}
|
||||
|
||||
private boolean isPresent(Object requiredType, boolean directOnly) {
|
||||
for (MergedAnnotation<?> annotation : this.annotations) {
|
||||
Class<? extends Annotation> type = annotation.getType();
|
||||
if (type == requiredType || type.getName().equals(requiredType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!directOnly) {
|
||||
for (AnnotationTypeMappings mappings : this.mappings) {
|
||||
for (int i = 1; i < mappings.size(); i++) {
|
||||
AnnotationTypeMapping mapping = mappings.get(i);
|
||||
if (isMappingForType(mapping, requiredType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType) {
|
||||
return get(annotationType, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
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(Class<A> annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector) {
|
||||
MergedAnnotation<A> result = find(annotationType, predicate, selector);
|
||||
return (result != null ? result : MergedAnnotation.missing());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> MergedAnnotation<A> get(String annotationType) {
|
||||
return get(annotationType, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
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(String annotationType,
|
||||
@Nullable Predicate<? super MergedAnnotation<A>> predicate,
|
||||
@Nullable MergedAnnotationSelector<A> selector) {
|
||||
|
||||
MergedAnnotation<A> result = find(annotationType, predicate, selector);
|
||||
return (result != null ? result : MergedAnnotation.missing());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <A extends Annotation> MergedAnnotation<A> find(Object requiredType,
|
||||
Predicate<? super MergedAnnotation<A>> predicate,
|
||||
MergedAnnotationSelector<A> selector) {
|
||||
if (selector == null) {
|
||||
selector = MergedAnnotationSelectors.nearest();
|
||||
}
|
||||
MergedAnnotation<A> result = null;
|
||||
for (int i = 0; i < this.annotations.length; i++) {
|
||||
MergedAnnotation<?> root = this.annotations[i];
|
||||
AnnotationTypeMappings mappings = this.mappings[i];
|
||||
for (int mappingIndex = 0; mappingIndex < mappings.size(); mappingIndex++) {
|
||||
AnnotationTypeMapping mapping = mappings.get(mappingIndex);
|
||||
if (!isMappingForType(mapping, requiredType)) {
|
||||
continue;
|
||||
}
|
||||
MergedAnnotation<A> candidate = (mappingIndex == 0
|
||||
? (MergedAnnotation<A>) root
|
||||
: TypeMappedAnnotation.createIfPossible(mapping, root, IntrospectionFailureLogger.INFO));
|
||||
if (candidate != null && (predicate == null || predicate.test(candidate))) {
|
||||
if (selector.isBestCandidate(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
result = (result != null ? selector.select(result, candidate) : candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> Stream<MergedAnnotation<A>> stream(Class<A> annotationType) {
|
||||
return StreamSupport.stream(spliterator(annotationType), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Annotation> Stream<MergedAnnotation<A>> stream(String annotationType) {
|
||||
return StreamSupport.stream(spliterator(annotationType), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<MergedAnnotation<Annotation>> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
|
||||
private static boolean isMappingForType(AnnotationTypeMapping mapping, @Nullable Object requiredType) {
|
||||
if (requiredType == null) {
|
||||
return true;
|
||||
}
|
||||
Class<? extends Annotation> actualType = mapping.getAnnotationType();
|
||||
return (actualType == requiredType || actualType.getName().equals(requiredType));
|
||||
}
|
||||
|
||||
static MergedAnnotations of(Collection<MergedAnnotation<?>> annotations) {
|
||||
Assert.notNull(annotations, "Annotations must not be null");
|
||||
if(annotations.isEmpty()) {
|
||||
return TypeMappedAnnotations.NONE;
|
||||
}
|
||||
return new MergedAnnotationsCollection(annotations);
|
||||
}
|
||||
|
||||
|
||||
private class AnnotationsSpliterator<A extends Annotation> implements Spliterator<MergedAnnotation<A>> {
|
||||
|
||||
@Nullable
|
||||
private Object requiredType;
|
||||
|
||||
private final int[] mappingCursors;
|
||||
|
||||
public AnnotationsSpliterator(@Nullable Object requiredType) {
|
||||
this.mappingCursors = new int[annotations.length];
|
||||
this.requiredType = requiredType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super MergedAnnotation<A>> action) {
|
||||
int lowestDepth = Integer.MAX_VALUE;
|
||||
int annotationResult = -1;
|
||||
for (int annotationIndex = 0; annotationIndex < annotations.length; annotationIndex++) {
|
||||
AnnotationTypeMapping mapping = getNextSuitableMapping(annotationIndex);
|
||||
if (mapping != null && mapping.getDepth() < lowestDepth) {
|
||||
annotationResult = annotationIndex;
|
||||
lowestDepth = mapping.getDepth();
|
||||
}
|
||||
if (lowestDepth == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (annotationResult != -1) {
|
||||
MergedAnnotation<A> mergedAnnotation = createMergedAnnotationIfPossible(annotationResult, this.mappingCursors[annotationResult]);
|
||||
this.mappingCursors[annotationResult]++;
|
||||
if (mergedAnnotation == null) {
|
||||
return tryAdvance(action);
|
||||
}
|
||||
action.accept(mergedAnnotation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AnnotationTypeMapping getNextSuitableMapping(int annotationIndex) {
|
||||
AnnotationTypeMapping mapping;
|
||||
do {
|
||||
mapping = getMapping(annotationIndex, this.mappingCursors[annotationIndex]);
|
||||
if (mapping != null && isMappingForType(mapping, this.requiredType)) {
|
||||
return mapping;
|
||||
}
|
||||
this.mappingCursors[annotationIndex]++;
|
||||
}
|
||||
while (mapping != null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AnnotationTypeMapping getMapping(int annotationIndex, int mappingIndex) {
|
||||
AnnotationTypeMappings mappings = MergedAnnotationsCollection.this.mappings[annotationIndex];
|
||||
return (mappingIndex < mappings.size() ? mappings.get(mappingIndex) : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private MergedAnnotation<A> createMergedAnnotationIfPossible(
|
||||
int annotationIndex, int mappingIndex) {
|
||||
MergedAnnotation<?> root = annotations[annotationIndex];
|
||||
if(mappingIndex == 0) {
|
||||
return (MergedAnnotation<A>) root;
|
||||
}
|
||||
IntrospectionFailureLogger logger = (this.requiredType != null
|
||||
? IntrospectionFailureLogger.INFO
|
||||
: IntrospectionFailureLogger.DEBUG);
|
||||
return TypeMappedAnnotation.createIfPossible(
|
||||
mappings[annotationIndex].get(mappingIndex), root, logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<MergedAnnotation<A>> trySplit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
AnnotationTypeMappings mappings = MergedAnnotationsCollection.this.mappings[i];
|
||||
int numberOfMappings = mappings.size();
|
||||
numberOfMappings -= Math.min(this.mappingCursors[i], mappings.size());
|
||||
size += numberOfMappings;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return NONNULL | IMMUTABLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,10 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
private static final AnnotationFilter FILTER_ALL = (annotationType -> true);
|
||||
|
||||
private static final MergedAnnotations NONE = new TypeMappedAnnotations(
|
||||
/**
|
||||
* Shared instance that can be used when there are no annotations.
|
||||
*/
|
||||
static final MergedAnnotations NONE = new TypeMappedAnnotations(
|
||||
null, new Annotation[0], RepeatableContainers.none(), FILTER_ALL);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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
|
||||
*
|
||||
* http://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.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link MergedAnnotationsCollection}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class MergedAnnotationsCollectionTests {
|
||||
|
||||
@Test
|
||||
public void ofWhenDirectAnnotationsIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> MergedAnnotationsCollection.of(null)).withMessage(
|
||||
"Annotations must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofWhenEmptyReturnsSharedNoneInstance() {
|
||||
MergedAnnotations annotations = MergedAnnotationsCollection.of(new ArrayList<>());
|
||||
assertThat(annotations).isSameAs(TypeMappedAnnotations.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenAnnotationIsNotDirectlyPresentThrowsException() {
|
||||
MergedAnnotation<?> annotation = mock(MergedAnnotation.class);
|
||||
given(annotation.isDirectlyPresent()).willReturn(false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
MergedAnnotationsCollection.of(Collections.singleton(annotation));
|
||||
}).withMessage("Annotation must be directly present");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenAnnotationAggregateIndexIsNotZeroThrowsException() {
|
||||
MergedAnnotation<?> annotation = mock(MergedAnnotation.class);
|
||||
given(annotation.isDirectlyPresent()).willReturn(true);
|
||||
given(annotation.getAggregateIndex()).willReturn(1);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
MergedAnnotationsCollection.of(Collections.singleton(annotation));
|
||||
}).withMessage("Annotation must have aggregate index of zero");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interateIteratesInCorrectOrder() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
List<Class<?>> types = new ArrayList<>();
|
||||
for (MergedAnnotation<?> annotation : annotations) {
|
||||
types.add(annotation.getType());
|
||||
}
|
||||
assertThat(types).containsExactly(Direct.class, Simple.class, Meta1.class,
|
||||
Meta2.class, Meta11.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spliteratorIteratesInCorrectOrder() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
Spliterator<MergedAnnotation<Annotation>> spliterator = annotations.spliterator();
|
||||
List<Class<?>> types = new ArrayList<>();
|
||||
spliterator.forEachRemaining(annotation -> types.add(annotation.getType()));
|
||||
assertThat(types).containsExactly(Direct.class, Simple.class, Meta1.class,
|
||||
Meta2.class, Meta11.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spliteratorEstimatesSize() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
Spliterator<MergedAnnotation<Annotation>> spliterator = annotations.spliterator();
|
||||
assertThat(spliterator.estimateSize()).isEqualTo(5);
|
||||
spliterator.tryAdvance(
|
||||
annotation -> assertThat(annotation.getType()).isEqualTo(Direct.class));
|
||||
assertThat(spliterator.estimateSize()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenDirectlyPresentReturnsTrue() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isPresent(Direct.class)).isTrue();
|
||||
assertThat(annotations.isPresent(Direct.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenMetaPresentReturnsTrue() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isPresent(Meta11.class)).isTrue();
|
||||
assertThat(annotations.isPresent(Meta11.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPresentWhenNotPresentReturnsFalse() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isPresent(Missing.class)).isFalse();
|
||||
assertThat(annotations.isPresent(Missing.class.getName())).isFalse();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDirectlyPresentWhenDirectlyPresentReturnsTrue() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isDirectlyPresent(Direct.class)).isTrue();
|
||||
assertThat(annotations.isDirectlyPresent(Direct.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDirectlyPresentWhenMetaPresentReturnsFalse() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isDirectlyPresent(Meta11.class)).isFalse();
|
||||
assertThat(annotations.isDirectlyPresent(Meta11.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDirectlyPresentWhenNotPresentReturnsFalse() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.isDirectlyPresent(Missing.class)).isFalse();
|
||||
assertThat(annotations.isDirectlyPresent(Missing.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReturnsAppropriateAnnotation() {
|
||||
MergedAnnotations annotations = getMutiRoute1();
|
||||
assertThat(annotations.get(MutiRouteTarget.class).getString(
|
||||
MergedAnnotation.VALUE)).isEqualTo("12");
|
||||
assertThat(annotations.get(MutiRouteTarget.class.getName()).getString(
|
||||
MergedAnnotation.VALUE)).isEqualTo("12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenNotPresentReturnsMissing() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
assertThat(annotations.get(Missing.class)).isEqualTo(MergedAnnotation.missing());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithPredicateReturnsOnlyMatching() {
|
||||
MergedAnnotations annotations = getMutiRoute1();
|
||||
assertThat(annotations.get(MutiRouteTarget.class,
|
||||
annotation -> annotation.getDepth() >= 3).getString(
|
||||
MergedAnnotation.VALUE)).isEqualTo("111");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithSelectorReturnsSelected() {
|
||||
MergedAnnotations annotations = getMutiRoute1();
|
||||
MergedAnnotationSelector<MutiRouteTarget> deepest = (existing,
|
||||
candidate) -> candidate.getDepth() > existing.getDepth() ? candidate
|
||||
: existing;
|
||||
assertThat(annotations.get(MutiRouteTarget.class, null, deepest).getString(
|
||||
MergedAnnotation.VALUE)).isEqualTo("111");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamStreamsInCorrectOrder() {
|
||||
MergedAnnotations annotations = getDirectAndSimple();
|
||||
List<Class<?>> types = new ArrayList<>();
|
||||
annotations.stream().forEach(annotation -> types.add(annotation.getType()));
|
||||
assertThat(types).containsExactly(Direct.class, Simple.class, Meta1.class,
|
||||
Meta2.class, Meta11.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWithTypeStreamsInCorrectOrder() {
|
||||
MergedAnnotations annotations = getMutiRoute1();
|
||||
List<String> values = new ArrayList<>();
|
||||
annotations.stream(MutiRouteTarget.class).forEach(
|
||||
annotation -> values.add(annotation.getString(MergedAnnotation.VALUE)));
|
||||
assertThat(values).containsExactly("12", "111");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMetaWhenRootHasAttributeValuesShouldAlaisAttributes() {
|
||||
MergedAnnotation<Alaised> root = MergedAnnotation.of(null, null, Alaised.class,
|
||||
Collections.singletonMap("testAlias", "test"));
|
||||
MergedAnnotations annotations = MergedAnnotationsCollection.of(
|
||||
Collections.singleton(root));
|
||||
MergedAnnotation<AlaisTarget> metaAnnotation = annotations.get(AlaisTarget.class);
|
||||
assertThat(metaAnnotation.getString("test")).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMetaWhenRootHasNoAttributeValuesShouldAlaisAttributes() {
|
||||
MergedAnnotation<Alaised> root = MergedAnnotation.of(null, null, Alaised.class,
|
||||
Collections.emptyMap());
|
||||
MergedAnnotations annotations = MergedAnnotationsCollection.of(
|
||||
Collections.singleton(root));
|
||||
MergedAnnotation<AlaisTarget> metaAnnotation = annotations.get(AlaisTarget.class);
|
||||
assertThat(root.getString("testAlias")).isEqualTo("newdefault");
|
||||
assertThat(metaAnnotation.getString("test")).isEqualTo("newdefault");
|
||||
}
|
||||
|
||||
private MergedAnnotations getDirectAndSimple() {
|
||||
List<MergedAnnotation<?>> list = new ArrayList<>();
|
||||
list.add(MergedAnnotation.of(null, null, Direct.class, Collections.emptyMap()));
|
||||
list.add(MergedAnnotation.of(null, null, Simple.class, Collections.emptyMap()));
|
||||
return MergedAnnotationsCollection.of(list);
|
||||
}
|
||||
|
||||
private MergedAnnotations getMutiRoute1() {
|
||||
List<MergedAnnotation<?>> list = new ArrayList<>();
|
||||
list.add(MergedAnnotation.of(null, null, MutiRoute1.class,
|
||||
Collections.emptyMap()));
|
||||
return MergedAnnotationsCollection.of(list);
|
||||
}
|
||||
|
||||
@Meta1
|
||||
@Meta2
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Direct {
|
||||
|
||||
}
|
||||
|
||||
@Meta11
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta1 {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta2 {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta11 {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Simple {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Missing {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MutiRouteTarget {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@MutiRoute11
|
||||
@MutiRoute12
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MutiRoute1 {
|
||||
|
||||
}
|
||||
|
||||
@MutiRoute111
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MutiRoute11 {
|
||||
|
||||
}
|
||||
|
||||
@MutiRouteTarget("12")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MutiRoute12 {
|
||||
|
||||
}
|
||||
|
||||
@MutiRouteTarget("111")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MutiRoute111 {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface AlaisTarget {
|
||||
|
||||
String test() default "default";
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@AlaisTarget
|
||||
@interface Alaised {
|
||||
|
||||
@AliasFor(annotation = AlaisTarget.class, attribute = "test")
|
||||
String testAlias() default "newdefault";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user