Support searches for merged repeatable annotations with "get" semantics
This commit picks up where 2535469099 left off with added support for
"get" search semantics for merged repeatable annotations.
Specifically, this commit introduces a new
getMergedRepeatableAnnotations() method in AnnotatedElementUtils.
Issue: SPR-13973
This commit is contained in:
@@ -57,6 +57,7 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.*;
|
||||
* @since 4.0.3
|
||||
* @see AnnotationUtilsTests
|
||||
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
|
||||
* @see ComposedRepeatableAnnotationsTests
|
||||
*/
|
||||
public class AnnotatedElementUtilsTests {
|
||||
|
||||
|
||||
@@ -37,15 +37,17 @@ import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotatedElementUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests that verify support for finding all composed, repeatable
|
||||
* Unit tests that verify support for getting and finding all composed, repeatable
|
||||
* annotations on a single annotated element.
|
||||
*
|
||||
* <p>See <a href="https://jira.spring.io/browse/SPR-13973">SPR-13973</a>.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see AnnotatedElementUtils
|
||||
* @see AnnotatedElementUtils#getMergedRepeatableAnnotations
|
||||
* @see AnnotatedElementUtils#findMergedRepeatableAnnotations
|
||||
* @see AnnotatedElementUtilsTests
|
||||
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
|
||||
*/
|
||||
public class ComposedRepeatableAnnotationsTests {
|
||||
|
||||
@@ -53,43 +55,92 @@ public class ComposedRepeatableAnnotationsTests {
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void getNonRepeatableAnnotation() {
|
||||
expectNonRepeatableAnnotation();
|
||||
getMergedRepeatableAnnotations(getClass(), NonRepeatable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInvalidRepeatableAnnotationContainerMissingValueAttribute() {
|
||||
expectContainerMissingValueAttribute();
|
||||
getMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class, ContainerMissingValueAttribute.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInvalidRepeatableAnnotationContainerWithNonArrayValueAttribute() {
|
||||
expectContainerWithNonArrayValueAttribute();
|
||||
getMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class, ContainerWithNonArrayValueAttribute.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInvalidRepeatableAnnotationContainerWithArrayValueAttributeButWrongComponentType() {
|
||||
expectContainerWithArrayValueAttributeButWrongComponentType();
|
||||
getMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class,
|
||||
ContainerWithArrayValueAttributeButWrongComponentType.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableAnnotationsOnClass() {
|
||||
assertGetRepeatableAnnotations(RepeatableClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepeatableAnnotationsOnSuperclass() {
|
||||
assertGetRepeatableAnnotations(SubRepeatableClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getComposedRepeatableAnnotationsOnClass() {
|
||||
assertGetRepeatableAnnotations(ComposedRepeatableClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getComposedRepeatableAnnotationsMixedWithContainerOnClass() {
|
||||
assertGetRepeatableAnnotations(ComposedRepeatableMixedWithContainerClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getComposedContainerForRepeatableAnnotationsOnClass() {
|
||||
assertGetRepeatableAnnotations(ComposedContainerClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNoninheritedComposedRepeatableAnnotationsOnClass() {
|
||||
Class<?> element = NoninheritedRepeatableClass.class;
|
||||
Set<Noninherited> annotations = getMergedRepeatableAnnotations(element, Noninherited.class);
|
||||
assertNoninheritedRepeatableAnnotations(annotations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNoninheritedComposedRepeatableAnnotationsOnSuperclass() {
|
||||
Class<?> element = SubNoninheritedRepeatableClass.class;
|
||||
Set<Noninherited> annotations = getMergedRepeatableAnnotations(element, Noninherited.class);
|
||||
assertNotNull(annotations);
|
||||
assertEquals(0, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNonRepeatableAnnotation() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(startsWith("annotationType must be a repeatable annotation"));
|
||||
exception.expectMessage(containsString("failed to resolve container type for"));
|
||||
exception.expectMessage(containsString(NonRepeatable.class.getName()));
|
||||
expectNonRepeatableAnnotation();
|
||||
findMergedRepeatableAnnotations(getClass(), NonRepeatable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findInvalidRepeatableAnnotationContainerMissingValueAttribute() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Invalid declaration of container type"));
|
||||
exception.expectMessage(containsString(ContainerMissingValueAttribute.class.getName()));
|
||||
exception.expectMessage(containsString("for repeatable annotation"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
exception.expectCause(isA(NoSuchMethodException.class));
|
||||
expectContainerMissingValueAttribute();
|
||||
findMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class, ContainerMissingValueAttribute.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findInvalidRepeatableAnnotationContainerWithNonArrayValueAttribute() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Container type"));
|
||||
exception.expectMessage(containsString(ContainerWithNonArrayValueAttribute.class.getName()));
|
||||
exception.expectMessage(containsString("must declare a 'value' attribute for an array of type"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
expectContainerWithNonArrayValueAttribute();
|
||||
findMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class, ContainerWithNonArrayValueAttribute.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findInvalidRepeatableAnnotationContainerWithArrayValueAttributeButWrongComponentType() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Container type"));
|
||||
exception.expectMessage(containsString(ContainerWithArrayValueAttributeButWrongComponentType.class.getName()));
|
||||
exception.expectMessage(containsString("must declare a 'value' attribute for an array of type"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
expectContainerWithArrayValueAttributeButWrongComponentType();
|
||||
findMergedRepeatableAnnotations(getClass(), InvalidRepeatable.class,
|
||||
ContainerWithArrayValueAttributeButWrongComponentType.class);
|
||||
}
|
||||
@@ -114,11 +165,70 @@ public class ComposedRepeatableAnnotationsTests {
|
||||
assertFindRepeatableAnnotations(ComposedRepeatableMixedWithContainerClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNoninheritedComposedRepeatableAnnotationsOnClass() {
|
||||
Class<?> element = NoninheritedRepeatableClass.class;
|
||||
Set<Noninherited> annotations = findMergedRepeatableAnnotations(element, Noninherited.class);
|
||||
assertNoninheritedRepeatableAnnotations(annotations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNoninheritedComposedRepeatableAnnotationsOnSuperclass() {
|
||||
Class<?> element = SubNoninheritedRepeatableClass.class;
|
||||
Set<Noninherited> annotations = findMergedRepeatableAnnotations(element, Noninherited.class);
|
||||
assertNoninheritedRepeatableAnnotations(annotations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findComposedContainerForRepeatableAnnotationsOnClass() {
|
||||
assertFindRepeatableAnnotations(ComposedContainerClass.class);
|
||||
}
|
||||
|
||||
private void expectNonRepeatableAnnotation() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(startsWith("annotationType must be a repeatable annotation"));
|
||||
exception.expectMessage(containsString("failed to resolve container type for"));
|
||||
exception.expectMessage(containsString(NonRepeatable.class.getName()));
|
||||
}
|
||||
|
||||
private void expectContainerMissingValueAttribute() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Invalid declaration of container type"));
|
||||
exception.expectMessage(containsString(ContainerMissingValueAttribute.class.getName()));
|
||||
exception.expectMessage(containsString("for repeatable annotation"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
exception.expectCause(isA(NoSuchMethodException.class));
|
||||
}
|
||||
|
||||
private void expectContainerWithNonArrayValueAttribute() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Container type"));
|
||||
exception.expectMessage(containsString(ContainerWithNonArrayValueAttribute.class.getName()));
|
||||
exception.expectMessage(containsString("must declare a 'value' attribute for an array of type"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
}
|
||||
|
||||
private void expectContainerWithArrayValueAttributeButWrongComponentType() {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Container type"));
|
||||
exception.expectMessage(containsString(ContainerWithArrayValueAttributeButWrongComponentType.class.getName()));
|
||||
exception.expectMessage(containsString("must declare a 'value' attribute for an array of type"));
|
||||
exception.expectMessage(containsString(InvalidRepeatable.class.getName()));
|
||||
}
|
||||
|
||||
private void assertGetRepeatableAnnotations(AnnotatedElement element) {
|
||||
assertNotNull(element);
|
||||
|
||||
Set<PeteRepeat> peteRepeats = getMergedRepeatableAnnotations(element, PeteRepeat.class);
|
||||
assertNotNull(peteRepeats);
|
||||
assertEquals(3, peteRepeats.size());
|
||||
|
||||
Iterator<PeteRepeat> iterator = peteRepeats.iterator();
|
||||
assertEquals("A", iterator.next().value());
|
||||
assertEquals("B", iterator.next().value());
|
||||
assertEquals("C", iterator.next().value());
|
||||
}
|
||||
|
||||
private void assertFindRepeatableAnnotations(AnnotatedElement element) {
|
||||
assertNotNull(element);
|
||||
|
||||
@@ -132,6 +242,16 @@ public class ComposedRepeatableAnnotationsTests {
|
||||
assertEquals("C", iterator.next().value());
|
||||
}
|
||||
|
||||
private void assertNoninheritedRepeatableAnnotations(Set<Noninherited> annotations) {
|
||||
assertNotNull(annotations);
|
||||
assertEquals(3, annotations.size());
|
||||
|
||||
Iterator<Noninherited> iterator = annotations.iterator();
|
||||
assertEquals("A", iterator.next().value());
|
||||
assertEquals("B", iterator.next().value());
|
||||
assertEquals("C", iterator.next().value());
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -227,4 +347,40 @@ public class ComposedRepeatableAnnotationsTests {
|
||||
static class ComposedContainerClass {
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Noninheriteds {
|
||||
|
||||
Noninherited[] value();
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(Noninheriteds.class)
|
||||
@interface Noninherited {
|
||||
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Noninherited(name = "shadowed")
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ComposedNoninherited {
|
||||
|
||||
@AliasFor(annotation = Noninherited.class)
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@ComposedNoninherited(name = "C")
|
||||
@Noninheriteds({ @Noninherited(value = "A"), @Noninherited(name = "B") })
|
||||
static class NoninheritedRepeatableClass {
|
||||
}
|
||||
|
||||
static class SubNoninheritedRepeatableClass extends NoninheritedRepeatableClass {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.*;
|
||||
* @since 4.3
|
||||
* @see AnnotatedElementUtils
|
||||
* @see AnnotatedElementUtilsTests
|
||||
* @see ComposedRepeatableAnnotationsTests
|
||||
*/
|
||||
public class MultipleComposedAnnotationsOnSingleAnnotatedElementTests {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user