Browse Source

Introduce getAliasedClassArray() in AnnotationAttributes

Issue: SPR-11393
pull/816/head
Sam Brannen 11 years ago
parent
commit
384ee69300
  1. 162
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java
  2. 72
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java

162
spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java

@ -114,7 +114,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -114,7 +114,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public String getString(String attributeName) {
return doGet(attributeName, String.class);
return getRequiredAttribute(attributeName, String.class);
}
/**
@ -130,7 +130,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -130,7 +130,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public String[] getStringArray(String attributeName) {
return doGet(attributeName, String[].class);
return getRequiredAttribute(attributeName, String[].class);
}
/**
@ -157,32 +157,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -157,32 +157,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
*/
public String[] getAliasedStringArray(String attributeName, Class<? extends Annotation> annotationType,
Object annotationSource) {
Assert.hasText(attributeName, "attributeName must not be null or empty");
Assert.notNull(annotationType, "annotationType must not be null");
String[] attributeValue = getStringArrayWithoutNullCheck(attributeName);
String aliasName = AnnotationUtils.getAttributeAliasMap(annotationType).get(attributeName);
String[] aliasValue = getStringArrayWithoutNullCheck(aliasName);
boolean attributeDeclared = !ObjectUtils.isEmpty(attributeValue);
boolean aliasDeclared = !ObjectUtils.isEmpty(aliasValue);
if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) && attributeDeclared && aliasDeclared) {
String elementName = (annotationSource == null ? "unknown element" : annotationSource.toString());
String msg = String.format("In annotation [%s] declared on [%s], "
+ "attribute [%s] and its alias [%s] are present with values of [%s] and [%s], "
+ "but only one is permitted.", this.displayName, elementName, attributeName, aliasName,
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue));
throw new AnnotationConfigurationException(msg);
}
if (!attributeDeclared) {
attributeValue = aliasValue;
}
assertAttributePresence(attributeName, aliasName, attributeValue);
return attributeValue;
return getRequiredArrayWithAttributeAlias(attributeName, annotationType, annotationSource, String[].class);
}
/**
@ -195,7 +170,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -195,7 +170,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public boolean getBoolean(String attributeName) {
return doGet(attributeName, Boolean.class);
return getRequiredAttribute(attributeName, Boolean.class);
}
/**
@ -209,7 +184,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -209,7 +184,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
*/
@SuppressWarnings("unchecked")
public <N extends Number> N getNumber(String attributeName) {
return (N) doGet(attributeName, Number.class);
return (N) getRequiredAttribute(attributeName, Number.class);
}
/**
@ -223,7 +198,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -223,7 +198,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
*/
@SuppressWarnings("unchecked")
public <E extends Enum<?>> E getEnum(String attributeName) {
return (E) doGet(attributeName, Enum.class);
return (E) getRequiredAttribute(attributeName, Enum.class);
}
/**
@ -237,7 +212,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -237,7 +212,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
*/
@SuppressWarnings("unchecked")
public <T> Class<? extends T> getClass(String attributeName) {
return doGet(attributeName, Class.class);
return getRequiredAttribute(attributeName, Class.class);
}
/**
@ -253,7 +228,34 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -253,7 +228,34 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public Class<?>[] getClassArray(String attributeName) {
return doGet(attributeName, Class[].class);
return getRequiredAttribute(attributeName, Class[].class);
}
/**
* Get the value stored under the specified {@code attributeName} as an
* array of classes, taking into account alias semantics defined via
* {@link AliasFor @AliasFor}.
* <p>If there is no value stored under the specified {@code attributeName}
* but the attribute has an alias declared via {@code @AliasFor}, the
* value of the alias will be returned.
*
* @param attributeName the name of the attribute to get; never
* {@code null} or empty
* @param annotationType the type of annotation represented by this
* {@code AnnotationAttributes} instance; never {@code null}
* @param annotationSource the source of the annotation represented by
* this {@code AnnotationAttributes} (e.g., the {@link AnnotatedElement});
* or {@code null} if unknown
* @return the array of classes
* @throws IllegalArgumentException if the attribute and its alias do
* not exist or are not of type {@code Class[]}
* @throws AnnotationConfigurationException if the attribute and its
* alias are both present with different non-empty values
* @since 4.2
*/
public Class<?>[] getAliasedClassArray(String attributeName, Class<? extends Annotation> annotationType,
Object annotationSource) {
return getRequiredArrayWithAttributeAlias(attributeName, annotationType, annotationSource, Class[].class);
}
/**
@ -268,7 +270,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -268,7 +270,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public AnnotationAttributes getAnnotation(String attributeName) {
return doGet(attributeName, AnnotationAttributes.class);
return getRequiredAttribute(attributeName, AnnotationAttributes.class);
}
/**
@ -283,7 +285,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -283,7 +285,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* @since 4.2
*/
public <A extends Annotation> A getAnnotation(String attributeName, Class<A> annotationType) {
return doGet(attributeName, annotationType);
return getRequiredAttribute(attributeName, annotationType);
}
/**
@ -301,7 +303,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -301,7 +303,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
public AnnotationAttributes[] getAnnotationArray(String attributeName) {
return doGet(attributeName, AnnotationAttributes[].class);
return getRequiredAttribute(attributeName, AnnotationAttributes[].class);
}
/**
@ -321,7 +323,83 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -321,7 +323,83 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
@SuppressWarnings("unchecked")
public <A extends Annotation> A[] getAnnotationArray(String attributeName, Class<A> annotationType) {
Object array = Array.newInstance(annotationType, 0);
return (A[]) doGet(attributeName, array.getClass());
return (A[]) getRequiredAttribute(attributeName, array.getClass());
}
/**
* Get the value stored under the specified {@code attributeName} as an
* array of the {@code expectedType}, taking into account alias semantics
* defined via {@link AliasFor @AliasFor}.
* <p>If there is no value stored under the specified {@code attributeName}
* but the attribute has an alias declared via {@code @AliasFor}, the
* value of the alias will be returned.
*
* @param attributeName the name of the attribute to get; never
* {@code null} or empty
* @param annotationType the type of annotation represented by this
* {@code AnnotationAttributes} instance; never {@code null}
* @param annotationSource the source of the annotation represented by
* this {@code AnnotationAttributes} (e.g., the {@link AnnotatedElement});
* or {@code null} if unknown
* @param expectedType the expected array type; never {@code null}
* @return the array of values
* @throws IllegalArgumentException if the attribute and its alias do
* not exist or are not of the {@code expectedType}, or if the
* {@code expectedType} is not an array
* @throws AnnotationConfigurationException if the attribute and its
* alias are both present with different non-empty values
* @since 4.2
*/
private <T> T getRequiredArrayWithAttributeAlias(String attributeName, Class<? extends Annotation> annotationType,
Object annotationSource, Class<T> expectedType) {
Assert.hasText(attributeName, "attributeName must not be null or empty");
Assert.notNull(annotationType, "annotationType must not be null");
Assert.notNull(expectedType, "expectedType must not be null");
Assert.isTrue(expectedType.isArray(), "expectedType must be an array");
T attributeValue = getAttribute(attributeName, expectedType);
String aliasName = AnnotationUtils.getAttributeAliasMap(annotationType).get(attributeName);
T aliasValue = getAttribute(aliasName, expectedType);
boolean attributeDeclared = !ObjectUtils.isEmpty((Object[]) attributeValue);
boolean aliasDeclared = !ObjectUtils.isEmpty((Object[]) aliasValue);
if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) && attributeDeclared && aliasDeclared) {
String elementName = (annotationSource == null ? "unknown element" : annotationSource.toString());
String msg = String.format("In annotation [%s] declared on [%s], "
+ "attribute [%s] and its alias [%s] are present with values of [%s] and [%s], "
+ "but only one is permitted.", this.displayName, elementName, attributeName, aliasName,
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue));
throw new AnnotationConfigurationException(msg);
}
if (!attributeDeclared) {
attributeValue = aliasValue;
}
assertAttributePresence(attributeName, aliasName, attributeValue);
return attributeValue;
}
/**
* Get the value stored under the specified {@code attributeName},
* ensuring that the value is of the {@code expectedType}.
* @param attributeName the name of the attribute to get; never
* {@code null} or empty
* @param expectedType the expected type; never {@code null}
* @return the value
* @throws IllegalArgumentException if the attribute is not of the
* expected type
* @see #getRequiredAttribute(String, Class)
*/
@SuppressWarnings("unchecked")
private <T> T getAttribute(String attributeName, Class<T> expectedType) {
Object value = get(attributeName);
if (value != null) {
assertAttributeType(attributeName, value, expectedType);
}
return (T) value;
}
/**
@ -340,7 +418,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -340,7 +418,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* if it is not of the expected type
*/
@SuppressWarnings("unchecked")
private <T> T doGet(String attributeName, Class<T> expectedType) {
private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
Assert.hasText(attributeName, "attributeName must not be null or empty");
Object value = get(attributeName);
assertAttributePresence(attributeName, value);
@ -378,14 +456,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> { @@ -378,14 +456,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
}
}
private String[] getStringArrayWithoutNullCheck(String attributeName) {
Object value = get(attributeName);
if (value != null) {
assertAttributeType(attributeName, value, String[].class);
}
return (String[]) value;
}
/**
* Store the supplied {@code value} in this map under the specified
* {@code key}, unless a value is already stored under the key.

72
spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java

@ -209,6 +209,71 @@ public class AnnotationAttributesTests { @@ -209,6 +209,71 @@ public class AnnotationAttributesTests {
return attributes.getAliasedStringArray(attributeName, ContextConfig.class, null);
}
@Test
public void getAliasedClassArray() {
final Class<?>[] INPUT = new Class<?>[] { String.class };
final Class<?>[] EMPTY = new Class<?>[0];
attributes.clear();
attributes.put("classes", INPUT);
assertArrayEquals(INPUT, getAliasedClassArray("classes"));
assertArrayEquals(INPUT, getAliasedClassArray("value"));
attributes.clear();
attributes.put("value", INPUT);
assertArrayEquals(INPUT, getAliasedClassArray("classes"));
assertArrayEquals(INPUT, getAliasedClassArray("value"));
attributes.clear();
attributes.put("classes", INPUT);
attributes.put("value", INPUT);
assertArrayEquals(INPUT, getAliasedClassArray("classes"));
assertArrayEquals(INPUT, getAliasedClassArray("value"));
attributes.clear();
attributes.put("classes", INPUT);
attributes.put("value", EMPTY);
assertArrayEquals(INPUT, getAliasedClassArray("classes"));
assertArrayEquals(INPUT, getAliasedClassArray("value"));
attributes.clear();
attributes.put("classes", EMPTY);
attributes.put("value", INPUT);
assertArrayEquals(INPUT, getAliasedClassArray("classes"));
assertArrayEquals(INPUT, getAliasedClassArray("value"));
attributes.clear();
attributes.put("classes", EMPTY);
attributes.put("value", EMPTY);
assertArrayEquals(EMPTY, getAliasedClassArray("classes"));
assertArrayEquals(EMPTY, getAliasedClassArray("value"));
}
@Test
public void getAliasedClassArrayWithMissingAliasedAttributes() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage(equalTo("Neither attribute 'classes' nor its alias 'value' was found in attributes for annotation [unknown]"));
getAliasedClassArray("classes");
}
@Test
public void getAliasedClassArrayWithDifferentAliasedValues() {
attributes.put("classes", new Class[] { String.class });
attributes.put("value", new Class[] { Number.class });
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(containsString("In annotation [unknown]"));
exception.expectMessage(containsString("attribute [classes] and its alias [value]"));
exception.expectMessage(containsString("[{class java.lang.String}] and [{class java.lang.Number}]"));
exception.expectMessage(containsString("but only one is permitted"));
getAliasedClassArray("classes");
}
private Class<?>[] getAliasedClassArray(String attributeName) {
return attributes.getAliasedClassArray(attributeName, Filter.class, null);
}
enum Color {
RED, WHITE, BLUE
@ -216,6 +281,13 @@ public class AnnotationAttributesTests { @@ -216,6 +281,13 @@ public class AnnotationAttributesTests {
@Retention(RetentionPolicy.RUNTIME)
@interface Filter {
@AliasFor(attribute = "classes")
Class<?>[] value() default {};
@AliasFor(attribute = "value")
Class<?>[] classes() default {};
String pattern();
}

Loading…
Cancel
Save