Support transitive implicit attribute aliases with @AliasFor
This commit picks up where 3eacb837c2
(SPR-13345) left off by adding support for transitive implicit aliases
configured via @AliasFor.
Issue: SPR-13405
This commit is contained in:
@@ -956,21 +956,14 @@ public class AnnotatedElementUtils {
|
|||||||
|
|
||||||
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
|
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
|
||||||
String attributeName = attributeMethod.getName();
|
String attributeName = attributeMethod.getName();
|
||||||
List<String> aliases = AnnotationUtils.getAliasedAttributeNames(attributeMethod, targetAnnotationType);
|
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
|
||||||
|
|
||||||
// Explicit annotation attribute override declared via @AliasFor
|
// Explicit annotation attribute override declared via @AliasFor
|
||||||
if (!aliases.isEmpty()) {
|
if (attributeOverrideName != null) {
|
||||||
if (aliases.size() != 1) {
|
if (attributes.containsKey(attributeOverrideName)) {
|
||||||
throw new IllegalStateException(String.format(
|
overrideAttribute(element, annotation, attributes, attributeName, attributeOverrideName);
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implicit annotation attribute override based on convention
|
// Implicit annotation attribute override based on convention
|
||||||
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
|
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
|
||||||
overrideAttribute(element, annotation, attributes, attributeName, attributeName);
|
overrideAttribute(element, annotation, attributes, attributeName, attributeName);
|
||||||
|
|||||||
@@ -137,6 +137,9 @@ public abstract class AnnotationUtils {
|
|||||||
private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache =
|
private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache =
|
||||||
new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);
|
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;
|
private static transient Log logger;
|
||||||
|
|
||||||
|
|
||||||
@@ -1436,7 +1439,7 @@ public abstract class AnnotationUtils {
|
|||||||
|
|
||||||
map = new HashMap<String, List<String>>();
|
map = new HashMap<String, List<String>>();
|
||||||
for (Method attribute : getAttributeMethods(annotationType)) {
|
for (Method attribute : getAttributeMethods(annotationType)) {
|
||||||
List<String> aliasNames = getAliasedAttributeNames(attribute);
|
List<String> aliasNames = getAttributeAliasNames(attribute);
|
||||||
if (!aliasNames.isEmpty()) {
|
if (!aliasNames.isEmpty()) {
|
||||||
map.put(attribute.getName(), aliasNames);
|
map.put(attribute.getName(), aliasNames);
|
||||||
}
|
}
|
||||||
@@ -1469,7 +1472,7 @@ public abstract class AnnotationUtils {
|
|||||||
|
|
||||||
synthesizable = Boolean.FALSE;
|
synthesizable = Boolean.FALSE;
|
||||||
for (Method attribute : getAttributeMethods(annotationType)) {
|
for (Method attribute : getAttributeMethods(annotationType)) {
|
||||||
if (!getAliasedAttributeNames(attribute).isEmpty()) {
|
if (!getAttributeAliasNames(attribute).isEmpty()) {
|
||||||
synthesizable = Boolean.TRUE;
|
synthesizable = Boolean.TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1497,7 +1500,6 @@ public abstract class AnnotationUtils {
|
|||||||
/**
|
/**
|
||||||
* Get the names of the aliased attributes configured via
|
* Get the names of the aliased attributes configured via
|
||||||
* {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
|
* {@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}
|
* @param attribute the attribute to find aliases for; never {@code null}
|
||||||
* @return the names of the aliased attributes; never {@code null}, though
|
* @return the names of the aliased attributes; never {@code null}, though
|
||||||
* potentially <em>empty</em>
|
* potentially <em>empty</em>
|
||||||
@@ -1506,74 +1508,39 @@ public abstract class AnnotationUtils {
|
|||||||
* @throws AnnotationConfigurationException if invalid configuration of
|
* @throws AnnotationConfigurationException if invalid configuration of
|
||||||
* {@code @AliasFor} is detected
|
* {@code @AliasFor} is detected
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
* @see #getAliasedAttributeNames(Method, Class)
|
* @see #getAttributeOverrideName(Method, Class)
|
||||||
*/
|
*/
|
||||||
static List<String> getAliasedAttributeNames(Method attribute) {
|
static List<String> getAttributeAliasNames(Method attribute) {
|
||||||
return getAliasedAttributeNames(attribute, (Class<? extends Annotation>) null);
|
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}.
|
* {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}.
|
||||||
* <p>If the supplied {@code metaAnnotationType} is non-null, the
|
* @param attribute the attribute from which to retrieve the override;
|
||||||
* returned list will contain at most one element.
|
* never {@code null}
|
||||||
* @param attribute the attribute to find aliases for; never {@code null}
|
* @param metaAnnotationType the type of meta-annotation in which the
|
||||||
* @param metaAnnotationType the type of meta-annotation in which an
|
* overridden attribute is allowed to be declared
|
||||||
* aliased attribute is allowed to be declared; {@code null} implies
|
* @return the name of the overridden attribute, or {@code null} if not
|
||||||
* <em>within the same annotation</em> as the supplied attribute
|
* found or not applicable for the specified meta-annotation type
|
||||||
* @return the names of the aliased attributes; never {@code null}, though
|
|
||||||
* potentially <em>empty</em>
|
|
||||||
* @throws IllegalArgumentException if the supplied attribute method is
|
* @throws IllegalArgumentException if the supplied attribute method is
|
||||||
* {@code null} or not from an annotation, or if the supplied meta-annotation
|
* {@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
|
* @throws AnnotationConfigurationException if invalid configuration of
|
||||||
* {@code @AliasFor} is detected
|
* {@code @AliasFor} is detected
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
static List<String> getAliasedAttributeNames(Method attribute, Class<? extends Annotation> metaAnnotationType) {
|
static String getAttributeOverrideName(Method attribute, Class<? extends Annotation> metaAnnotationType) {
|
||||||
Assert.notNull(attribute, "attribute method must not be null");
|
Assert.notNull(attribute, "attribute must not be null");
|
||||||
|
Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
|
||||||
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
|
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
|
||||||
"metaAnnotationType must not be java.lang.annotation.Annotation");
|
"metaAnnotationType must not be java.lang.annotation.Annotation");
|
||||||
|
|
||||||
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
|
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
|
||||||
|
return (descriptor == null ? null : descriptor.getAttributeOverrideName(metaAnnotationType));
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1912,6 +1879,9 @@ public abstract class AnnotationUtils {
|
|||||||
* on a given annotation attribute and includes support for validating
|
* on a given annotation attribute and includes support for validating
|
||||||
* the configuration of aliases (both explicit and implicit).
|
* the configuration of aliases (both explicit and implicit).
|
||||||
* @since 4.2.1
|
* @since 4.2.1
|
||||||
|
* @see #from
|
||||||
|
* @see #getAttributeAliasNames
|
||||||
|
* @see #getAttributeOverrideName
|
||||||
*/
|
*/
|
||||||
private static class AliasDescriptor {
|
private static class AliasDescriptor {
|
||||||
|
|
||||||
@@ -1921,6 +1891,8 @@ public abstract class AnnotationUtils {
|
|||||||
|
|
||||||
private final String sourceAttributeName;
|
private final String sourceAttributeName;
|
||||||
|
|
||||||
|
private final Method aliasedAttribute;
|
||||||
|
|
||||||
private final Class<? extends Annotation> aliasedAnnotationType;
|
private final Class<? extends Annotation> aliasedAnnotationType;
|
||||||
|
|
||||||
private final String aliasedAttributeName;
|
private final String aliasedAttributeName;
|
||||||
@@ -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
|
* of {@code @AliasFor} on the supplied annotation attribute and
|
||||||
* validate the configuration of {@code @AliasFor}.
|
* validate the configuration of {@code @AliasFor}.
|
||||||
* @param attribute the annotation attribute that is annotated with
|
* @param attribute the annotation attribute that is annotated with
|
||||||
* {@code @AliasFor}
|
* {@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}
|
* is not annotated with {@code @AliasFor}
|
||||||
* @see #validateAgainst(AliasDescriptor)
|
* @see #validateAgainst
|
||||||
*/
|
*/
|
||||||
public static AliasDescriptor from(Method attribute) {
|
public static AliasDescriptor from(Method attribute) {
|
||||||
|
AliasDescriptor descriptor = aliasDescriptorCache.get(attribute);
|
||||||
|
if (descriptor != null) {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
|
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
|
||||||
if (aliasFor == null) {
|
if (aliasFor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AliasDescriptor descriptor = new AliasDescriptor(attribute, aliasFor);
|
descriptor = new AliasDescriptor(attribute, aliasFor);
|
||||||
descriptor.validate();
|
descriptor.validate();
|
||||||
|
aliasDescriptorCache.put(attribute, descriptor);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
|
private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
|
||||||
Class<?> declaringClass = sourceAttribute.getDeclaringClass();
|
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.sourceAttribute = sourceAttribute;
|
||||||
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
|
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
|
||||||
this.sourceAttributeName = this.sourceAttribute.getName();
|
this.sourceAttributeName = this.sourceAttribute.getName();
|
||||||
|
|
||||||
this.aliasedAnnotationType = (Annotation.class.equals(aliasFor.annotation()) ? this.sourceAnnotationType
|
this.aliasedAnnotationType = (Annotation.class.equals(aliasFor.annotation()) ? this.sourceAnnotationType
|
||||||
: aliasFor.annotation());
|
: aliasFor.annotation());
|
||||||
this.aliasedAttributeName = getAliasedAttributeName(aliasFor, this.sourceAttribute);
|
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);
|
this.isAliasPair = this.sourceAnnotationType.equals(this.aliasedAnnotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1974,20 +1964,8 @@ public abstract class AnnotationUtils {
|
|||||||
throw new AnnotationConfigurationException(msg);
|
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) {
|
if (this.isAliasPair) {
|
||||||
AliasFor mirrorAliasFor = aliasedAttribute.getAnnotation(AliasFor.class);
|
AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class);
|
||||||
if (mirrorAliasFor == null) {
|
if (mirrorAliasFor == null) {
|
||||||
String msg = String.format(
|
String msg = String.format(
|
||||||
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].",
|
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].",
|
||||||
@@ -1995,8 +1973,7 @@ public abstract class AnnotationUtils {
|
|||||||
throw new AnnotationConfigurationException(msg);
|
throw new AnnotationConfigurationException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor,
|
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute);
|
||||||
aliasedAttribute);
|
|
||||||
if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
|
if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
|
||||||
String msg = String.format(
|
String msg = String.format(
|
||||||
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
|
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
|
||||||
@@ -2007,7 +1984,7 @@ public abstract class AnnotationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Class<?> returnType = this.sourceAttribute.getReturnType();
|
Class<?> returnType = this.sourceAttribute.getReturnType();
|
||||||
Class<?> aliasedReturnType = aliasedAttribute.getReturnType();
|
Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType();
|
||||||
if (!returnType.equals(aliasedReturnType)) {
|
if (!returnType.equals(aliasedReturnType)) {
|
||||||
String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
|
String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
|
||||||
+ "and attribute [%s] in annotation [%s] must declare the same return type.",
|
+ "and attribute [%s] in annotation [%s] must declare the same return type.",
|
||||||
@@ -2017,7 +1994,7 @@ public abstract class AnnotationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isAliasPair) {
|
if (this.isAliasPair) {
|
||||||
validateDefaultValueConfiguration(aliasedAttribute);
|
validateDefaultValueConfiguration(this.aliasedAttribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2047,63 +2024,101 @@ public abstract class AnnotationUtils {
|
|||||||
* Validate this descriptor against the supplied descriptor.
|
* Validate this descriptor against the supplied descriptor.
|
||||||
* <p>This method only validates the configuration of default values
|
* <p>This method only validates the configuration of default values
|
||||||
* for the two descriptors, since other aspects of the descriptors
|
* 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);
|
validateDefaultValueConfiguration(otherDescriptor.sourceAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this descriptor represent an alias for an attribute in the
|
* Determine if this descriptor represents an explicit override for
|
||||||
* supplied {@code targetAnnotationType}?
|
* an attribute in the supplied {@code metaAnnotationType}.
|
||||||
|
* @see #isAliasFor
|
||||||
*/
|
*/
|
||||||
public boolean isAliasFor(Class<? extends Annotation> targetAnnotationType) {
|
private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) {
|
||||||
return targetAnnotationType.equals(this.aliasedAnnotationType);
|
return this.aliasedAnnotationType.equals(metaAnnotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this descriptor is logically equal to the supplied
|
* Determine if this descriptor and the supplied descriptor both
|
||||||
* object.
|
* effectively represent aliases for the same attribute in the same
|
||||||
* <p>Two descriptors are considered equal if the aliases they
|
* target annotation, either explicitly or implicitly.
|
||||||
* represent are from attributes in one annotation that alias the
|
* <p>This method searches the attribute override hierarchy, beginning
|
||||||
* same attribute in a given target annotation.
|
* 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) {
|
private boolean isAliasFor(AliasDescriptor otherDescriptor) {
|
||||||
if (this == other) {
|
for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) {
|
||||||
return true;
|
for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) {
|
||||||
|
if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!(other instanceof AliasDescriptor)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasDescriptor that = (AliasDescriptor) other;
|
|
||||||
|
|
||||||
if (!this.sourceAnnotationType.equals(that.sourceAnnotationType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!this.aliasedAnnotationType.equals(that.aliasedAnnotationType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!this.aliasedAttributeName.equals(that.aliasedAttributeName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public List<String> getAttributeAliasNames() {
|
||||||
public int hashCode() {
|
// Explicit alias pair?
|
||||||
int result = this.sourceAnnotationType.hashCode();
|
if (this.isAliasPair) {
|
||||||
result = 31 * result + this.aliasedAnnotationType.hashCode();
|
return Collections.singletonList(this.aliasedAttributeName);
|
||||||
result = 31 * result + this.aliasedAttributeName.hashCode();
|
}
|
||||||
return result;
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else: explicit attribute override for a different meta-annotation
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AliasDescriptor getAttributeOverrideDescriptor() {
|
||||||
|
if (this.isAliasPair) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return AliasDescriptor.from(this.aliasedAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s: '%s' in @%s is an alias for '%s' in @%s", getClass().getSimpleName(),
|
return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(),
|
||||||
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
|
this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName,
|
||||||
(this.aliasedAnnotationType != null ? this.aliasedAnnotationType.getName() : null));
|
this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2121,7 +2136,6 @@ public abstract class AnnotationUtils {
|
|||||||
* @throws AnnotationConfigurationException if invalid configuration of
|
* @throws AnnotationConfigurationException if invalid configuration of
|
||||||
* {@code @AliasFor} is detected
|
* {@code @AliasFor} is detected
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
* @see AnnotationUtils#getAliasedAttributeNames(Method, Class)
|
|
||||||
*/
|
*/
|
||||||
private static String getAliasedAttributeName(AliasFor aliasFor, Method attribute) {
|
private static String getAliasedAttributeName(AliasFor aliasFor, Method attribute) {
|
||||||
String attributeName = aliasFor.attribute();
|
String attributeName = aliasFor.attribute();
|
||||||
|
|||||||
@@ -351,13 +351,23 @@ public class AnnotatedElementUtilsTests {
|
|||||||
assertGetMergedAnnotation(ImplicitAliasesContextConfigClass3.class, "baz.xml");
|
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();
|
String name = ContextConfig.class.getName();
|
||||||
ContextConfig contextConfig = getMergedAnnotation(element, ContextConfig.class);
|
ContextConfig contextConfig = getMergedAnnotation(element, ContextConfig.class);
|
||||||
|
|
||||||
assertNotNull("Should find @ContextConfig on " + element.getSimpleName(), contextConfig);
|
assertNotNull("Should find @ContextConfig on " + element.getSimpleName(), contextConfig);
|
||||||
assertArrayEquals("locations", new String[] { expected }, contextConfig.locations());
|
assertArrayEquals("locations", expected, contextConfig.locations());
|
||||||
assertArrayEquals("value", new String[] { expected }, contextConfig.value());
|
assertArrayEquals("value", expected, contextConfig.value());
|
||||||
assertArrayEquals("classes", new Class<?>[0], contextConfig.classes());
|
assertArrayEquals("classes", new Class<?>[0], contextConfig.classes());
|
||||||
|
|
||||||
// Verify contracts between utility methods:
|
// Verify contracts between utility methods:
|
||||||
@@ -800,6 +810,28 @@ public class AnnotatedElementUtilsTests {
|
|||||||
@interface ComposedImplicitAliasesContextConfig {
|
@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
|
* Invalid because the configuration declares a value for 'value' and
|
||||||
* requires a value for the aliased 'locations'. So we likely end up with
|
* requires a value for the aliased 'locations'. So we likely end up with
|
||||||
@@ -1028,6 +1060,14 @@ public class AnnotatedElementUtilsTests {
|
|||||||
static class ImplicitAliasesContextConfigClass3 {
|
static class ImplicitAliasesContextConfigClass3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TransitiveImplicitAliasesContextConfig(groovy = "test.groovy")
|
||||||
|
static class TransitiveImplicitAliasesContextConfigClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransitiveImplicitAliasesWithSkippedLevelContextConfig(xml = "test.xml")
|
||||||
|
static class TransitiveImplicitAliasesWithSkippedLevelContextConfigClass {
|
||||||
|
}
|
||||||
|
|
||||||
@ComposedImplicitAliasesContextConfig
|
@ComposedImplicitAliasesContextConfig
|
||||||
static class ComposedImplicitAliasesContextConfigClass {
|
static class ComposedImplicitAliasesContextConfigClass {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class AnnotationUtilsTests {
|
|||||||
|
|
||||||
static void clearCaches() {
|
static void clearCaches() {
|
||||||
clearCache("findAnnotationCache", "annotatedInterfaceCache", "metaPresentCache", "synthesizableCache",
|
clearCache("findAnnotationCache", "annotatedInterfaceCache", "metaPresentCache", "synthesizableCache",
|
||||||
"attributeAliasesCache", "attributeMethodsCache");
|
"attributeAliasesCache", "attributeMethodsCache", "aliasDescriptorCache");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearCache(String... cacheNames) {
|
static void clearCache(String... cacheNames) {
|
||||||
@@ -720,26 +720,26 @@ public class AnnotationUtilsTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAliasedAttributeNamesFromWrongTargetAnnotation() throws Exception {
|
public void getAttributeOverrideNameFromWrongTargetAnnotation() throws Exception {
|
||||||
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
||||||
assertThat("xmlConfigFile is not an alias for @Component.",
|
assertThat("xmlConfigFile is not an alias for @Component.",
|
||||||
getAliasedAttributeNames(attribute, Component.class), is(empty()));
|
getAttributeOverrideName(attribute, Component.class), is(nullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAliasedAttributeNamesForNonAliasedAttribute() throws Exception {
|
public void getAttributeOverrideNameForNonAliasedAttribute() throws Exception {
|
||||||
Method nonAliasedAttribute = ImplicitAliasesContextConfig.class.getDeclaredMethod("nonAliasedAttribute");
|
Method nonAliasedAttribute = ImplicitAliasesContextConfig.class.getDeclaredMethod("nonAliasedAttribute");
|
||||||
assertThat(getAliasedAttributeNames(nonAliasedAttribute, ContextConfig.class), is(empty()));
|
assertThat(getAttributeOverrideName(nonAliasedAttribute, ContextConfig.class), is(nullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAliasedAttributeNamesFromAliasedComposedAnnotation() throws Exception {
|
public void getAttributeOverrideNameFromAliasedComposedAnnotation() throws Exception {
|
||||||
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
||||||
assertEquals(asList("location"), getAliasedAttributeNames(attribute, ContextConfig.class));
|
assertEquals("location", getAttributeOverrideName(attribute, ContextConfig.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAliasedAttributeNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
|
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
|
||||||
Method xmlFile = ImplicitAliasesContextConfig.class.getDeclaredMethod("xmlFile");
|
Method xmlFile = ImplicitAliasesContextConfig.class.getDeclaredMethod("xmlFile");
|
||||||
Method groovyScript = ImplicitAliasesContextConfig.class.getDeclaredMethod("groovyScript");
|
Method groovyScript = ImplicitAliasesContextConfig.class.getDeclaredMethod("groovyScript");
|
||||||
Method value = ImplicitAliasesContextConfig.class.getDeclaredMethod("value");
|
Method value = ImplicitAliasesContextConfig.class.getDeclaredMethod("value");
|
||||||
@@ -748,17 +748,63 @@ public class AnnotationUtilsTests {
|
|||||||
Method location3 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location3");
|
Method location3 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location3");
|
||||||
|
|
||||||
// Meta-annotation attribute overrides
|
// Meta-annotation attribute overrides
|
||||||
assertEquals(asList("location"), getAliasedAttributeNames(xmlFile, ContextConfig.class));
|
assertEquals("location", getAttributeOverrideName(xmlFile, ContextConfig.class));
|
||||||
assertEquals(asList("location"), getAliasedAttributeNames(groovyScript, ContextConfig.class));
|
assertEquals("location", getAttributeOverrideName(groovyScript, ContextConfig.class));
|
||||||
assertEquals(asList("location"), getAliasedAttributeNames(value, ContextConfig.class));
|
assertEquals("location", getAttributeOverrideName(value, ContextConfig.class));
|
||||||
|
|
||||||
// Implicit Aliases
|
// Implicit aliases
|
||||||
assertThat(getAliasedAttributeNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
|
assertThat(getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
|
||||||
assertThat(getAliasedAttributeNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
|
assertThat(getAttributeAliasNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
|
||||||
assertThat(getAliasedAttributeNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
|
assertThat(getAttributeAliasNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
|
||||||
assertThat(getAliasedAttributeNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
|
assertThat(getAttributeAliasNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
|
||||||
assertThat(getAliasedAttributeNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
|
assertThat(getAttributeAliasNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
|
||||||
assertThat(getAliasedAttributeNames(location3), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location2"));
|
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
|
@Test
|
||||||
@@ -936,7 +982,6 @@ public class AnnotationUtilsTests {
|
|||||||
|
|
||||||
ImplicitAliasesContextConfig synthesizedConfig = synthesizeAnnotation(config);
|
ImplicitAliasesContextConfig synthesizedConfig = synthesizeAnnotation(config);
|
||||||
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
|
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
|
||||||
assertNotSame(config, synthesizedConfig);
|
|
||||||
|
|
||||||
assertEquals("value: ", expected, synthesizedConfig.value());
|
assertEquals("value: ", expected, synthesizedConfig.value());
|
||||||
assertEquals("location1: ", expected, synthesizedConfig.location1());
|
assertEquals("location1: ", expected, synthesizedConfig.location1());
|
||||||
@@ -944,6 +989,45 @@ public class AnnotationUtilsTests {
|
|||||||
assertEquals("groovyScript: ", expected, synthesizedConfig.groovyScript());
|
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
|
@Test
|
||||||
public void synthesizeAnnotationWithImplicitAliasesWithMissingDefaultValues() throws Exception {
|
public void synthesizeAnnotationWithImplicitAliasesWithMissingDefaultValues() throws Exception {
|
||||||
Class<?> clazz = ImplicitAliasesWithMissingDefaultValuesContextConfigClass.class;
|
Class<?> clazz = ImplicitAliasesWithMissingDefaultValuesContextConfigClass.class;
|
||||||
@@ -2007,6 +2091,51 @@ public class AnnotationUtilsTests {
|
|||||||
static class ImplicitAliasesWithDuplicateValuesContextConfigClass {
|
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)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({})
|
@Target({})
|
||||||
@interface Filter {
|
@interface Filter {
|
||||||
|
|||||||
Reference in New Issue
Block a user