Introduce getAliasedStringArray() in AnnotationAttributes

Issue: SPR-11393
This commit is contained in:
Sam Brannen
2015-06-03 21:18:31 -04:00
parent 08c032d9fd
commit e5dc6e964c
3 changed files with 172 additions and 37 deletions

View File

@@ -17,12 +17,14 @@
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -131,6 +133,58 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
return doGet(attributeName, String[].class);
}
/**
* Get the value stored under the specified {@code attributeName} as an
* array of strings, 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 strings
* @throws IllegalArgumentException if the attribute and its alias do
* not exist or are not of type {@code String[]}
* @throws AnnotationConfigurationException if the attribute and its
* alias are both present with different non-empty values
* @since 4.2
*/
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;
}
/**
* Get the value stored under the specified {@code attributeName} as a
* boolean.
@@ -284,29 +338,52 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
* @return the value
* @throws IllegalArgumentException if the attribute does not exist or
* if it is not of the expected type
* @since 4.2
*/
@SuppressWarnings("unchecked")
private <T> T doGet(String attributeName, Class<T> expectedType) {
Assert.hasText(attributeName, "attributeName must not be null or empty");
Object value = get(attributeName);
if (value == null) {
assertAttributePresence(attributeName, value);
if (!expectedType.isInstance(value) && expectedType.isArray()
&& expectedType.getComponentType().isInstance(value)) {
Object array = Array.newInstance(expectedType.getComponentType(), 1);
Array.set(array, 0, value);
value = array;
}
assertAttributeType(attributeName, value, expectedType);
return (T) value;
}
private void assertAttributePresence(String attributeName, Object attributeValue) {
if (attributeValue == null) {
throw new IllegalArgumentException(String.format(
"Attribute '%s' not found in attributes for annotation [%s]", attributeName, this.displayName));
}
if (!expectedType.isInstance(value)) {
if (expectedType.isArray() && expectedType.getComponentType().isInstance(value)) {
Object array = Array.newInstance(expectedType.getComponentType(), 1);
Array.set(array, 0, value);
value = array;
}
else {
throw new IllegalArgumentException(String.format(
"Attribute '%s' is of type [%s], but [%s] was expected in attributes for annotation [%s]",
attributeName, value.getClass().getSimpleName(), expectedType.getSimpleName(), this.displayName));
}
}
private void assertAttributePresence(String attributeName, String aliasName, Object attributeValue) {
if (attributeValue == null) {
throw new IllegalArgumentException(String.format(
"Neither attribute '%s' nor its alias '%s' was found in attributes for annotation [%s]", attributeName,
aliasName, this.displayName));
}
return (T) value;
}
private void assertAttributeType(String attributeName, Object attributeValue, Class<?> expectedType) {
if (!expectedType.isInstance(attributeValue)) {
throw new IllegalArgumentException(String.format(
"Attribute '%s' is of type [%s], but [%s] was expected in attributes for annotation [%s]",
attributeName, attributeValue.getClass().getSimpleName(), expectedType.getSimpleName(),
this.displayName));
}
}
private String[] getStringArrayWithoutNullCheck(String attributeName) {
Object value = get(attributeName);
if (value != null) {
assertAttributeType(attributeName, value, String[].class);
}
return (String[]) value;
}
/**
@@ -354,7 +431,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
return String.valueOf(value);
}
/**
* Return an {@link AnnotationAttributes} instance based on the given map.
* <p>If the map is already an {@code AnnotationAttributes} instance, it