visited,
+ int metaDepth) {
// Search in annotations
for (Annotation annotation : annotations) {
- // Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
- if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) &&
- ((annotation.annotationType() == annotationType
- || annotation.annotationType().getName().equals(annotationName)) || metaDepth > 0)) {
- T result = processor.process(annotatedElement, annotation, metaDepth);
- if (result != null) {
- if (processor.aggregates() && metaDepth == 0) {
- processor.getAggregatedResults().add(result);
+ if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
+
+ // TODO Check non-repeatable annotations first, once we have sorted out
+ // the metaDepth nuances of getMetaAnnotationTypes().
+
+ // Repeatable annotations in container?
+ if (annotation.annotationType() == containerType) {
+ for (Annotation contained : getRawAnnotationsFromContainer(annotatedElement, annotation)) {
+ T result = processor.process(annotatedElement, contained, metaDepth);
+ if (result != null) {
+ // No need to post-process since repeatable annotations within a
+ // container cannot be composed annotations.
+ processor.getAggregatedResults().add(result);
+ }
}
- else {
- return result;
+ }
+ else if ((annotation.annotationType() == annotationType
+ || annotation.annotationType().getName().equals(annotationName)) || metaDepth > 0) {
+
+ // Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
+ T result = processor.process(annotatedElement, annotation, metaDepth);
+ if (result != null) {
+ if (processor.aggregates() && metaDepth == 0) {
+ processor.getAggregatedResults().add(result);
+ }
+ else {
+ return result;
+ }
}
}
}
@@ -947,7 +1068,7 @@ public class AnnotatedElementUtils {
for (Annotation annotation : annotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
T result = searchWithGetSemantics(annotation.annotationType(), annotationType,
- annotationName, processor, visited, metaDepth + 1);
+ annotationName, containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(annotatedElement, annotation, result);
if (processor.aggregates() && metaDepth == 0) {
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
index 09361ee904..20edf59b3d 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
@@ -57,6 +57,7 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.*;
* @since 4.0.3
* @see AnnotationUtilsTests
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
+ * @see ComposedRepeatableAnnotationsTests
*/
public class AnnotatedElementUtilsTests {
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/ComposedRepeatableAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/ComposedRepeatableAnnotationsTests.java
index 2876c55db5..5cbc10d9a8 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/ComposedRepeatableAnnotationsTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/ComposedRepeatableAnnotationsTests.java
@@ -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.
*
* See SPR-13973.
*
* @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 annotations = getMergedRepeatableAnnotations(element, Noninherited.class);
+ assertNoninheritedRepeatableAnnotations(annotations);
+ }
+
+ @Test
+ public void getNoninheritedComposedRepeatableAnnotationsOnSuperclass() {
+ Class> element = SubNoninheritedRepeatableClass.class;
+ Set 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 annotations = findMergedRepeatableAnnotations(element, Noninherited.class);
+ assertNoninheritedRepeatableAnnotations(annotations);
+ }
+
+ @Test
+ public void findNoninheritedComposedRepeatableAnnotationsOnSuperclass() {
+ Class> element = SubNoninheritedRepeatableClass.class;
+ Set 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 peteRepeats = getMergedRepeatableAnnotations(element, PeteRepeat.class);
+ assertNotNull(peteRepeats);
+ assertEquals(3, peteRepeats.size());
+
+ Iterator 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 annotations) {
+ assertNotNull(annotations);
+ assertEquals(3, annotations.size());
+
+ Iterator 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 {
+ }
+
}
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
index 395b9c31b5..fc2259acf4 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
@@ -42,6 +42,7 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.*;
* @since 4.3
* @see AnnotatedElementUtils
* @see AnnotatedElementUtilsTests
+ * @see ComposedRepeatableAnnotationsTests
*/
public class MultipleComposedAnnotationsOnSingleAnnotatedElementTests {