Polish MergedAnnotation support
This commit is contained in:
parent
cfc4a59135
commit
785e8d8116
|
@ -647,7 +647,7 @@ final class AnnotationTypeMapping {
|
||||||
!ObjectUtils.nullSafeEquals(lastValue, value)) {
|
!ObjectUtils.nullSafeEquals(lastValue, value)) {
|
||||||
String on = (source != null) ? " declared on " + source : "";
|
String on = (source != null) ? " declared on " + source : "";
|
||||||
throw new AnnotationConfigurationException(String.format(
|
throw new AnnotationConfigurationException(String.format(
|
||||||
"Different @AliasFor mirror values for annotation [%s]%s, attribute '%s' " +
|
"Different @AliasFor mirror values for annotation [%s]%s; attribute '%s' " +
|
||||||
"and its alias '%s' are declared with values of [%s] and [%s].",
|
"and its alias '%s' are declared with values of [%s] and [%s].",
|
||||||
getAnnotationType().getName(), on,
|
getAnnotationType().getName(), on,
|
||||||
attributes.get(result).getName(),
|
attributes.get(result).getName(),
|
||||||
|
|
|
@ -89,11 +89,11 @@ final class AttributeMethods {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this instance only contains only a single attribute named
|
* Determine if this instance only contains a single attribute named
|
||||||
* {@code value}.
|
* {@code value}.
|
||||||
* @return {@code true} if this is only a value attribute
|
* @return {@code true} if there is only a value attribute
|
||||||
*/
|
*/
|
||||||
boolean isOnlyValueAttribute() {
|
boolean hasOnlyValueAttribute() {
|
||||||
return (this.attributeMethods.length == 1 &&
|
return (this.attributeMethods.length == 1 &&
|
||||||
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName()));
|
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public interface MergedAnnotation<A extends Annotation> {
|
||||||
* Return a complete type hierarchy from this annotation to the
|
* Return a complete type hierarchy from this annotation to the
|
||||||
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
|
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
|
||||||
* merged annotation instance.
|
* merged annotation instance.
|
||||||
* @return the type heirarchy for the annotation
|
* @return the type hierarchy for the annotation
|
||||||
* @see MergedAnnotationPredicates#unique(Function)
|
* @see MergedAnnotationPredicates#unique(Function)
|
||||||
*/
|
*/
|
||||||
List<Class<? extends Annotation>> getTypeHierarchy();
|
List<Class<? extends Annotation>> getTypeHierarchy();
|
||||||
|
|
|
@ -35,10 +35,10 @@ import org.springframework.lang.Nullable;
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Explicit and Implicit {@link AliasFor @AliasFor} declarations on one or
|
* <li>Explicit and Implicit {@link AliasFor @AliasFor} declarations on one or
|
||||||
* more attributes within the annotation.</li>
|
* more attributes within the annotation</li>
|
||||||
* <li>Explicit {@link AliasFor @AliasFor} declarations for a meta-annotation.</li>
|
* <li>Explicit {@link AliasFor @AliasFor} declarations for a meta-annotation</li>
|
||||||
* <li>Convention based attribute aliases for a meta-annotation</li>
|
* <li>Convention based attribute aliases for a meta-annotation</li>
|
||||||
* <li>From a meta-annotation declaration.</li>
|
* <li>From a meta-annotation declaration</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>For example, a {@code @PostMapping} annotation might be defined as follows:
|
* <p>For example, a {@code @PostMapping} annotation might be defined as follows:
|
||||||
|
@ -116,7 +116,7 @@ import org.springframework.lang.Nullable;
|
||||||
* // get all ExampleAnnotation declarations (including any meta-annotations) and
|
* // get all ExampleAnnotation declarations (including any meta-annotations) and
|
||||||
* // print the merged "value" attributes
|
* // print the merged "value" attributes
|
||||||
* mergedAnnotations.stream(ExampleAnnotation.class).map(
|
* mergedAnnotations.stream(ExampleAnnotation.class).map(
|
||||||
* a -> a.getString("value")).forEach(System.out::println);
|
* a -> a.getString("value")).forEach(System.out::println);
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
|
|
@ -163,11 +163,8 @@ public abstract class RepeatableContainers {
|
||||||
|
|
||||||
private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
|
private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
|
AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
|
||||||
if (methods.isOnlyValueAttribute()) {
|
if (methods.hasOnlyValueAttribute()) {
|
||||||
Method method = methods.get("value");
|
Method method = methods.get(0);
|
||||||
if (method == null) {
|
|
||||||
return NONE;
|
|
||||||
}
|
|
||||||
Class<?> returnType = method.getReturnType();
|
Class<?> returnType = method.getReturnType();
|
||||||
if (returnType.isArray()) {
|
if (returnType.isArray()) {
|
||||||
Class<?> componentType = returnType.getComponentType();
|
Class<?> componentType = returnType.getComponentType();
|
||||||
|
@ -201,7 +198,7 @@ public abstract class RepeatableContainers {
|
||||||
if (container == null) {
|
if (container == null) {
|
||||||
container = deduceContainer(repeatable);
|
container = deduceContainer(repeatable);
|
||||||
}
|
}
|
||||||
Method valueMethod = AttributeMethods.forAnnotationType(container).get("value");
|
Method valueMethod = AttributeMethods.forAnnotationType(container).get(MergedAnnotation.VALUE);
|
||||||
try {
|
try {
|
||||||
if (valueMethod == null) {
|
if (valueMethod == null) {
|
||||||
throw new NoSuchMethodException("No value method found");
|
throw new NoSuchMethodException("No value method found");
|
||||||
|
|
|
@ -394,7 +394,7 @@ public class AnnotationTypeMappingsTests {
|
||||||
.withMessage("Different @AliasFor mirror values for annotation ["
|
.withMessage("Different @AliasFor mirror values for annotation ["
|
||||||
+ AliasPair.class.getName() + "] declared on "
|
+ AliasPair.class.getName() + "] declared on "
|
||||||
+ WithDifferentValueAliasPair.class.getName()
|
+ WithDifferentValueAliasPair.class.getName()
|
||||||
+ ", attribute 'a' and its alias 'b' are declared with values of [test1] and [test2].");
|
+ "; attribute 'a' and its alias 'b' are declared with values of [test1] and [test2].");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -52,52 +52,46 @@ public class AttributeMethodsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forAnnotationTypeWhenHasMultipleAttributesReturnsAttributes() {
|
public void forAnnotationTypeWhenHasMultipleAttributesReturnsAttributes() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
|
||||||
assertThat(methods.get("value").getName()).isEqualTo("value");
|
assertThat(methods.get("value").getName()).isEqualTo("value");
|
||||||
assertThat(methods.get("intValue").getName()).isEqualTo("intValue");
|
assertThat(methods.get("intValue").getName()).isEqualTo("intValue");
|
||||||
assertThat(getAll(methods)).flatExtracting(Method::getName).containsExactly(
|
assertThat(getAll(methods)).flatExtracting(Method::getName).containsExactly("intValue", "value");
|
||||||
"intValue", "value");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isOnlyValueAttributeWhenHasOnlyValueAttributeReturnsTrue() {
|
public void hasOnlyValueAttributeWhenHasOnlyValueAttributeReturnsTrue() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(ValueOnly.class);
|
AttributeMethods methods = AttributeMethods.forAnnotationType(ValueOnly.class);
|
||||||
assertThat(methods.isOnlyValueAttribute()).isTrue();
|
assertThat(methods.hasOnlyValueAttribute()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isOnlyValueAttributeWhenHasOnlySingleNonValueAttributeReturnsFalse() {
|
public void hasOnlyValueAttributeWhenHasOnlySingleNonValueAttributeReturnsFalse() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(NonValueOnly.class);
|
AttributeMethods methods = AttributeMethods.forAnnotationType(NonValueOnly.class);
|
||||||
assertThat(methods.isOnlyValueAttribute()).isFalse();
|
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isOnlyValueAttributeWhenHasOnlyMultipleAttributesIncludingValueReturnsFalse() {
|
public void hasOnlyValueAttributeWhenHasOnlyMultipleAttributesIncludingValueReturnsFalse() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||||
assertThat(methods.isOnlyValueAttribute()).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void indexOfNameReturnsIndex() {
|
public void indexOfNameReturnsIndex() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
|
||||||
assertThat(methods.indexOf("value")).isEqualTo(1);
|
assertThat(methods.indexOf("value")).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void indexOfMethodReturnsIndex() throws Exception {
|
public void indexOfMethodReturnsIndex() throws Exception {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
|
||||||
Method method = MultipleAttributes.class.getDeclaredMethod("value");
|
Method method = MultipleAttributes.class.getDeclaredMethod("value");
|
||||||
assertThat(methods.indexOf(method)).isEqualTo(1);
|
assertThat(methods.indexOf(method)).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sizeReturnsSize() {
|
public void sizeReturnsSize() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
|
||||||
assertThat(methods.size()).isEqualTo(2);
|
assertThat(methods.size()).isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +103,7 @@ public class AttributeMethodsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canThrowTypeNotPresentExceptionWhenHasClassArrayAttributeReturnsTrue() {
|
public void canThrowTypeNotPresentExceptionWhenHasClassArrayAttributeReturnsTrue() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(ClassArrayValue.class);
|
||||||
ClassArrayValue.class);
|
|
||||||
assertThat(methods.canThrowTypeNotPresentException(0)).isTrue();
|
assertThat(methods.canThrowTypeNotPresentException(0)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,15 +115,13 @@ public class AttributeMethodsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasDefaultValueMethodWhenHasDefaultValueMethodReturnsTrue() {
|
public void hasDefaultValueMethodWhenHasDefaultValueMethodReturnsTrue() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(DefaultValueAttribute.class);
|
||||||
DefaultValueAttribute.class);
|
|
||||||
assertThat(methods.hasDefaultValueMethod()).isTrue();
|
assertThat(methods.hasDefaultValueMethod()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasDefaultValueMethodWhenHasNoDefaultValueMethodsReturnsFalse() {
|
public void hasDefaultValueMethodWhenHasNoDefaultValueMethodsReturnsFalse() {
|
||||||
AttributeMethods methods = AttributeMethods.forAnnotationType(
|
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||||
MultipleAttributes.class);
|
|
||||||
assertThat(methods.hasDefaultValueMethod()).isFalse();
|
assertThat(methods.hasDefaultValueMethod()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +129,7 @@ public class AttributeMethodsTests {
|
||||||
public void isValidWhenHasTypeNotPresentExceptionReturnsFalse() {
|
public void isValidWhenHasTypeNotPresentExceptionReturnsFalse() {
|
||||||
ClassValue annotation = mockAnnotation(ClassValue.class);
|
ClassValue annotation = mockAnnotation(ClassValue.class);
|
||||||
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
||||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(
|
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||||
annotation.annotationType());
|
|
||||||
assertThat(attributes.isValid(annotation)).isFalse();
|
assertThat(attributes.isValid(annotation)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +138,7 @@ public class AttributeMethodsTests {
|
||||||
public void isValidWhenDoesNotHaveTypeNotPresentExceptionReturnsTrue() {
|
public void isValidWhenDoesNotHaveTypeNotPresentExceptionReturnsTrue() {
|
||||||
ClassValue annotation = mock(ClassValue.class);
|
ClassValue annotation = mock(ClassValue.class);
|
||||||
given(annotation.value()).willReturn((Class) InputStream.class);
|
given(annotation.value()).willReturn((Class) InputStream.class);
|
||||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(
|
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||||
annotation.annotationType());
|
|
||||||
assertThat(attributes.isValid(annotation)).isTrue();
|
assertThat(attributes.isValid(annotation)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,10 +146,8 @@ public class AttributeMethodsTests {
|
||||||
public void validateWhenHasTypeNotPresentExceptionThrowsException() {
|
public void validateWhenHasTypeNotPresentExceptionThrowsException() {
|
||||||
ClassValue annotation = mockAnnotation(ClassValue.class);
|
ClassValue annotation = mockAnnotation(ClassValue.class);
|
||||||
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
||||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(
|
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||||
annotation.annotationType());
|
assertThatIllegalStateException().isThrownBy(() -> attributes.validate(annotation));
|
||||||
assertThatIllegalStateException().isThrownBy(() ->
|
|
||||||
attributes.validate(annotation));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -168,8 +155,7 @@ public class AttributeMethodsTests {
|
||||||
public void validateWhenDoesNotHaveTypeNotPresentExceptionThrowsNothing() {
|
public void validateWhenDoesNotHaveTypeNotPresentExceptionThrowsNothing() {
|
||||||
ClassValue annotation = mockAnnotation(ClassValue.class);
|
ClassValue annotation = mockAnnotation(ClassValue.class);
|
||||||
given(annotation.value()).willReturn((Class) InputStream.class);
|
given(annotation.value()).willReturn((Class) InputStream.class);
|
||||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(
|
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||||
annotation.annotationType());
|
|
||||||
attributes.validate(annotation);
|
attributes.validate(annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +175,11 @@ public class AttributeMethodsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface NoAttributes {
|
@interface NoAttributes {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface MultipleAttributes {
|
@interface MultipleAttributes {
|
||||||
|
|
||||||
int intValue();
|
int intValue();
|
||||||
|
|
||||||
|
@ -203,35 +188,35 @@ public class AttributeMethodsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface ValueOnly {
|
@interface ValueOnly {
|
||||||
|
|
||||||
String value();
|
String value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface NonValueOnly {
|
@interface NonValueOnly {
|
||||||
|
|
||||||
String test();
|
String test();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface ClassValue {
|
@interface ClassValue {
|
||||||
|
|
||||||
Class<?> value();
|
Class<?> value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface ClassArrayValue {
|
@interface ClassArrayValue {
|
||||||
|
|
||||||
Class<?>[] value();
|
Class<?>[] value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface DefaultValueAttribute {
|
@interface DefaultValueAttribute {
|
||||||
|
|
||||||
String one();
|
String one();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue