Browse Source

Merge from sbrannen/SPR-13405

* SPR-13405:
  Support transitive implicit attribute aliases with @AliasFor
pull/868/head
Sam Brannen 9 years ago
parent
commit
8e14520bf5
  1. 15
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  2. 240
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  3. 46
      spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
  4. 167
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

15
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -956,21 +956,14 @@ public class AnnotatedElementUtils { @@ -956,21 +956,14 @@ public class AnnotatedElementUtils {
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
String attributeName = attributeMethod.getName();
List<String> aliases = AnnotationUtils.getAliasedAttributeNames(attributeMethod, targetAnnotationType);
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
// Explicit annotation attribute override declared via @AliasFor
if (!aliases.isEmpty()) {
if (aliases.size() != 1) {
throw new IllegalStateException(String.format(
"Alias list for annotation attribute [%s] must contain at most one element: %s",
attributeMethod, aliases));
}
String aliasedAttributeName = aliases.get(0);
if (attributes.containsKey(aliasedAttributeName)) {
overrideAttribute(element, annotation, attributes, attributeName, aliasedAttributeName);
if (attributeOverrideName != null) {
if (attributes.containsKey(attributeOverrideName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeOverrideName);
}
}
// Implicit annotation attribute override based on convention
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeName);

240
spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

@ -137,6 +137,9 @@ public abstract class AnnotationUtils { @@ -137,6 +137,9 @@ public abstract class AnnotationUtils {
private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache =
new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);
private static final Map<Method, AliasDescriptor> aliasDescriptorCache =
new ConcurrentReferenceHashMap<Method, AliasDescriptor>(256);
private static transient Log logger;
@ -1436,7 +1439,7 @@ public abstract class AnnotationUtils { @@ -1436,7 +1439,7 @@ public abstract class AnnotationUtils {
map = new HashMap<String, List<String>>();
for (Method attribute : getAttributeMethods(annotationType)) {
List<String> aliasNames = getAliasedAttributeNames(attribute);
List<String> aliasNames = getAttributeAliasNames(attribute);
if (!aliasNames.isEmpty()) {
map.put(attribute.getName(), aliasNames);
}
@ -1469,7 +1472,7 @@ public abstract class AnnotationUtils { @@ -1469,7 +1472,7 @@ public abstract class AnnotationUtils {
synthesizable = Boolean.FALSE;
for (Method attribute : getAttributeMethods(annotationType)) {
if (!getAliasedAttributeNames(attribute).isEmpty()) {
if (!getAttributeAliasNames(attribute).isEmpty()) {
synthesizable = Boolean.TRUE;
break;
}
@ -1497,7 +1500,6 @@ public abstract class AnnotationUtils { @@ -1497,7 +1500,6 @@ public abstract class AnnotationUtils {
/**
* Get the names of the aliased attributes configured via
* {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
* <p>This method does not resolve meta-annotation attribute overrides.
* @param attribute the attribute to find aliases for; never {@code null}
* @return the names of the aliased attributes; never {@code null}, though
* potentially <em>empty</em>
@ -1506,74 +1508,39 @@ public abstract class AnnotationUtils { @@ -1506,74 +1508,39 @@ public abstract class AnnotationUtils {
* @throws AnnotationConfigurationException if invalid configuration of
* {@code @AliasFor} is detected
* @since 4.2
* @see #getAliasedAttributeNames(Method, Class)
* @see #getAttributeOverrideName(Method, Class)
*/
static List<String> getAliasedAttributeNames(Method attribute) {
return getAliasedAttributeNames(attribute, (Class<? extends Annotation>) null);
static List<String> getAttributeAliasNames(Method attribute) {
Assert.notNull(attribute, "attribute must not be null");
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
return (descriptor == null ? Collections.emptyList() : descriptor.getAttributeAliasNames());
}
/**
* Get the names of the aliased attributes configured via
* Get the name of the overridden attribute configured via
* {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
* <p>If the supplied {@code metaAnnotationType} is non-null, the
* returned list will contain at most one element.
* @param attribute the attribute to find aliases for; never {@code null}
* @param metaAnnotationType the type of meta-annotation in which an
* aliased attribute is allowed to be declared; {@code null} implies
* <em>within the same annotation</em> as the supplied attribute
* @return the names of the aliased attributes; never {@code null}, though
* potentially <em>empty</em>
* @param attribute the attribute from which to retrieve the override;
* never {@code null}
* @param metaAnnotationType the type of meta-annotation in which the
* overridden attribute is allowed to be declared
* @return the name of the overridden attribute, or {@code null} if not
* found or not applicable for the specified meta-annotation type
* @throws IllegalArgumentException if the supplied attribute method is
* {@code null} or not from an annotation, or if the supplied meta-annotation
* type is {@link Annotation}
* type is {@code null} or {@link Annotation}
* @throws AnnotationConfigurationException if invalid configuration of
* {@code @AliasFor} is detected
* @since 4.2
*/
static List<String> getAliasedAttributeNames(Method attribute, Class<? extends Annotation> metaAnnotationType) {
Assert.notNull(attribute, "attribute method must not be null");
static String getAttributeOverrideName(Method attribute, Class<? extends Annotation> metaAnnotationType) {
Assert.notNull(attribute, "attribute must not be null");
Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
"metaAnnotationType must not be java.lang.annotation.Annotation");
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
// No alias declared via @AliasFor?
if (descriptor == null) {
return Collections.emptyList();
}
// Searching for explicit meta-annotation attribute override?
if (metaAnnotationType != null) {
if (descriptor.isAliasFor(metaAnnotationType)) {
return Collections.singletonList(descriptor.aliasedAttributeName);
}
// Else: explicit attribute override for a different meta-annotation
return Collections.emptyList();
}
// Explicit alias pair?
if (descriptor.isAliasPair) {
return Collections.singletonList(descriptor.aliasedAttributeName);
}
// Else: search for implicit aliases
List<String> aliases = new ArrayList<String>();
for (Method currentAttribute : getAttributeMethods(descriptor.sourceAnnotationType)) {
// An attribute cannot alias itself
if (attribute.equals(currentAttribute)) {
continue;
}
// If two attributes override the same attribute in the same meta-annotation,
// they are "implicit" aliases for each other.
AliasDescriptor otherDescriptor = AliasDescriptor.from(currentAttribute);
if (descriptor.equals(otherDescriptor)) {
descriptor.validateAgainst(otherDescriptor);
aliases.add(otherDescriptor.sourceAttributeName);
}
}
return aliases;
return (descriptor == null ? null : descriptor.getAttributeOverrideName(metaAnnotationType));
}
/**
@ -1912,6 +1879,9 @@ public abstract class AnnotationUtils { @@ -1912,6 +1879,9 @@ public abstract class AnnotationUtils {
* on a given annotation attribute and includes support for validating
* the configuration of aliases (both explicit and implicit).
* @since 4.2.1
* @see #from
* @see #getAttributeAliasNames
* @see #getAttributeOverrideName
*/
private static class AliasDescriptor {
@ -1921,6 +1891,8 @@ public abstract class AnnotationUtils { @@ -1921,6 +1891,8 @@ public abstract class AnnotationUtils {
private final String sourceAttributeName;
private final Method aliasedAttribute;
private final Class<? extends Annotation> aliasedAnnotationType;
private final String aliasedAttributeName;
@ -1929,37 +1901,55 @@ public abstract class AnnotationUtils { @@ -1929,37 +1901,55 @@ public abstract class AnnotationUtils {
/**
* Create a new {@code AliasDescriptor} <em>from</em> the declaration
* Create an {@code AliasDescriptor} <em>from</em> the declaration
* of {@code @AliasFor} on the supplied annotation attribute and
* validate the configuration of {@code @AliasFor}.
* @param attribute the annotation attribute that is annotated with
* {@code @AliasFor}
* @return a new alias descriptor, or {@code null} if the attribute
* @return an alias descriptor, or {@code null} if the attribute
* is not annotated with {@code @AliasFor}
* @see #validateAgainst(AliasDescriptor)
* @see #validateAgainst
*/
public static AliasDescriptor from(Method attribute) {
AliasDescriptor descriptor = aliasDescriptorCache.get(attribute);
if (descriptor != null) {
return descriptor;
}
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
if (aliasFor == null) {
return null;
}
AliasDescriptor descriptor = new AliasDescriptor(attribute, aliasFor);
descriptor = new AliasDescriptor(attribute, aliasFor);
descriptor.validate();
aliasDescriptorCache.put(attribute, descriptor);
return descriptor;
}
@SuppressWarnings("unchecked")
private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
Class<?> declaringClass = sourceAttribute.getDeclaringClass();
Assert.isTrue(declaringClass.isAnnotation(), "attribute method must be from an annotation");
Assert.isTrue(declaringClass.isAnnotation(), "sourceAttribute must be from an annotation");
this.sourceAttribute = sourceAttribute;
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
this.sourceAttributeName = this.sourceAttribute.getName();
this.aliasedAnnotationType = (Annotation.class.equals(aliasFor.annotation()) ? this.sourceAnnotationType
: aliasFor.annotation());
this.aliasedAttributeName = getAliasedAttributeName(aliasFor, this.sourceAttribute);
try {
this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
}
catch (NoSuchMethodException ex) {
String msg = String.format(
"Attribute [%s] in annotation [%s] is declared as an @AliasFor nonexistent attribute [%s] in annotation [%s].",
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
this.aliasedAnnotationType.getName());
throw new AnnotationConfigurationException(msg, ex);
}
this.isAliasPair = this.sourceAnnotationType.equals(this.aliasedAnnotationType);
}
@ -1974,20 +1964,8 @@ public abstract class AnnotationUtils { @@ -1974,20 +1964,8 @@ public abstract class AnnotationUtils {
throw new AnnotationConfigurationException(msg);
}
Method aliasedAttribute;
try {
aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
}
catch (NoSuchMethodException ex) {
String msg = String.format(
"Attribute [%s] in annotation [%s] is declared as an @AliasFor nonexistent attribute [%s] in annotation [%s].",
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
this.aliasedAnnotationType.getName());
throw new AnnotationConfigurationException(msg, ex);
}
if (this.isAliasPair) {
AliasFor mirrorAliasFor = aliasedAttribute.getAnnotation(AliasFor.class);
AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class);
if (mirrorAliasFor == null) {
String msg = String.format(
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].",
@ -1995,8 +1973,7 @@ public abstract class AnnotationUtils { @@ -1995,8 +1973,7 @@ public abstract class AnnotationUtils {
throw new AnnotationConfigurationException(msg);
}
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor,
aliasedAttribute);
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute);
if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
String msg = String.format(
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
@ -2007,7 +1984,7 @@ public abstract class AnnotationUtils { @@ -2007,7 +1984,7 @@ public abstract class AnnotationUtils {
}
Class<?> returnType = this.sourceAttribute.getReturnType();
Class<?> aliasedReturnType = aliasedAttribute.getReturnType();
Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType();
if (!returnType.equals(aliasedReturnType)) {
String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
+ "and attribute [%s] in annotation [%s] must declare the same return type.",
@ -2017,7 +1994,7 @@ public abstract class AnnotationUtils { @@ -2017,7 +1994,7 @@ public abstract class AnnotationUtils {
}
if (this.isAliasPair) {
validateDefaultValueConfiguration(aliasedAttribute);
validateDefaultValueConfiguration(this.aliasedAttribute);
}
}
@ -2047,63 +2024,101 @@ public abstract class AnnotationUtils { @@ -2047,63 +2024,101 @@ public abstract class AnnotationUtils {
* Validate this descriptor against the supplied descriptor.
* <p>This method only validates the configuration of default values
* for the two descriptors, since other aspects of the descriptors
* were validated when the descriptors were created.
* are validated when they are created.
*/
public void validateAgainst(AliasDescriptor otherDescriptor) {
private void validateAgainst(AliasDescriptor otherDescriptor) {
validateDefaultValueConfiguration(otherDescriptor.sourceAttribute);
}
/**
* Does this descriptor represent an alias for an attribute in the
* supplied {@code targetAnnotationType}?
* Determine if this descriptor represents an explicit override for
* an attribute in the supplied {@code metaAnnotationType}.
* @see #isAliasFor
*/
public boolean isAliasFor(Class<? extends Annotation> targetAnnotationType) {
return targetAnnotationType.equals(this.aliasedAnnotationType);
private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) {
return this.aliasedAnnotationType.equals(metaAnnotationType);
}
/**
* Determine if this descriptor is logically equal to the supplied
* object.
* <p>Two descriptors are considered equal if the aliases they
* represent are from attributes in one annotation that alias the
* same attribute in a given target annotation.
* Determine if this descriptor and the supplied descriptor both
* effectively represent aliases for the same attribute in the same
* target annotation, either explicitly or implicitly.
* <p>This method searches the attribute override hierarchy, beginning
* with this descriptor, in order to detect implicit and transitively
* implicit aliases.
* @return {@code true} if this descriptor and the supplied descriptor
* effectively alias the same annotation attribute
* @see #isOverrideFor
*/
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AliasDescriptor)) {
return false;
private boolean isAliasFor(AliasDescriptor otherDescriptor) {
for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) {
for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) {
if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) {
return true;
}
}
}
return false;
}
AliasDescriptor that = (AliasDescriptor) other;
public List<String> getAttributeAliasNames() {
// Explicit alias pair?
if (this.isAliasPair) {
return Collections.singletonList(this.aliasedAttributeName);
}
if (!this.sourceAnnotationType.equals(that.sourceAnnotationType)) {
return false;
// Else: search for implicit aliases
List<String> aliases = new ArrayList<String>();
for (AliasDescriptor otherDescriptor : getOtherDescriptors()) {
if (this.isAliasFor(otherDescriptor)) {
this.validateAgainst(otherDescriptor);
aliases.add(otherDescriptor.sourceAttributeName);
}
}
if (!this.aliasedAnnotationType.equals(that.aliasedAnnotationType)) {
return false;
return aliases;
}
private List<AliasDescriptor> getOtherDescriptors() {
List<AliasDescriptor> otherDescriptors = new ArrayList<AliasDescriptor>();
for (Method currentAttribute : getAttributeMethods(this.sourceAnnotationType)) {
if (!this.sourceAttribute.equals(currentAttribute)) {
AliasDescriptor otherDescriptor = AliasDescriptor.from(currentAttribute);
if (otherDescriptor != null) {
otherDescriptors.add(otherDescriptor);
}
}
}
if (!this.aliasedAttributeName.equals(that.aliasedAttributeName)) {
return false;
return otherDescriptors;
}
public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) {
Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
"metaAnnotationType must not be java.lang.annotation.Annotation");
// Search the attribute override hierarchy, starting with the current attribute
for (AliasDescriptor desc = this; desc != null; desc = desc.getAttributeOverrideDescriptor()) {
if (desc.isOverrideFor(metaAnnotationType)) {
return desc.aliasedAttributeName;
}
}
return true;
// Else: explicit attribute override for a different meta-annotation
return null;
}
@Override
public int hashCode() {
int result = this.sourceAnnotationType.hashCode();
result = 31 * result + this.aliasedAnnotationType.hashCode();
result = 31 * result + this.aliasedAttributeName.hashCode();
return result;
private AliasDescriptor getAttributeOverrideDescriptor() {
if (this.isAliasPair) {
return null;
}
return AliasDescriptor.from(this.aliasedAttribute);
}
@Override
public String toString() {
return String.format("%s: '%s' in @%s is an alias for '%s' in @%s", getClass().getSimpleName(),
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
(this.aliasedAnnotationType != null ? this.aliasedAnnotationType.getName() : null));
return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(),
this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName,
this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName);
}
/**
@ -2121,7 +2136,6 @@ public abstract class AnnotationUtils { @@ -2121,7 +2136,6 @@ public abstract class AnnotationUtils {
* @throws AnnotationConfigurationException if invalid configuration of
* {@code @AliasFor} is detected
* @since 4.2
* @see AnnotationUtils#getAliasedAttributeNames(Method, Class)
*/
private static String getAliasedAttributeName(AliasFor aliasFor, Method attribute) {
String attributeName = aliasFor.attribute();

46
spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java

@ -351,13 +351,23 @@ public class AnnotatedElementUtilsTests { @@ -351,13 +351,23 @@ public class AnnotatedElementUtilsTests {
assertGetMergedAnnotation(ImplicitAliasesContextConfigClass3.class, "baz.xml");
}
private void assertGetMergedAnnotation(Class<?> element, String expected) {
@Test
public void getMergedAnnotationWithTransitiveImplicitAliases() {
assertGetMergedAnnotation(TransitiveImplicitAliasesContextConfigClass.class, "test.groovy");
}
@Test
public void getMergedAnnotationWithTransitiveImplicitAliasesWithSkippedLevel() {
assertGetMergedAnnotation(TransitiveImplicitAliasesWithSkippedLevelContextConfigClass.class, "test.xml");
}
private void assertGetMergedAnnotation(Class<?> element, String... expected) {
String name = ContextConfig.class.getName();
ContextConfig contextConfig = getMergedAnnotation(element, ContextConfig.class);
assertNotNull("Should find @ContextConfig on " + element.getSimpleName(), contextConfig);
assertArrayEquals("locations", new String[] { expected }, contextConfig.locations());
assertArrayEquals("value", new String[] { expected }, contextConfig.value());
assertArrayEquals("locations", expected, contextConfig.locations());
assertArrayEquals("value", expected, contextConfig.value());
assertArrayEquals("classes", new Class<?>[0], contextConfig.classes());
// Verify contracts between utility methods:
@ -800,6 +810,28 @@ public class AnnotatedElementUtilsTests { @@ -800,6 +810,28 @@ public class AnnotatedElementUtilsTests {
@interface ComposedImplicitAliasesContextConfig {
}
@ImplicitAliasesContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface TransitiveImplicitAliasesContextConfig {
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "xmlFiles")
String[] xml() default {};
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "groovyScripts")
String[] groovy() default {};
}
@ImplicitAliasesContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface TransitiveImplicitAliasesWithSkippedLevelContextConfig {
@AliasFor(annotation = ContextConfig.class, attribute = "locations")
String[] xml() default {};
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "groovyScripts")
String[] groovy() default {};
}
/**
* Invalid because the configuration declares a value for 'value' and
* requires a value for the aliased 'locations'. So we likely end up with
@ -1028,6 +1060,14 @@ public class AnnotatedElementUtilsTests { @@ -1028,6 +1060,14 @@ public class AnnotatedElementUtilsTests {
static class ImplicitAliasesContextConfigClass3 {
}
@TransitiveImplicitAliasesContextConfig(groovy = "test.groovy")
static class TransitiveImplicitAliasesContextConfigClass {
}
@TransitiveImplicitAliasesWithSkippedLevelContextConfig(xml = "test.xml")
static class TransitiveImplicitAliasesWithSkippedLevelContextConfigClass {
}
@ComposedImplicitAliasesContextConfig
static class ComposedImplicitAliasesContextConfigClass {
}

167
spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

@ -59,7 +59,7 @@ public class AnnotationUtilsTests { @@ -59,7 +59,7 @@ public class AnnotationUtilsTests {
static void clearCaches() {
clearCache("findAnnotationCache", "annotatedInterfaceCache", "metaPresentCache", "synthesizableCache",
"attributeAliasesCache", "attributeMethodsCache");
"attributeAliasesCache", "attributeMethodsCache", "aliasDescriptorCache");
}
static void clearCache(String... cacheNames) {
@ -720,26 +720,26 @@ public class AnnotationUtilsTests { @@ -720,26 +720,26 @@ public class AnnotationUtilsTests {
}
@Test
public void getAliasedAttributeNamesFromWrongTargetAnnotation() throws Exception {
public void getAttributeOverrideNameFromWrongTargetAnnotation() throws Exception {
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
assertThat("xmlConfigFile is not an alias for @Component.",
getAliasedAttributeNames(attribute, Component.class), is(empty()));
getAttributeOverrideName(attribute, Component.class), is(nullValue()));
}
@Test
public void getAliasedAttributeNamesForNonAliasedAttribute() throws Exception {
public void getAttributeOverrideNameForNonAliasedAttribute() throws Exception {
Method nonAliasedAttribute = ImplicitAliasesContextConfig.class.getDeclaredMethod("nonAliasedAttribute");
assertThat(getAliasedAttributeNames(nonAliasedAttribute, ContextConfig.class), is(empty()));
assertThat(getAttributeOverrideName(nonAliasedAttribute, ContextConfig.class), is(nullValue()));
}
@Test
public void getAliasedAttributeNamesFromAliasedComposedAnnotation() throws Exception {
public void getAttributeOverrideNameFromAliasedComposedAnnotation() throws Exception {
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
assertEquals(asList("location"), getAliasedAttributeNames(attribute, ContextConfig.class));
assertEquals("location", getAttributeOverrideName(attribute, ContextConfig.class));
}
@Test
public void getAliasedAttributeNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
Method xmlFile = ImplicitAliasesContextConfig.class.getDeclaredMethod("xmlFile");
Method groovyScript = ImplicitAliasesContextConfig.class.getDeclaredMethod("groovyScript");
Method value = ImplicitAliasesContextConfig.class.getDeclaredMethod("value");
@ -748,17 +748,63 @@ public class AnnotationUtilsTests { @@ -748,17 +748,63 @@ public class AnnotationUtilsTests {
Method location3 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location3");
// Meta-annotation attribute overrides
assertEquals(asList("location"), getAliasedAttributeNames(xmlFile, ContextConfig.class));
assertEquals(asList("location"), getAliasedAttributeNames(groovyScript, ContextConfig.class));
assertEquals(asList("location"), getAliasedAttributeNames(value, ContextConfig.class));
assertEquals("location", getAttributeOverrideName(xmlFile, ContextConfig.class));
assertEquals("location", getAttributeOverrideName(groovyScript, ContextConfig.class));
assertEquals("location", getAttributeOverrideName(value, ContextConfig.class));
// Implicit Aliases
assertThat(getAliasedAttributeNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
assertThat(getAliasedAttributeNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
assertThat(getAliasedAttributeNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
assertThat(getAliasedAttributeNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
assertThat(getAliasedAttributeNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
assertThat(getAliasedAttributeNames(location3), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location2"));
// Implicit aliases
assertThat(getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
assertThat(getAttributeAliasNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
assertThat(getAttributeAliasNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
assertThat(getAttributeAliasNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
assertThat(getAttributeAliasNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
assertThat(getAttributeAliasNames(location3), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location2"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliasesForAliasPair() throws Exception {
Method xmlFile = ImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("xmlFile");
Method groovyScript = ImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("groovyScript");
// Meta-annotation attribute overrides
assertEquals("location", getAttributeOverrideName(xmlFile, ContextConfig.class));
assertEquals("value", getAttributeOverrideName(groovyScript, ContextConfig.class));
// Implicit aliases
assertThat(getAttributeAliasNames(xmlFile), containsInAnyOrder("groovyScript"));
assertThat(getAttributeAliasNames(groovyScript), containsInAnyOrder("xmlFile"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliases() throws Exception {
Method xml = TransitiveImplicitAliasesContextConfig.class.getDeclaredMethod("xml");
Method groovy = TransitiveImplicitAliasesContextConfig.class.getDeclaredMethod("groovy");
// Explicit meta-annotation attribute overrides
assertEquals("xmlFile", getAttributeOverrideName(xml, ImplicitAliasesContextConfig.class));
assertEquals("groovyScript", getAttributeOverrideName(groovy, ImplicitAliasesContextConfig.class));
// Transitive meta-annotation attribute overrides
assertEquals("location", getAttributeOverrideName(xml, ContextConfig.class));
assertEquals("location", getAttributeOverrideName(groovy, ContextConfig.class));
// Transitive implicit aliases
assertThat(getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
assertThat(getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
}
@Test
public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliasesForAliasPair() throws Exception {
Method xml = TransitiveImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("xml");
Method groovy = TransitiveImplicitAliasesForAliasPairContextConfig.class.getDeclaredMethod("groovy");
// Explicit meta-annotation attribute overrides
assertEquals("xmlFile", getAttributeOverrideName(xml, ImplicitAliasesForAliasPairContextConfig.class));
assertEquals("groovyScript", getAttributeOverrideName(groovy, ImplicitAliasesForAliasPairContextConfig.class));
// Transitive implicit aliases
assertThat(getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
assertThat(getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
}
@Test
@ -936,7 +982,6 @@ public class AnnotationUtilsTests { @@ -936,7 +982,6 @@ public class AnnotationUtilsTests {
ImplicitAliasesContextConfig synthesizedConfig = synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertNotSame(config, synthesizedConfig);
assertEquals("value: ", expected, synthesizedConfig.value());
assertEquals("location1: ", expected, synthesizedConfig.location1());
@ -944,6 +989,45 @@ public class AnnotationUtilsTests { @@ -944,6 +989,45 @@ public class AnnotationUtilsTests {
assertEquals("groovyScript: ", expected, synthesizedConfig.groovyScript());
}
@Test
public void synthesizeAnnotationWithImplicitAliasesForAliasPair() throws Exception {
Class<?> clazz = ImplicitAliasesForAliasPairContextConfigClass.class;
ImplicitAliasesForAliasPairContextConfig config = clazz.getAnnotation(ImplicitAliasesForAliasPairContextConfig.class);
assertNotNull(config);
ImplicitAliasesForAliasPairContextConfig synthesizedConfig = synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xmlFile: ", "test.xml", synthesizedConfig.xmlFile());
assertEquals("groovyScript: ", "test.xml", synthesizedConfig.groovyScript());
}
@Test
public void synthesizeAnnotationWithTransitiveImplicitAliases() throws Exception {
Class<?> clazz = TransitiveImplicitAliasesContextConfigClass.class;
TransitiveImplicitAliasesContextConfig config = clazz.getAnnotation(TransitiveImplicitAliasesContextConfig.class);
assertNotNull(config);
TransitiveImplicitAliasesContextConfig synthesizedConfig = synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xml: ", "test.xml", synthesizedConfig.xml());
assertEquals("groovy: ", "test.xml", synthesizedConfig.groovy());
}
@Test
public void synthesizeAnnotationWithTransitiveImplicitAliasesForAliasPair() throws Exception {
Class<?> clazz = TransitiveImplicitAliasesForAliasPairContextConfigClass.class;
TransitiveImplicitAliasesForAliasPairContextConfig config = clazz.getAnnotation(TransitiveImplicitAliasesForAliasPairContextConfig.class);
assertNotNull(config);
TransitiveImplicitAliasesForAliasPairContextConfig synthesizedConfig = synthesizeAnnotation(config);
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
assertEquals("xml: ", "test.xml", synthesizedConfig.xml());
assertEquals("groovy: ", "test.xml", synthesizedConfig.groovy());
}
@Test
public void synthesizeAnnotationWithImplicitAliasesWithMissingDefaultValues() throws Exception {
Class<?> clazz = ImplicitAliasesWithMissingDefaultValuesContextConfigClass.class;
@ -2007,6 +2091,51 @@ public class AnnotationUtilsTests { @@ -2007,6 +2091,51 @@ public class AnnotationUtilsTests {
static class ImplicitAliasesWithDuplicateValuesContextConfigClass {
}
@ContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface ImplicitAliasesForAliasPairContextConfig {
@AliasFor(annotation = ContextConfig.class, attribute = "location")
String xmlFile() default "";
@AliasFor(annotation = ContextConfig.class, value = "value")
String groovyScript() default "";
}
@ImplicitAliasesForAliasPairContextConfig(xmlFile = "test.xml")
static class ImplicitAliasesForAliasPairContextConfigClass {
}
@ImplicitAliasesContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface TransitiveImplicitAliasesContextConfig {
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "xmlFile")
String xml() default "";
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "groovyScript")
String groovy() default "";
}
@TransitiveImplicitAliasesContextConfig(xml = "test.xml")
static class TransitiveImplicitAliasesContextConfigClass {
}
@ImplicitAliasesForAliasPairContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface TransitiveImplicitAliasesForAliasPairContextConfig {
@AliasFor(annotation = ImplicitAliasesForAliasPairContextConfig.class, attribute = "xmlFile")
String xml() default "";
@AliasFor(annotation = ImplicitAliasesForAliasPairContextConfig.class, attribute = "groovyScript")
String groovy() default "";
}
@TransitiveImplicitAliasesForAliasPairContextConfig(xml = "test.xml")
static class TransitiveImplicitAliasesForAliasPairContextConfigClass {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {

Loading…
Cancel
Save