|
|
|
@ -17,12 +17,14 @@
@@ -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> {
@@ -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> {
@@ -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) { |
|
|
|
|
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)) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
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)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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, value.getClass().getSimpleName(), expectedType.getSimpleName(), this.displayName)); |
|
|
|
|
attributeName, attributeValue.getClass().getSimpleName(), expectedType.getSimpleName(), |
|
|
|
|
this.displayName)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return (T) value; |
|
|
|
|
|
|
|
|
|
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> {
@@ -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 |
|
|
|
|