Complete migration of annotation utility methods
Delete `InternalAnnotationUtils` and `InternalAnnotatedElementUtils` and migrate exclusively to the new `MergedAnnotations` API. Closes gh-22562
This commit is contained in:
parent
a14bfe9a21
commit
37255afca4
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link AnnotationAttributeExtractor} implementations
|
||||
* that transparently enforce attribute alias semantics for annotation
|
||||
* attributes that are annotated with {@link AliasFor @AliasFor}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @param <S> the type of source supported by this extractor
|
||||
* @see Annotation
|
||||
* @see AliasFor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, java.lang.reflect.AnnotatedElement)
|
||||
*/
|
||||
abstract class AbstractAliasAwareAnnotationAttributeExtractor<S> implements AnnotationAttributeExtractor<S> {
|
||||
|
||||
private final Class<? extends Annotation> annotationType;
|
||||
|
||||
@Nullable
|
||||
private final Object annotatedElement;
|
||||
|
||||
private final S source;
|
||||
|
||||
private final Map<String, List<String>> attributeAliasMap;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code AbstractAliasAwareAnnotationAttributeExtractor}.
|
||||
* @param annotationType the annotation type to synthesize; never {@code null}
|
||||
* @param annotatedElement the element that is annotated with the annotation
|
||||
* of the supplied type; may be {@code null} if unknown
|
||||
* @param source the underlying source of annotation attributes; never {@code null}
|
||||
*/
|
||||
AbstractAliasAwareAnnotationAttributeExtractor(
|
||||
Class<? extends Annotation> annotationType, @Nullable Object annotatedElement, S source) {
|
||||
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
Assert.notNull(source, "source must not be null");
|
||||
this.annotationType = annotationType;
|
||||
this.annotatedElement = annotatedElement;
|
||||
this.source = source;
|
||||
this.attributeAliasMap = InternalAnnotationUtils.getAttributeAliasMap(annotationType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final Class<? extends Annotation> getAnnotationType() {
|
||||
return this.annotationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final Object getAnnotatedElement() {
|
||||
return this.annotatedElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final S getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final Object getAttributeValue(Method attributeMethod) {
|
||||
String attributeName = attributeMethod.getName();
|
||||
Object attributeValue = getRawAttributeValue(attributeMethod);
|
||||
|
||||
List<String> aliasNames = this.attributeAliasMap.get(attributeName);
|
||||
if (aliasNames != null) {
|
||||
Object defaultValue = AnnotationUtils.getDefaultValue(this.annotationType, attributeName);
|
||||
for (String aliasName : aliasNames) {
|
||||
Object aliasValue = getRawAttributeValue(aliasName);
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) &&
|
||||
!ObjectUtils.nullSafeEquals(attributeValue, defaultValue) &&
|
||||
!ObjectUtils.nullSafeEquals(aliasValue, defaultValue)) {
|
||||
String elementName = (this.annotatedElement != null ? this.annotatedElement.toString() : "unknown element");
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"In annotation [%s] declared on %s and synthesized from [%s], attribute '%s' and its " +
|
||||
"alias '%s' are present with values of [%s] and [%s], but only one is permitted.",
|
||||
this.annotationType.getName(), elementName, this.source, attributeName, aliasName,
|
||||
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue)));
|
||||
}
|
||||
|
||||
// If the user didn't declare the annotation with an explicit value,
|
||||
// use the value of the alias instead.
|
||||
if (ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) {
|
||||
attributeValue = aliasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the raw, unmodified attribute value from the underlying
|
||||
* {@linkplain #getSource source} that corresponds to the supplied
|
||||
* attribute method.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Object getRawAttributeValue(Method attributeMethod);
|
||||
|
||||
/**
|
||||
* Get the raw, unmodified attribute value from the underlying
|
||||
* {@linkplain #getSource source} that corresponds to the supplied
|
||||
* attribute name.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Object getRawAttributeValue(String attributeName);
|
||||
|
||||
}
|
||||
|
|
@ -51,15 +51,7 @@ import java.lang.annotation.Target;
|
|||
* <h3>Usage Requirements</h3>
|
||||
* <p>Like with any annotation in Java, the mere presence of {@code @AliasFor}
|
||||
* on its own will not enforce alias semantics. For alias semantics to be
|
||||
* enforced, annotations must be <em>loaded</em> via the utility methods in
|
||||
* {@link AnnotationUtils}. Behind the scenes, Spring will <em>synthesize</em>
|
||||
* an annotation by wrapping it in a dynamic proxy that transparently enforces
|
||||
* <em>attribute alias</em> semantics for annotation attributes that are
|
||||
* annotated with {@code @AliasFor}. Similarly, {@link AnnotatedElementUtils}
|
||||
* supports explicit meta-annotation attribute overrides when {@code @AliasFor}
|
||||
* is used within an annotation hierarchy. Typically you will not need to
|
||||
* manually synthesize annotations on your own since Spring will do that for
|
||||
* you transparently when looking up annotations on Spring-managed components.
|
||||
* enforced, annotations must be <em>loaded</em> via {@link MergedAnnotations}.
|
||||
*
|
||||
* <h3>Implementation Requirements</h3>
|
||||
* <ul>
|
||||
|
|
@ -176,9 +168,7 @@ import java.lang.annotation.Target;
|
|||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @see AnnotatedElementUtils
|
||||
* @see AnnotationUtils
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, java.lang.reflect.AnnotatedElement)
|
||||
* @see MergedAnnotations
|
||||
* @see SynthesizedAnnotation
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ public abstract class AnnotatedElementUtils {
|
|||
* @param annotations the annotations to expose through the {@code AnnotatedElement}
|
||||
* @since 4.3
|
||||
*/
|
||||
public static AnnotatedElement forAnnotations(final Annotation... annotations) {
|
||||
return InternalAnnotatedElementUtils.forAnnotations(annotations);
|
||||
public static AnnotatedElement forAnnotations(Annotation... annotations) {
|
||||
return new AnnotatedElementForAnnotations(annotations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -120,12 +120,7 @@ public abstract class AnnotatedElementUtils {
|
|||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMetaAnnotationTypes(element, annotationType)
|
||||
).withDescription(() -> element + " " + annotationType
|
||||
).to(() ->
|
||||
getMetaAnnotationTypes(element, element.getAnnotation(annotationType))
|
||||
);
|
||||
return getMetaAnnotationTypes(element, element.getAnnotation(annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -145,17 +140,12 @@ public abstract class AnnotatedElementUtils {
|
|||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
|
||||
String annotationName) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMetaAnnotationTypes(element, annotationName)
|
||||
).withDescription(() -> element + " " + annotationName
|
||||
).to(() -> {
|
||||
for (Annotation annotation : element.getAnnotations()) {
|
||||
if (annotation.annotationType().getName().equals(annotationName)) {
|
||||
return getMetaAnnotationTypes(element, annotation);
|
||||
}
|
||||
for (Annotation annotation : element.getAnnotations()) {
|
||||
if (annotation.annotationType().getName().equals(annotationName)) {
|
||||
return getMetaAnnotationTypes(element, annotation);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
});
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
|
||||
|
|
@ -184,12 +174,8 @@ public abstract class AnnotatedElementUtils {
|
|||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.hasMetaAnnotationTypes(element, annotationType)
|
||||
).to(() ->
|
||||
getAnnotations(element).stream(annotationType)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent)
|
||||
);
|
||||
return getAnnotations(element).stream(annotationType)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -207,12 +193,8 @@ public abstract class AnnotatedElementUtils {
|
|||
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
|
||||
String annotationName) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.hasMetaAnnotationTypes(element, annotationName)
|
||||
).to(() ->
|
||||
getAnnotations(element).stream(annotationName)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent)
|
||||
);
|
||||
return getAnnotations(element).stream(annotationName)
|
||||
.anyMatch(MergedAnnotation::isMetaPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -232,11 +214,7 @@ public abstract class AnnotatedElementUtils {
|
|||
public static boolean isAnnotated(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.isAnnotated(element, annotationType)
|
||||
).to(() ->
|
||||
getAnnotations(element).isPresent(annotationType)
|
||||
);
|
||||
return getAnnotations(element).isPresent(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -252,11 +230,7 @@ public abstract class AnnotatedElementUtils {
|
|||
* @return {@code true} if a matching annotation is present
|
||||
*/
|
||||
public static boolean isAnnotated(AnnotatedElement element, String annotationName) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.isAnnotated(element, annotationName))
|
||||
.to(() ->
|
||||
getAnnotations(element).isPresent(annotationName)
|
||||
);
|
||||
return getAnnotations(element).isPresent(annotationName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -280,14 +254,9 @@ public abstract class AnnotatedElementUtils {
|
|||
public static AnnotationAttributes getMergedAnnotationAttributes(
|
||||
AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMergedAnnotationAttributes(element,
|
||||
annotationType))
|
||||
.toNullable(() -> {
|
||||
MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, false, false);
|
||||
});
|
||||
MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -346,14 +315,9 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, String annotationName, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMergedAnnotationAttributes(element,
|
||||
annotationName, classValuesAsString, nestedAnnotationsAsMap)
|
||||
).toNullable(() -> {
|
||||
MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
});
|
||||
MergedAnnotation<?> mergedAnnotation = getAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -377,13 +341,20 @@ public abstract class AnnotatedElementUtils {
|
|||
@Nullable
|
||||
public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element,
|
||||
Class<A> annotationType) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMergedAnnotation(element, annotationType)
|
||||
).toNullable(() ->
|
||||
getAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null)
|
||||
);
|
||||
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType);
|
||||
if (annotation != null) {
|
||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||
}
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
return getAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -409,12 +380,8 @@ public abstract class AnnotatedElementUtils {
|
|||
public static <A extends Annotation> Set<A> getAllMergedAnnotations(
|
||||
AnnotatedElement element, Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getAllMergedAnnotations(element, annotationType)
|
||||
).to(() ->
|
||||
getAnnotations(element).stream(annotationType)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return getAnnotations(element).stream(annotationType)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -438,14 +405,9 @@ public abstract class AnnotatedElementUtils {
|
|||
public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element,
|
||||
Set<Class<? extends Annotation>> annotationTypes) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getAllMergedAnnotations(element,
|
||||
annotationTypes)
|
||||
).to(() ->
|
||||
getAnnotations(element).stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes))
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return getAnnotations(element).stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes))
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -508,14 +470,9 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getMergedRepeatableAnnotations(element,
|
||||
annotationType, containerType)
|
||||
).to(() ->
|
||||
getRepeatableAnnotations(element, containerType, annotationType)
|
||||
.stream(annotationType)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return getRepeatableAnnotations(element, containerType, annotationType)
|
||||
.stream(annotationType)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -562,16 +519,11 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, String annotationName,
|
||||
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.getAllAnnotationAttributes(element,
|
||||
annotationName, classValuesAsString, nestedAnnotationsAsMap)
|
||||
).toNullable(() ->{
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return getAnnotations(element).stream(annotationName)
|
||||
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, mapValues));
|
||||
});
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return getAnnotations(element).stream(annotationName)
|
||||
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, mapValues));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -591,15 +543,14 @@ public abstract class AnnotatedElementUtils {
|
|||
public static boolean hasAnnotation(AnnotatedElement element,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.hasAnnotation(element, annotationType)
|
||||
).withSkippedEquivalentCheck(() ->
|
||||
InternalAnnotatedElementUtils.hasAnnotation(element, annotationType) &&
|
||||
InternalAnnotatedElementUtils.findMergedAnnotationAttributes(element,
|
||||
annotationType.getName(), false, false) == null
|
||||
).to(() ->
|
||||
findAnnotations(element).isPresent(annotationType)
|
||||
);
|
||||
// Shortcut: directly present on the element, with no processing needed?
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return false;
|
||||
}
|
||||
if (AnnotationsScanner.getDeclaredAnnotation(element, annotationType) != null) {
|
||||
return true;
|
||||
}
|
||||
return findAnnotations(element).isPresent(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -634,14 +585,9 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, Class<? extends Annotation> annotationType,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findMergedAnnotationAttributes(element,
|
||||
annotationType, classValuesAsString, nestedAnnotationsAsMap)
|
||||
).toNullable(() -> {
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
});
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -676,14 +622,9 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, String annotationName, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findMergedAnnotationAttributes(element,
|
||||
annotationName, classValuesAsString, nestedAnnotationsAsMap)
|
||||
).toNullable(() -> {
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
});
|
||||
MergedAnnotation<?> mergedAnnotation = findAnnotations(element)
|
||||
.get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
|
||||
return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -708,13 +649,16 @@ public abstract class AnnotatedElementUtils {
|
|||
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findMergedAnnotation(element, annotationType)
|
||||
).toNullable(() ->
|
||||
findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null)
|
||||
);
|
||||
A annotation = AnnotationsScanner.getDeclaredAnnotation(element, annotationType);
|
||||
if (annotation != null) {
|
||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||
}
|
||||
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
|
||||
return null;
|
||||
}
|
||||
return findAnnotations(element)
|
||||
.get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -739,14 +683,9 @@ public abstract class AnnotatedElementUtils {
|
|||
public static <A extends Annotation> Set<A> findAllMergedAnnotations(
|
||||
AnnotatedElement element, Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findAllMergedAnnotations(element, annotationType)
|
||||
).withSkippedOriginalExceptionCheck().to(() ->
|
||||
findAnnotations(element)
|
||||
.stream(annotationType)
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return findAnnotations(element).stream(annotationType)
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -770,14 +709,10 @@ public abstract class AnnotatedElementUtils {
|
|||
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element,
|
||||
Set<Class<? extends Annotation>> annotationTypes) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findAllMergedAnnotations(element, annotationTypes)
|
||||
).to(()->
|
||||
findAnnotations(element).stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes))
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return findAnnotations(element).stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes))
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -840,15 +775,10 @@ public abstract class AnnotatedElementUtils {
|
|||
AnnotatedElement element, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotatedElementUtils.findMergedRepeatableAnnotations(element,
|
||||
annotationType, containerType)
|
||||
).to(() ->
|
||||
findRepeatableAnnotations(element, containerType, annotationType)
|
||||
.stream(annotationType)
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet())
|
||||
);
|
||||
return findRepeatableAnnotations(element, containerType, annotationType)
|
||||
.stream(annotationType)
|
||||
.sorted(highAggregateIndexesFirst())
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
private static MergedAnnotations getAnnotations(AnnotatedElement element) {
|
||||
|
|
@ -893,7 +823,8 @@ public abstract class AnnotatedElementUtils {
|
|||
}
|
||||
|
||||
private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
|
||||
return Comparator.<MergedAnnotation<A>>comparingInt(MergedAnnotation::getAggregateIndex).reversed();
|
||||
return Comparator.<MergedAnnotation<A>> comparingInt(
|
||||
MergedAnnotation::getAggregateIndex).reversed();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -907,4 +838,40 @@ public abstract class AnnotatedElementUtils {
|
|||
MapValues.of(classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapted {@link AnnotatedElement} that hold specific annotations.
|
||||
*/
|
||||
private static class AnnotatedElementForAnnotations implements AnnotatedElement {
|
||||
|
||||
private final Annotation[] annotations;
|
||||
|
||||
AnnotatedElementForAnnotations(Annotation... annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return (T) annotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getDeclaredAnnotations() {
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package org.springframework.core.annotation;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
|
|
@ -33,12 +33,12 @@ import java.util.function.Function;
|
|||
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
|
||||
import org.springframework.core.annotation.InternalAnnotationUtils.DefaultValueHolder;
|
||||
import org.springframework.core.annotation.MergedAnnotation.MapValues;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -184,13 +184,9 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A getAnnotation(Annotation annotation,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotation(annotation, annotationType)
|
||||
).toNullable(() ->
|
||||
MergedAnnotations.from(annotation)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null)
|
||||
);
|
||||
return MergedAnnotations.from(annotation)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -209,14 +205,10 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A getAnnotation(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotation(annotatedElement, annotationType)
|
||||
).toNullable(() ->
|
||||
MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null)
|
||||
);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -237,14 +229,10 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A getAnnotation(Method method,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotation(method, annotationType)
|
||||
).toNullable(() ->
|
||||
MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null)
|
||||
);
|
||||
return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.PLAIN)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -260,15 +248,11 @@ public abstract class AnnotationUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotations(annotatedElement)
|
||||
).toNullable(() ->
|
||||
MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray())
|
||||
);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -285,15 +269,11 @@ public abstract class AnnotationUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static Annotation[] getAnnotations(Method method) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotations(method)
|
||||
).toNullable(()->
|
||||
MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray())
|
||||
);
|
||||
return MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS,
|
||||
RepeatableContainers.none(), AnnotationFilter.NONE).stream()
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -363,21 +343,16 @@ public abstract class AnnotationUtils {
|
|||
AnnotatedElement annotatedElement, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getRepeatableAnnotations(annotatedElement,
|
||||
annotationType, containerAnnotationType)
|
||||
).to(() -> {
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.SUPER_CLASS,
|
||||
repeatableContainers, annotationFilter)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
});
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.SUPER_CLASS,
|
||||
repeatableContainers, annotationFilter)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -448,21 +423,15 @@ public abstract class AnnotationUtils {
|
|||
AnnotatedElement annotatedElement, Class<A> annotationType,
|
||||
@Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getDeclaredRepeatableAnnotations(annotatedElement,
|
||||
annotationType, containerAnnotationType)
|
||||
).to(() -> {
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(
|
||||
annotationType, containerAnnotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
repeatableContainers, annotationFilter).stream(annotationType)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
);
|
||||
RepeatableContainers repeatableContainers = containerAnnotationType != null ?
|
||||
RepeatableContainers.of(annotationType, containerAnnotationType) :
|
||||
RepeatableContainers.standardRepeatables();
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(
|
||||
annotationType, containerAnnotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
repeatableContainers, annotationFilter).stream(annotationType)
|
||||
.map(MergedAnnotation::withNonMergedAttributes)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -485,15 +454,11 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A findAnnotation(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.findAnnotation(annotatedElement, annotationType)
|
||||
).toNullable(() ->{
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
});
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -515,16 +480,11 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A findAnnotation(Method method,
|
||||
@Nullable Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.findAnnotation(method, annotationType)
|
||||
).toNullable(() -> {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
|
||||
});
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(method, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -553,16 +513,11 @@ public abstract class AnnotationUtils {
|
|||
public static <A extends Annotation> A findAnnotation(Class<?> clazz,
|
||||
Class<A> annotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.findAnnotation(clazz, annotationType)
|
||||
).toNullable(() -> {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
);
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.EXHAUSTIVE,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType).withNonMergedAttributes()
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -591,17 +546,11 @@ public abstract class AnnotationUtils {
|
|||
public static Class<?> findAnnotationDeclaringClass(
|
||||
Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) {
|
||||
|
||||
return MigrateMethod.<Class<?>> from(() ->
|
||||
InternalAnnotationUtils.findAnnotationDeclaringClass(annotationType,
|
||||
clazz)
|
||||
).toNullable(() -> {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType, MergedAnnotation::isDirectlyPresent)
|
||||
.getSource();
|
||||
}
|
||||
);
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationType);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.get(annotationType, MergedAnnotation::isDirectlyPresent)
|
||||
.getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -632,19 +581,13 @@ public abstract class AnnotationUtils {
|
|||
public static Class<?> findAnnotationDeclaringClassForTypes(
|
||||
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
|
||||
|
||||
return MigrateMethod.<Class<?>> from(() ->
|
||||
InternalAnnotationUtils.findAnnotationDeclaringClassForTypes(
|
||||
annotationTypes, clazz)
|
||||
).toNullable(() -> {
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationTypes);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes).and(MergedAnnotation::isDirectlyPresent))
|
||||
.map(MergedAnnotation::getSource)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
);
|
||||
AnnotationFilter annotationFilter = AnnotationFilter.mostAppropriateFor(annotationTypes);
|
||||
return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPER_CLASS,
|
||||
RepeatableContainers.none(), annotationFilter)
|
||||
.stream()
|
||||
.filter(MergedAnnotationPredicates.typeIn(annotationTypes).and(MergedAnnotation::isDirectlyPresent))
|
||||
.map(MergedAnnotation::getSource)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -668,11 +611,7 @@ public abstract class AnnotationUtils {
|
|||
public static boolean isAnnotationDeclaredLocally(
|
||||
Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)
|
||||
).withSkippedOriginalExceptionCheck().to(() ->
|
||||
MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent()
|
||||
);
|
||||
return MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -697,15 +636,11 @@ public abstract class AnnotationUtils {
|
|||
public static boolean isAnnotationInherited(
|
||||
Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.isAnnotationInherited(annotationType, clazz)
|
||||
).to(() ->
|
||||
MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.findFirst().orElseGet(MergedAnnotation::missing)
|
||||
.getAggregateIndex() > 0
|
||||
);
|
||||
return MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS)
|
||||
.stream(annotationType)
|
||||
.filter(MergedAnnotation::isDirectlyPresent)
|
||||
.findFirst().orElseGet(MergedAnnotation::missing)
|
||||
.getAggregateIndex() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -720,13 +655,8 @@ public abstract class AnnotationUtils {
|
|||
Class<? extends Annotation> annotationType,
|
||||
@Nullable Class<? extends Annotation> metaAnnotationType) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.isAnnotationMetaPresent(annotationType,
|
||||
metaAnnotationType)
|
||||
).to(() ->
|
||||
MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE)
|
||||
.isPresent(annotationType)
|
||||
);
|
||||
return MergedAnnotations.from(annotationType, SearchStrategy.EXHAUSTIVE)
|
||||
.isPresent(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -736,11 +666,7 @@ public abstract class AnnotationUtils {
|
|||
* @return {@code true} if the annotation is in the {@code java.lang.annotation} package
|
||||
*/
|
||||
public static boolean isInJavaLangAnnotationPackage(@Nullable Annotation annotation) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.isInJavaLangAnnotationPackage(annotation)
|
||||
).to(() ->
|
||||
JAVA_LANG_ANNOTATION_FILTER.matches(annotation)
|
||||
);
|
||||
return JAVA_LANG_ANNOTATION_FILTER.matches(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -751,11 +677,7 @@ public abstract class AnnotationUtils {
|
|||
* @since 4.2
|
||||
*/
|
||||
public static boolean isInJavaLangAnnotationPackage(@Nullable String annotationType) {
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.isInJavaLangAnnotationPackage(annotationType)
|
||||
).to(() ->
|
||||
JAVA_LANG_ANNOTATION_FILTER.matches(annotationType)
|
||||
);
|
||||
return JAVA_LANG_ANNOTATION_FILTER.matches(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -771,12 +693,8 @@ public abstract class AnnotationUtils {
|
|||
* @see #getAnnotationAttributes(Annotation)
|
||||
*/
|
||||
public static void validateAnnotation(Annotation annotation) {
|
||||
MigrateMethod.fromCall(() ->
|
||||
InternalAnnotationUtils.validateAnnotation(annotation)
|
||||
).to(() ->
|
||||
AttributeMethods.forAnnotationType(annotation.annotationType())
|
||||
.validate(annotation)
|
||||
);
|
||||
AttributeMethods.forAnnotationType(annotation.annotationType())
|
||||
.validate(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -882,16 +800,11 @@ public abstract class AnnotationUtils {
|
|||
@Nullable AnnotatedElement annotatedElement, Annotation annotation,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getAnnotationAttributes(annotatedElement,
|
||||
annotation, classValuesAsString, nestedAnnotationsAsMap)
|
||||
).to(() -> {
|
||||
ClassLoader classLoader = annotation.annotationType().getClassLoader();
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return MergedAnnotation.from(annotatedElement, annotation)
|
||||
.withNonMergedAttributes()
|
||||
.asMap(getAnnotationAttributesFactory(classLoader), mapValues);
|
||||
});
|
||||
ClassLoader classLoader = annotation.annotationType().getClassLoader();
|
||||
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
|
||||
return MergedAnnotation.from(annotatedElement, annotation)
|
||||
.withNonMergedAttributes()
|
||||
.asMap(getAnnotationAttributesFactory(classLoader), mapValues);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -899,8 +812,8 @@ public abstract class AnnotationUtils {
|
|||
ClassLoader classLoader) {
|
||||
|
||||
return annotation -> {
|
||||
Class<Annotation> type = (Class<Annotation>) ClassUtils.resolveClassName(
|
||||
annotation.getType(), classLoader);
|
||||
Class<Annotation> type = (Class<Annotation>) ClassUtils
|
||||
.resolveClassName(annotation.getType(), classLoader);
|
||||
return new AnnotationAttributes(type, true);
|
||||
};
|
||||
}
|
||||
|
|
@ -912,15 +825,12 @@ public abstract class AnnotationUtils {
|
|||
* @since 4.3.2
|
||||
*/
|
||||
public static void registerDefaultValues(AnnotationAttributes attributes) {
|
||||
AnnotationAttributes copy = new AnnotationAttributes(attributes);
|
||||
MigrateMethod.fromCall(()->
|
||||
InternalAnnotationUtils.registerDefaultValues(copy)
|
||||
).withArgumentCheck(copy, attributes).to(()-> {
|
||||
Class<? extends Annotation> annotationType = attributes.annotationType();
|
||||
if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) {
|
||||
getDefaultValues(annotationType).forEach(attributes::putIfAbsent);
|
||||
}
|
||||
});
|
||||
Class<? extends Annotation> annotationType = attributes.annotationType();
|
||||
if (annotationType != null && Modifier.isPublic(annotationType.getModifiers()) &&
|
||||
!AnnotationFilter.PLAIN.matches(annotationType)) {
|
||||
Map<String, DefaultValueHolder> defaultValues = getDefaultValues(annotationType);
|
||||
defaultValues.forEach(attributes::putIfAbsent);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, DefaultValueHolder> getDefaultValues(
|
||||
|
|
@ -977,49 +887,41 @@ public abstract class AnnotationUtils {
|
|||
* @see #getDefaultValue(Class, String)
|
||||
*/
|
||||
public static void postProcessAnnotationAttributes(@Nullable Object annotatedElement,
|
||||
AnnotationAttributes attributes, boolean classValuesAsString) {
|
||||
@Nullable AnnotationAttributes attributes, boolean classValuesAsString) {
|
||||
|
||||
AnnotationAttributes copy = new AnnotationAttributes(attributes);
|
||||
MigrateMethod.fromCall(() ->
|
||||
InternalAnnotationUtils.postProcessAnnotationAttributes(annotatedElement, copy,
|
||||
classValuesAsString)
|
||||
).withArgumentCheck(copy, attributes).to(()-> {
|
||||
if (attributes == null || attributes.annotationType() == null) {
|
||||
return;
|
||||
}
|
||||
if (!attributes.validated) {
|
||||
Class<? extends Annotation> annotationType = attributes.annotationType();
|
||||
if (annotationType != null) {
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings
|
||||
.forAnnotationType(annotationType).get(0);
|
||||
for (int i = 0; i < mapping.getMirrorSets().size(); i++) {
|
||||
MirrorSet mirrorSet = mapping.getMirrorSets().get(i);
|
||||
int resolved = mirrorSet.resolve(attributes.displayName, attributes,
|
||||
AnnotationUtils::getAttributeValueForMirrorResolution);
|
||||
if (resolved != -1) {
|
||||
Method attribute = mapping.getAttributes().get(resolved);
|
||||
Object value = attributes.get(attribute.getName());
|
||||
for (int j = 0; j < mirrorSet.size(); j++) {
|
||||
Method mirror = mirrorSet.get(j);
|
||||
if (mirror != attribute) {
|
||||
attributes.put(mirror.getName(),
|
||||
adaptValue(annotatedElement, value, classValuesAsString));
|
||||
}
|
||||
}
|
||||
if (attributes == null || attributes.annotationType() == null) {
|
||||
return;
|
||||
}
|
||||
if (!attributes.validated) {
|
||||
Class<? extends Annotation> annotationType = attributes.annotationType();
|
||||
AnnotationTypeMapping mapping = AnnotationTypeMappings
|
||||
.forAnnotationType(annotationType).get(0);
|
||||
for (int i = 0; i < mapping.getMirrorSets().size(); i++) {
|
||||
MirrorSet mirrorSet = mapping.getMirrorSets().get(i);
|
||||
int resolved = mirrorSet.resolve(attributes.displayName, attributes,
|
||||
AnnotationUtils::getAttributeValueForMirrorResolution);
|
||||
if (resolved != -1) {
|
||||
Method attribute = mapping.getAttributes().get(resolved);
|
||||
Object value = attributes.get(attribute.getName());
|
||||
for (int j = 0; j < mirrorSet.size(); j++) {
|
||||
Method mirror = mirrorSet.get(j);
|
||||
if (mirror != attribute) {
|
||||
attributes.put(mirror.getName(),
|
||||
adaptValue(annotatedElement, value, classValuesAsString));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) {
|
||||
String attributeName = attributeEntry.getKey();
|
||||
Object value = attributeEntry.getValue();
|
||||
if (value instanceof DefaultValueHolder) {
|
||||
value = ((DefaultValueHolder) value).defaultValue;
|
||||
attributes.put(attributeName,
|
||||
adaptValue(annotatedElement, value, classValuesAsString));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) {
|
||||
String attributeName = attributeEntry.getKey();
|
||||
Object value = attributeEntry.getValue();
|
||||
if (value instanceof DefaultValueHolder) {
|
||||
value = ((DefaultValueHolder) value).defaultValue;
|
||||
attributes.put(attributeName,
|
||||
adaptValue(annotatedElement, value, classValuesAsString));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getAttributeValueForMirrorResolution(Method attribute,
|
||||
|
|
@ -1032,16 +934,20 @@ public abstract class AnnotationUtils {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
static Object adaptValue(@Nullable Object annotatedElement, @Nullable Object value,
|
||||
boolean classValuesAsString) {
|
||||
private static Object adaptValue(@Nullable Object annotatedElement,
|
||||
@Nullable Object value, boolean classValuesAsString) {
|
||||
|
||||
if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
return ((Class<?>) value).getName();
|
||||
}
|
||||
if (value instanceof Class[]) {
|
||||
return Arrays.stream((Class<?>[]) value).map(Class::getName).toArray(
|
||||
String[]::new);
|
||||
Class<?>[] classes = (Class<?>[]) value;
|
||||
String[] names = new String[classes.length];
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
names[i] = classes[i].getName();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
||||
if (value instanceof Annotation) {
|
||||
|
|
@ -1053,8 +959,8 @@ public abstract class AnnotationUtils {
|
|||
Annotation[] synthesized = (Annotation[]) Array.newInstance(
|
||||
annotations.getClass().getComponentType(), annotations.length);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
synthesized[i] = MergedAnnotation.from(annotatedElement,
|
||||
annotations[i]).synthesize();
|
||||
synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i])
|
||||
.synthesize();
|
||||
}
|
||||
return synthesized;
|
||||
}
|
||||
|
|
@ -1088,7 +994,72 @@ public abstract class AnnotationUtils {
|
|||
public static Object getValue(@Nullable Annotation annotation,
|
||||
@Nullable String attributeName) {
|
||||
|
||||
return InternalAnnotationUtils.getValue(annotation, attributeName);
|
||||
if (annotation == null || !StringUtils.hasText(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
rethrowAnnotationConfigurationException(ex.getTargetException());
|
||||
throw new IllegalStateException(
|
||||
"Could not obtain value for annotation attribute '" +
|
||||
attributeName + "' in " + annotation, ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the supplied throwable is an {@link AnnotationConfigurationException},
|
||||
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
|
||||
* allowing it to propagate to the caller.
|
||||
* <p>Otherwise, this method does nothing.
|
||||
* @param ex the throwable to inspect
|
||||
*/
|
||||
private static void rethrowAnnotationConfigurationException(Throwable ex) {
|
||||
if (ex instanceof AnnotationConfigurationException) {
|
||||
throw (AnnotationConfigurationException) ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the supplied annotation introspection exception.
|
||||
* <p>If the supplied exception is an {@link AnnotationConfigurationException},
|
||||
* it will simply be thrown, allowing it to propagate to the caller, and
|
||||
* nothing will be logged.
|
||||
* <p>Otherwise, this method logs an introspection failure (in particular
|
||||
* {@code TypeNotPresentExceptions}) before moving on, assuming nested
|
||||
* Class values were not resolvable within annotation attributes and
|
||||
* thereby effectively pretending there were no annotations on the specified
|
||||
* element.
|
||||
* @param element the element that we tried to introspect annotations on
|
||||
* @param ex the exception that we encountered
|
||||
* @see #rethrowAnnotationConfigurationException
|
||||
*/
|
||||
private static void handleIntrospectionFailure(@Nullable AnnotatedElement element,
|
||||
Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
boolean meta = false;
|
||||
if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
|
||||
// Meta-annotation or (default) value lookup on an annotation type
|
||||
logger = IntrospectionFailureLogger.DEBUG;
|
||||
meta = true;
|
||||
}
|
||||
if (logger.isEnabled()) {
|
||||
String message = meta ?
|
||||
"Failed to meta-introspect annotation " :
|
||||
"Failed to introspect annotations on ";
|
||||
logger.log(message + element + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1144,15 +1115,11 @@ public abstract class AnnotationUtils {
|
|||
@Nullable Class<? extends Annotation> annotationType,
|
||||
@Nullable String attributeName) {
|
||||
|
||||
return MigrateMethod.from(() ->
|
||||
InternalAnnotationUtils.getDefaultValue(annotationType, attributeName)
|
||||
).toNullable(() -> {
|
||||
if (annotationType == null || !StringUtils.hasText(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
return MergedAnnotation.from(annotationType)
|
||||
.getDefaultValue(attributeName).orElse(null);
|
||||
});
|
||||
if (annotationType == null || !StringUtils.hasText(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
return MergedAnnotation.from(annotationType)
|
||||
.getDefaultValue(attributeName).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1211,18 +1178,13 @@ public abstract class AnnotationUtils {
|
|||
Map<String, Object> attributes, Class<A> annotationType,
|
||||
@Nullable AnnotatedElement annotatedElement) {
|
||||
|
||||
return MigrateMethod.from(()->
|
||||
InternalAnnotationUtils.synthesizeAnnotation(attributes, annotationType,
|
||||
annotatedElement)
|
||||
).to(()-> {
|
||||
try {
|
||||
return MergedAnnotation.from(annotatedElement, annotationType, attributes)
|
||||
.synthesize();
|
||||
}
|
||||
catch (NoSuchElementException | IllegalStateException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return MergedAnnotation.from(annotatedElement, annotationType, attributes)
|
||||
.synthesize();
|
||||
}
|
||||
catch (NoSuchElementException | IllegalStateException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1277,17 +1239,12 @@ public abstract class AnnotationUtils {
|
|||
|
||||
private static <A extends Annotation> A synthesizeAnnotation(A annotation,
|
||||
@Nullable Object annotatedElement) {
|
||||
|
||||
return MigrateMethod.from(()->
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation, annotatedElement)
|
||||
).withSkippedOriginalExceptionCheck().to(() -> {
|
||||
if (annotation instanceof SynthesizedAnnotation ||
|
||||
AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement) ||
|
||||
AnnotationFilter.PLAIN.matches(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
|
||||
});
|
||||
if (annotation instanceof SynthesizedAnnotation ||
|
||||
AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement) ||
|
||||
AnnotationFilter.PLAIN.matches(annotation)) {
|
||||
return annotation;
|
||||
}
|
||||
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1295,7 +1252,8 @@ public abstract class AnnotationUtils {
|
|||
* @since 4.3.15
|
||||
*/
|
||||
public static void clearCache() {
|
||||
InternalAnnotationUtils.clearCache();
|
||||
AnnotationTypeMappings.clearCache();
|
||||
AnnotationsScanner.clearCache();
|
||||
}
|
||||
|
||||
private static <A extends Annotation> boolean isSingleLevelPresent(
|
||||
|
|
@ -1304,4 +1262,22 @@ public abstract class AnnotationUtils {
|
|||
return depth == 0 || depth == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal holder used to wrap default values.
|
||||
*/
|
||||
private static class DefaultValueHolder {
|
||||
|
||||
final Object defaultValue;
|
||||
|
||||
public DefaultValueHolder(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "*" + this.defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link AnnotationAttributeExtractor} strategy
|
||||
* that is backed by an {@link Annotation}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @see Annotation
|
||||
* @see AliasFor
|
||||
* @see AbstractAliasAwareAnnotationAttributeExtractor
|
||||
* @see MapAnnotationAttributeExtractor
|
||||
* @see AnnotationUtils#synthesizeAnnotation
|
||||
*/
|
||||
class DefaultAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttributeExtractor<Annotation> {
|
||||
|
||||
/**
|
||||
* Construct a new {@code DefaultAnnotationAttributeExtractor}.
|
||||
* @param annotation the annotation to synthesize; never {@code null}
|
||||
* @param annotatedElement the element that is annotated with the supplied
|
||||
* annotation; may be {@code null} if unknown
|
||||
*/
|
||||
DefaultAnnotationAttributeExtractor(Annotation annotation, @Nullable Object annotatedElement) {
|
||||
super(annotation.annotationType(), annotatedElement, annotation);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object getRawAttributeValue(Method attributeMethod) {
|
||||
ReflectionUtils.makeAccessible(attributeMethod);
|
||||
return ReflectionUtils.invokeMethod(attributeMethod, getSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object getRawAttributeValue(String attributeName) {
|
||||
Method attributeMethod = ReflectionUtils.findMethod(getAnnotationType(), attributeName);
|
||||
return (attributeMethod != null ? getRawAttributeValue(attributeMethod) : null);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link AnnotationAttributeExtractor} strategy that
|
||||
* is backed by a {@link Map}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @see Annotation
|
||||
* @see AliasFor
|
||||
* @see AbstractAliasAwareAnnotationAttributeExtractor
|
||||
* @see DefaultAnnotationAttributeExtractor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
*/
|
||||
class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttributeExtractor<Map<String, Object>> {
|
||||
|
||||
/**
|
||||
* Construct a new {@code MapAnnotationAttributeExtractor}.
|
||||
* <p>The supplied map must contain a key-value pair for every attribute
|
||||
* defined in the supplied {@code annotationType} that is not aliased or
|
||||
* does not have a default value.
|
||||
* @param attributes the map of annotation attributes; never {@code null}
|
||||
* @param annotationType the type of annotation to synthesize; never {@code null}
|
||||
* @param annotatedElement the element that is annotated with the annotation
|
||||
* of the supplied type; may be {@code null} if unknown
|
||||
*/
|
||||
MapAnnotationAttributeExtractor(Map<String, Object> attributes, Class<? extends Annotation> annotationType,
|
||||
@Nullable AnnotatedElement annotatedElement) {
|
||||
|
||||
super(annotationType, annotatedElement, enrichAndValidateAttributes(attributes, annotationType));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object getRawAttributeValue(Method attributeMethod) {
|
||||
return getRawAttributeValue(attributeMethod.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object getRawAttributeValue(String attributeName) {
|
||||
return getSource().get(attributeName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enrich and validate the supplied <em>attributes</em> map by ensuring
|
||||
* that it contains a non-null entry for each annotation attribute in
|
||||
* the specified {@code annotationType} and that the type of the entry
|
||||
* matches the return type for the corresponding annotation attribute.
|
||||
* <p>If an entry is a map (presumably of annotation attributes), an
|
||||
* attempt will be made to synthesize an annotation from it. Similarly,
|
||||
* if an entry is an array of maps, an attempt will be made to synthesize
|
||||
* an array of annotations from those maps.
|
||||
* <p>If an attribute is missing in the supplied map, it will be set
|
||||
* either to the value of its alias (if an alias exists) or to the
|
||||
* value of the attribute's default value (if defined), and otherwise
|
||||
* an {@link IllegalArgumentException} will be thrown.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> enrichAndValidateAttributes(
|
||||
Map<String, Object> originalAttributes, Class<? extends Annotation> annotationType) {
|
||||
|
||||
Map<String, Object> attributes = new LinkedHashMap<>(originalAttributes);
|
||||
Map<String, List<String>> attributeAliasMap = InternalAnnotationUtils.getAttributeAliasMap(annotationType);
|
||||
|
||||
for (Method attributeMethod : InternalAnnotationUtils.getAttributeMethods(annotationType)) {
|
||||
String attributeName = attributeMethod.getName();
|
||||
Object attributeValue = attributes.get(attributeName);
|
||||
|
||||
// if attribute not present, check aliases
|
||||
if (attributeValue == null) {
|
||||
List<String> aliasNames = attributeAliasMap.get(attributeName);
|
||||
if (aliasNames != null) {
|
||||
for (String aliasName : aliasNames) {
|
||||
Object aliasValue = attributes.get(aliasName);
|
||||
if (aliasValue != null) {
|
||||
attributeValue = aliasValue;
|
||||
attributes.put(attributeName, attributeValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if aliases not present, check default
|
||||
if (attributeValue == null) {
|
||||
Object defaultValue = AnnotationUtils.getDefaultValue(annotationType, attributeName);
|
||||
if (defaultValue != null) {
|
||||
attributeValue = defaultValue;
|
||||
attributes.put(attributeName, attributeValue);
|
||||
}
|
||||
}
|
||||
|
||||
// if still null
|
||||
Assert.notNull(attributeValue, () -> String.format(
|
||||
"Attributes map %s returned null for required attribute '%s' defined by annotation type [%s].",
|
||||
attributes, attributeName, annotationType.getName()));
|
||||
|
||||
// finally, ensure correct type
|
||||
Class<?> requiredReturnType = attributeMethod.getReturnType();
|
||||
Class<?> actualReturnType = attributeValue.getClass();
|
||||
|
||||
if (!ClassUtils.isAssignable(requiredReturnType, actualReturnType)) {
|
||||
boolean converted = false;
|
||||
|
||||
// Single element overriding an array of the same type?
|
||||
if (requiredReturnType.isArray() && requiredReturnType.getComponentType() == actualReturnType) {
|
||||
Object array = Array.newInstance(requiredReturnType.getComponentType(), 1);
|
||||
Array.set(array, 0, attributeValue);
|
||||
attributes.put(attributeName, array);
|
||||
converted = true;
|
||||
}
|
||||
|
||||
// Nested map representing a single annotation?
|
||||
else if (Annotation.class.isAssignableFrom(requiredReturnType) &&
|
||||
Map.class.isAssignableFrom(actualReturnType)) {
|
||||
Class<? extends Annotation> nestedAnnotationType =
|
||||
(Class<? extends Annotation>) requiredReturnType;
|
||||
Map<String, Object> map = (Map<String, Object>) attributeValue;
|
||||
attributes.put(attributeName, AnnotationUtils.synthesizeAnnotation(map, nestedAnnotationType, null));
|
||||
converted = true;
|
||||
}
|
||||
|
||||
// Nested array of maps representing an array of annotations?
|
||||
else if (requiredReturnType.isArray() && actualReturnType.isArray() &&
|
||||
Annotation.class.isAssignableFrom(requiredReturnType.getComponentType()) &&
|
||||
Map.class.isAssignableFrom(actualReturnType.getComponentType())) {
|
||||
Class<? extends Annotation> nestedAnnotationType =
|
||||
(Class<? extends Annotation>) requiredReturnType.getComponentType();
|
||||
Map<String, Object>[] maps = (Map<String, Object>[]) attributeValue;
|
||||
attributes.put(attributeName, InternalAnnotationUtils.synthesizeAnnotationArray(maps, nestedAnnotationType));
|
||||
converted = true;
|
||||
}
|
||||
|
||||
Assert.isTrue(converted, () -> String.format(
|
||||
"Attributes map %s returned a value of type [%s] for attribute '%s', " +
|
||||
"but a value of type [%s] is required as defined by annotation type [%s].",
|
||||
attributes, actualReturnType.getName(), attributeName, requiredReturnType.getName(),
|
||||
annotationType.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.annotation.InternalAnnotationUtils.DefaultValueHolder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Internal class used to help migrate annotation util methods to a new implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 5.2
|
||||
*/
|
||||
final class MigrateMethod {
|
||||
|
||||
private MigrateMethod() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReplacementMethod} builder for the deprecated method.
|
||||
* @param originalMethod the original method being migrated
|
||||
* @return a replacement builder.
|
||||
*/
|
||||
static <T> ReplacementMethod<T> from(Supplier<T> originalMethod) {
|
||||
return new ReplacementMethod<>(originalMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReplacementVoidMethod} for the deprecated method.
|
||||
* @param originalMethod the original method being migrated
|
||||
* @return a replacement builder.
|
||||
*/
|
||||
static ReplacementVoidMethod fromCall(Runnable originalMethod) {
|
||||
return new ReplacementVoidMethod(originalMethod);
|
||||
}
|
||||
|
||||
private static boolean isEquivalent(@Nullable Object result, @Nullable Object expectedResult) {
|
||||
if (ObjectUtils.nullSafeEquals(result, expectedResult)) {
|
||||
return true;
|
||||
}
|
||||
if (result == null && String.valueOf(expectedResult).startsWith(
|
||||
"@org.springframework.lang.")) {
|
||||
// Original methods don't filter spring annotation but we do
|
||||
return true;
|
||||
}
|
||||
if (result == null || expectedResult == null) {
|
||||
return false;
|
||||
}
|
||||
if (result instanceof DefaultValueHolder && expectedResult instanceof DefaultValueHolder) {
|
||||
return isEquivalent(((DefaultValueHolder) result).defaultValue,
|
||||
((DefaultValueHolder) expectedResult).defaultValue);
|
||||
}
|
||||
if (result instanceof Map && expectedResult instanceof Map) {
|
||||
return isEquivalentMap((Map<?, ?>) result, (Map<?, ?>) expectedResult);
|
||||
}
|
||||
if (result instanceof List && expectedResult instanceof List) {
|
||||
return isEquivalentList((List<?>) result, (List<?>) expectedResult);
|
||||
}
|
||||
if (result instanceof Object[] && expectedResult instanceof Object[]) {
|
||||
return isEquivalentArray((Object[]) result, (Object[]) expectedResult);
|
||||
}
|
||||
if (result instanceof Object[]) {
|
||||
if (isEquivalentArray((Object[]) result, new Object[] { expectedResult })) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(result instanceof Object[]) && expectedResult instanceof Object[]) {
|
||||
if (isEquivalentArray(new Object[] { result }, (Object[]) expectedResult)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isEquivalentMap(Map<?, ?> result, Map<?, ?> expectedResult) {
|
||||
if (result.size() != expectedResult.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<?, ?> entry : result.entrySet()) {
|
||||
if (!expectedResult.containsKey(entry.getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!isEquivalent(entry.getValue(), expectedResult.get(entry.getKey()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isEquivalentList(List<?> result, List<?> expectedResult) {
|
||||
if (result.size() != expectedResult.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
if (!isEquivalent(result.get(i), expectedResult.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isEquivalentArray(Object[] result, Object[] expectedResult) {
|
||||
if (result.length != expectedResult.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
if (!isEquivalent(result[i], expectedResult[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to complete replacement details for a deprecated annotation method.
|
||||
* @param <T> the return type
|
||||
*/
|
||||
static class ReplacementMethod<T> {
|
||||
|
||||
private final Supplier<T> originalMethod;
|
||||
|
||||
@Nullable
|
||||
private Supplier<String> description;
|
||||
|
||||
private boolean skipOriginalExceptionCheck;
|
||||
|
||||
private BooleanSupplier skipEquivalentCheck = () -> false;
|
||||
|
||||
ReplacementMethod(Supplier<T> deprecatedMethod) {
|
||||
this.originalMethod = deprecatedMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a description for the method.
|
||||
* @param description a description supplier
|
||||
* @return this instance
|
||||
*/
|
||||
public ReplacementMethod<T> withDescription(Supplier<String> description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementMethod<T> withSkippedOriginalExceptionCheck() {
|
||||
this.skipOriginalExceptionCheck = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementMethod<T> withSkippedEquivalentCheck(BooleanSupplier supplier) {
|
||||
this.skipEquivalentCheck = supplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the replacement method that should be used instead of the deprecated
|
||||
* one. The replacement method is called, and when appropriate the result is
|
||||
* checked against the deprecated method.
|
||||
* @param replacementMethod the replacement method
|
||||
* @return the result of the replacement method
|
||||
*/
|
||||
public T to(Supplier<T> replacementMethod) {
|
||||
T result = toNullable(replacementMethod);
|
||||
if (result == null) {
|
||||
throw new IllegalStateException("Unexpected null result");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the replacement method that should be used instead of the deprecated
|
||||
* one. The replacement method is called, and when appropriate the result is
|
||||
* checked against the deprecated method.
|
||||
* @param replacementMethod the replacement method
|
||||
* @return the result of the replacement method
|
||||
*/
|
||||
@Nullable
|
||||
public T toNullable(Supplier<T> replacementMethod) {
|
||||
T result = tryInvoke(replacementMethod);
|
||||
T expectedResult = this.originalMethod.get();
|
||||
if (!isEquivalent(result, expectedResult)) {
|
||||
if (this.skipEquivalentCheck.getAsBoolean()) {
|
||||
return expectedResult;
|
||||
}
|
||||
String description = (this.description != null ? " [" +
|
||||
this.description.get() + "]" : "");
|
||||
throw new IllegalStateException("Expected " + expectedResult +
|
||||
" got " + result + description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private T tryInvoke(Supplier<T> replacementMethod) {
|
||||
try {
|
||||
return replacementMethod.get();
|
||||
}
|
||||
catch (RuntimeException expected) {
|
||||
try {
|
||||
T expectedResult = this.originalMethod.get();
|
||||
if (this.skipOriginalExceptionCheck) {
|
||||
return expectedResult;
|
||||
}
|
||||
throw new Error("Expected exception not thrown", expected);
|
||||
}
|
||||
catch (RuntimeException actual) {
|
||||
if (!expected.getClass().isInstance(actual)) {
|
||||
throw new Error(
|
||||
"Exception is not " + expected.getClass().getName(),
|
||||
actual);
|
||||
}
|
||||
throw actual;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to complete replacement details for a deprecated annotation method that
|
||||
* returns void.
|
||||
*/
|
||||
static class ReplacementVoidMethod {
|
||||
|
||||
private final Runnable originalMethod;
|
||||
|
||||
private final List<Object[]> argumentChecks = new ArrayList<>();
|
||||
|
||||
public ReplacementVoidMethod(Runnable originalMethod) {
|
||||
this.originalMethod = originalMethod;
|
||||
}
|
||||
|
||||
public ReplacementVoidMethod withArgumentCheck(Object originalArgument,
|
||||
Object replacementArgument) {
|
||||
this.argumentChecks.add(
|
||||
new Object[] { originalArgument, replacementArgument });
|
||||
return this;
|
||||
}
|
||||
|
||||
public void to(Runnable replacementMethod) {
|
||||
tryInvoke(this.originalMethod);
|
||||
replacementMethod.run();
|
||||
for (Object[] arguments : this.argumentChecks) {
|
||||
Object expectedArgument = arguments[0];
|
||||
Object actualArgument = arguments[1];
|
||||
Assert.state(isEquivalent(actualArgument, expectedArgument),
|
||||
() -> "Expected argument mutation of " + expectedArgument
|
||||
+ " got " + actualArgument);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryInvoke(Runnable replacementMethod) {
|
||||
try {
|
||||
replacementMethod.run();
|
||||
}
|
||||
catch (RuntimeException expected) {
|
||||
try {
|
||||
this.originalMethod.run();
|
||||
throw new Error("Expected exception not thrown", expected);
|
||||
}
|
||||
catch (RuntimeException actual) {
|
||||
if (!expected.getClass().isInstance(actual)) {
|
||||
throw new Error(
|
||||
"Exception is not " + expected.getClass().getName(),
|
||||
actual);
|
||||
}
|
||||
throw actual;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} for an {@link Annotation} that Spring has
|
||||
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
|
||||
* functionality.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @see Annotation
|
||||
* @see AnnotationAttributeExtractor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
*/
|
||||
class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final AnnotationAttributeExtractor<?> attributeExtractor;
|
||||
|
||||
private final Map<String, Object> valueCache = new ConcurrentHashMap<>(8);
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code SynthesizedAnnotationInvocationHandler} for
|
||||
* the supplied {@link AnnotationAttributeExtractor}.
|
||||
* @param attributeExtractor the extractor to delegate to
|
||||
*/
|
||||
SynthesizedAnnotationInvocationHandler(AnnotationAttributeExtractor<?> attributeExtractor) {
|
||||
Assert.notNull(attributeExtractor, "AnnotationAttributeExtractor must not be null");
|
||||
this.attributeExtractor = attributeExtractor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (ReflectionUtils.isEqualsMethod(method)) {
|
||||
return annotationEquals(args[0]);
|
||||
}
|
||||
if (ReflectionUtils.isHashCodeMethod(method)) {
|
||||
return annotationHashCode();
|
||||
}
|
||||
if (ReflectionUtils.isToStringMethod(method)) {
|
||||
return annotationToString();
|
||||
}
|
||||
if (InternalAnnotationUtils.isAnnotationTypeMethod(method)) {
|
||||
return annotationType();
|
||||
}
|
||||
if (!InternalAnnotationUtils.isAttributeMethod(method)) {
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
|
||||
}
|
||||
return getAttributeValue(method);
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> annotationType() {
|
||||
return this.attributeExtractor.getAnnotationType();
|
||||
}
|
||||
|
||||
private Object getAttributeValue(Method attributeMethod) {
|
||||
String attributeName = attributeMethod.getName();
|
||||
Object value = this.valueCache.get(attributeName);
|
||||
if (value == null) {
|
||||
value = this.attributeExtractor.getAttributeValue(attributeMethod);
|
||||
if (value == null) {
|
||||
String msg = String.format("%s returned null for attribute name [%s] from attribute source [%s]",
|
||||
this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource());
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
// Synthesize nested annotations before returning them.
|
||||
if (value instanceof Annotation) {
|
||||
value = InternalAnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
|
||||
}
|
||||
else if (value instanceof Annotation[]) {
|
||||
value = InternalAnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
|
||||
}
|
||||
|
||||
this.valueCache.put(attributeName, value);
|
||||
}
|
||||
|
||||
// Clone arrays so that users cannot alter the contents of values in our cache.
|
||||
if (value.getClass().isArray()) {
|
||||
value = cloneArray(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the provided array, ensuring that original component type is
|
||||
* retained.
|
||||
* @param array the array to clone
|
||||
*/
|
||||
private Object cloneArray(Object array) {
|
||||
if (array instanceof boolean[]) {
|
||||
return ((boolean[]) array).clone();
|
||||
}
|
||||
if (array instanceof byte[]) {
|
||||
return ((byte[]) array).clone();
|
||||
}
|
||||
if (array instanceof char[]) {
|
||||
return ((char[]) array).clone();
|
||||
}
|
||||
if (array instanceof double[]) {
|
||||
return ((double[]) array).clone();
|
||||
}
|
||||
if (array instanceof float[]) {
|
||||
return ((float[]) array).clone();
|
||||
}
|
||||
if (array instanceof int[]) {
|
||||
return ((int[]) array).clone();
|
||||
}
|
||||
if (array instanceof long[]) {
|
||||
return ((long[]) array).clone();
|
||||
}
|
||||
if (array instanceof short[]) {
|
||||
return ((short[]) array).clone();
|
||||
}
|
||||
|
||||
// else
|
||||
return ((Object[]) array).clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Annotation#equals(Object)} for a definition of the required algorithm.
|
||||
* @param other the other object to compare against
|
||||
*/
|
||||
private boolean annotationEquals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!annotationType().isInstance(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Method attributeMethod : InternalAnnotationUtils.getAttributeMethods(annotationType())) {
|
||||
Object thisValue = getAttributeValue(attributeMethod);
|
||||
Object otherValue = ReflectionUtils.invokeMethod(attributeMethod, other);
|
||||
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Annotation#hashCode()} for a definition of the required algorithm.
|
||||
*/
|
||||
private int annotationHashCode() {
|
||||
int result = 0;
|
||||
|
||||
for (Method attributeMethod : InternalAnnotationUtils.getAttributeMethods(annotationType())) {
|
||||
Object value = getAttributeValue(attributeMethod);
|
||||
int hashCode;
|
||||
if (value.getClass().isArray()) {
|
||||
hashCode = hashCodeForArray(value);
|
||||
}
|
||||
else {
|
||||
hashCode = value.hashCode();
|
||||
}
|
||||
result += (127 * attributeMethod.getName().hashCode()) ^ hashCode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: we can NOT use any of the {@code nullSafeHashCode()} methods
|
||||
* in Spring's {@link ObjectUtils} because those hash code generation
|
||||
* algorithms do not comply with the requirements specified in
|
||||
* {@link Annotation#hashCode()}.
|
||||
* @param array the array to compute the hash code for
|
||||
*/
|
||||
private int hashCodeForArray(Object array) {
|
||||
if (array instanceof boolean[]) {
|
||||
return Arrays.hashCode((boolean[]) array);
|
||||
}
|
||||
if (array instanceof byte[]) {
|
||||
return Arrays.hashCode((byte[]) array);
|
||||
}
|
||||
if (array instanceof char[]) {
|
||||
return Arrays.hashCode((char[]) array);
|
||||
}
|
||||
if (array instanceof double[]) {
|
||||
return Arrays.hashCode((double[]) array);
|
||||
}
|
||||
if (array instanceof float[]) {
|
||||
return Arrays.hashCode((float[]) array);
|
||||
}
|
||||
if (array instanceof int[]) {
|
||||
return Arrays.hashCode((int[]) array);
|
||||
}
|
||||
if (array instanceof long[]) {
|
||||
return Arrays.hashCode((long[]) array);
|
||||
}
|
||||
if (array instanceof short[]) {
|
||||
return Arrays.hashCode((short[]) array);
|
||||
}
|
||||
|
||||
// else
|
||||
return Arrays.hashCode((Object[]) array);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Annotation#toString()} for guidelines on the recommended format.
|
||||
*/
|
||||
private String annotationToString() {
|
||||
StringBuilder sb = new StringBuilder("@").append(annotationType().getName()).append("(");
|
||||
|
||||
Iterator<Method> iterator = InternalAnnotationUtils.getAttributeMethods(annotationType()).iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Method attributeMethod = iterator.next();
|
||||
sb.append(attributeMethod.getName());
|
||||
sb.append('=');
|
||||
sb.append(attributeValueToString(getAttributeValue(attributeMethod)));
|
||||
sb.append(iterator.hasNext() ? ", " : "");
|
||||
}
|
||||
|
||||
return sb.append(")").toString();
|
||||
}
|
||||
|
||||
private String attributeValueToString(Object value) {
|
||||
if (value instanceof Object[]) {
|
||||
return "[" + StringUtils.arrayToDelimitedString((Object[]) value, ", ") + "]";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -70,12 +70,10 @@ public class AnnotationIntrospectionFailureTests {
|
|||
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
|
||||
withExampleMetaAnnotation,
|
||||
exampleMetaAnnotationClass)).isNull();
|
||||
// Ideally hasAnnotation should return false, however, it currently
|
||||
// detects annotations that might ultimately not load
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
|
||||
exampleAnnotationClass)).isTrue();
|
||||
exampleAnnotationClass)).isFalse();
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
|
||||
exampleMetaAnnotationClass)).isTrue();
|
||||
exampleMetaAnnotationClass)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
|
@ -487,8 +486,8 @@ public class AnnotationUtilsTests {
|
|||
@Test
|
||||
public void getAnnotationAttributesWithAttributeAliasesWithDifferentValues() throws Exception {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(containsString("attribute 'value' and its alias 'path'"));
|
||||
exception.expectMessage(containsString("values of [{/enigma}] and [{/test}]"));
|
||||
exception.expectMessage(containsString("attribute 'path' and its alias 'value'"));
|
||||
exception.expectMessage(containsString("values of [{/test}] and [{/enigma}]"));
|
||||
|
||||
Method method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes");
|
||||
WebMapping webMapping = method.getAnnotation(WebMapping.class);
|
||||
|
|
@ -730,381 +729,6 @@ public class AnnotationUtilsTests {
|
|||
assertThat(set.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOverrideNameFromWrongTargetAnnotation() throws Exception {
|
||||
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
||||
assertThat("xmlConfigFile is not an alias for @Component.",
|
||||
InternalAnnotationUtils.getAttributeOverrideName(attribute, Component.class), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOverrideNameForNonAliasedAttribute() throws Exception {
|
||||
Method nonAliasedAttribute = ImplicitAliasesContextConfig.class.getDeclaredMethod("nonAliasedAttribute");
|
||||
assertThat(InternalAnnotationUtils.getAttributeOverrideName(nonAliasedAttribute, ContextConfig.class), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOverrideNameFromAliasedComposedAnnotation() throws Exception {
|
||||
Method attribute = AliasedComposedContextConfig.class.getDeclaredMethod("xmlConfigFile");
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(attribute, ContextConfig.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliases() throws Exception {
|
||||
Method xmlFile = ImplicitAliasesContextConfig.class.getDeclaredMethod("xmlFile");
|
||||
Method groovyScript = ImplicitAliasesContextConfig.class.getDeclaredMethod("groovyScript");
|
||||
Method value = ImplicitAliasesContextConfig.class.getDeclaredMethod("value");
|
||||
Method location1 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location1");
|
||||
Method location2 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location2");
|
||||
Method location3 = ImplicitAliasesContextConfig.class.getDeclaredMethod("location3");
|
||||
|
||||
// Meta-annotation attribute overrides
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovyScript, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(value, ContextConfig.class));
|
||||
|
||||
// Implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "groovyScript", "location1", "location2", "location3"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovyScript), containsInAnyOrder("value", "xmlFile", "location1", "location2", "location3"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(value), containsInAnyOrder("xmlFile", "groovyScript", "location1", "location2", "location3"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location1), containsInAnyOrder("xmlFile", "groovyScript", "value", "location2", "location3"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location2), containsInAnyOrder("xmlFile", "groovyScript", "value", "location1", "location3"));
|
||||
assertThat(InternalAnnotationUtils.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", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
|
||||
assertEquals("value", InternalAnnotationUtils.getAttributeOverrideName(groovyScript, ContextConfig.class));
|
||||
|
||||
// Implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("groovyScript"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovyScript), containsInAnyOrder("xmlFile"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeAliasNamesFromComposedAnnotationWithImplicitAliasesWithImpliedAliasNamesOmitted()
|
||||
throws Exception {
|
||||
|
||||
Method value = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("value");
|
||||
Method location = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("location");
|
||||
Method xmlFile = ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("xmlFile");
|
||||
|
||||
// Meta-annotation attribute overrides
|
||||
assertEquals("value", InternalAnnotationUtils.getAttributeOverrideName(value, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(location, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xmlFile, ContextConfig.class));
|
||||
|
||||
// Implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(value), containsInAnyOrder("location", "xmlFile"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(location), containsInAnyOrder("value", "xmlFile"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xmlFile), containsInAnyOrder("value", "location"));
|
||||
}
|
||||
|
||||
@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", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesContextConfig.class));
|
||||
assertEquals("groovyScript", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesContextConfig.class));
|
||||
|
||||
// Transitive meta-annotation attribute overrides
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xml, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ContextConfig.class));
|
||||
|
||||
// Transitive implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
|
||||
assertThat(InternalAnnotationUtils.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", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesForAliasPairContextConfig.class));
|
||||
assertEquals("groovyScript", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesForAliasPairContextConfig.class));
|
||||
|
||||
// Transitive implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeAliasNamesFromComposedAnnotationWithTransitiveImplicitAliasesWithImpliedAliasNamesOmitted()
|
||||
throws Exception {
|
||||
|
||||
Method xml = TransitiveImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("xml");
|
||||
Method groovy = TransitiveImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class.getDeclaredMethod("groovy");
|
||||
|
||||
// Meta-annotation attribute overrides
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(xml, ContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ContextConfig.class));
|
||||
|
||||
// Explicit meta-annotation attribute overrides
|
||||
assertEquals("xmlFile", InternalAnnotationUtils.getAttributeOverrideName(xml, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class));
|
||||
assertEquals("location", InternalAnnotationUtils.getAttributeOverrideName(groovy, ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class));
|
||||
|
||||
// Transitive implicit aliases
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(groovy), containsInAnyOrder("xml"));
|
||||
assertThat(InternalAnnotationUtils.getAttributeAliasNames(xml), containsInAnyOrder("groovy"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithoutAttributeAliases() throws Exception {
|
||||
Component component = WebController.class.getAnnotation(Component.class);
|
||||
assertNotNull(component);
|
||||
Component synthesizedComponent = InternalAnnotationUtils.synthesizeAnnotation(component);
|
||||
assertNotNull(synthesizedComponent);
|
||||
assertSame(component, synthesizedComponent);
|
||||
assertEquals("value attribute: ", "webController", synthesizedComponent.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAlreadySynthesizedAnnotation() throws Exception {
|
||||
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
|
||||
WebMapping webMapping = method.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMapping);
|
||||
WebMapping synthesizedWebMapping = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
|
||||
assertNotSame(webMapping, synthesizedWebMapping);
|
||||
WebMapping synthesizedAgainWebMapping = InternalAnnotationUtils.synthesizeAnnotation(synthesizedWebMapping);
|
||||
assertThat(synthesizedAgainWebMapping, instanceOf(SynthesizedAnnotation.class));
|
||||
assertSame(synthesizedWebMapping, synthesizedAgainWebMapping);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedAgainWebMapping.name());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedAgainWebMapping.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedAgainWebMapping.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWhereAliasForIsMissingAttributeDeclaration() throws Exception {
|
||||
AliasForWithMissingAttributeDeclaration annotation = AliasForWithMissingAttributeDeclarationClass.class.getAnnotation(AliasForWithMissingAttributeDeclaration.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("@AliasFor declaration on attribute 'foo' in annotation"));
|
||||
exception.expectMessage(containsString(AliasForWithMissingAttributeDeclaration.class.getName()));
|
||||
exception.expectMessage(containsString("points to itself"));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWhereAliasForHasDuplicateAttributeDeclaration() throws Exception {
|
||||
AliasForWithDuplicateAttributeDeclaration annotation = AliasForWithDuplicateAttributeDeclarationClass.class.getAnnotation(AliasForWithDuplicateAttributeDeclaration.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("In @AliasFor declared on attribute 'foo' in annotation"));
|
||||
exception.expectMessage(containsString(AliasForWithDuplicateAttributeDeclaration.class.getName()));
|
||||
exception.expectMessage(anyOf(
|
||||
containsString("attribute 'attribute' and its alias 'value' are present with values of [baz] and [bar]"),
|
||||
containsString("attribute 'attribute' and its alias 'value' are present with values of 'bar' and 'baz'")));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasForNonexistentAttribute() throws Exception {
|
||||
AliasForNonexistentAttribute annotation = AliasForNonexistentAttributeClass.class.getAnnotation(AliasForNonexistentAttribute.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(either(allOf(
|
||||
startsWith("Attribute 'foo' in"),
|
||||
containsString(AliasForNonexistentAttribute.class.getName()),
|
||||
containsString("is declared as an @AliasFor nonexistent attribute 'bar'"))
|
||||
).or(
|
||||
containsString("@AliasFor declaration on attribute 'foo' in annotation ["
|
||||
+ AliasForNonexistentAttribute.class.getName()
|
||||
+ "] declares an alias for 'bar' which is not present")));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasWithoutMirroredAliasFor() throws Exception {
|
||||
AliasForWithoutMirroredAliasFor annotation =
|
||||
AliasForWithoutMirroredAliasForClass.class.getAnnotation(AliasForWithoutMirroredAliasFor.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(allOf(startsWith("Attribute 'bar' in"),
|
||||
containsString(AliasForWithoutMirroredAliasFor.class.getName()),
|
||||
either(containsString("@AliasFor [foo]")).or(containsString("@AliasFor 'foo'"))));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasWithMirroredAliasForWrongAttribute() throws Exception {
|
||||
AliasForWithMirroredAliasForWrongAttribute annotation =
|
||||
AliasForWithMirroredAliasForWrongAttributeClass.class.getAnnotation(AliasForWithMirroredAliasForWrongAttribute.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Attribute 'bar' in"));
|
||||
exception.expectMessage(containsString(AliasForWithMirroredAliasForWrongAttribute.class.getName()));
|
||||
exception.expectMessage(anyOf(
|
||||
containsString("must be declared as an @AliasFor [foo], not [quux]"),
|
||||
containsString("is declared as an @AliasFor nonexistent attribute 'quux'"),
|
||||
containsString("must be declared as an @AliasFor 'foo', not attribute 'quux'")));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasForAttributeOfDifferentType() throws Exception {
|
||||
AliasForAttributeOfDifferentType annotation =
|
||||
AliasForAttributeOfDifferentTypeClass.class.getAnnotation(AliasForAttributeOfDifferentType.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Misconfigured aliases"));
|
||||
exception.expectMessage(containsString(AliasForAttributeOfDifferentType.class.getName()));
|
||||
exception.expectMessage(containsString("attribute 'foo'"));
|
||||
exception.expectMessage(containsString("attribute 'bar'"));
|
||||
exception.expectMessage(containsString("same return type"));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasForWithMissingDefaultValues() throws Exception {
|
||||
AliasForWithMissingDefaultValues annotation =
|
||||
AliasForWithMissingDefaultValuesClass.class.getAnnotation(AliasForWithMissingDefaultValues.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Misconfigured aliases"));
|
||||
exception.expectMessage(containsString(AliasForWithMissingDefaultValues.class.getName()));
|
||||
exception.expectMessage(containsString("attribute 'foo' in annotation"));
|
||||
exception.expectMessage(containsString("attribute 'bar' in annotation"));
|
||||
exception.expectMessage(containsString("default values"));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasForAttributeWithDifferentDefaultValue() throws Exception {
|
||||
AliasForAttributeWithDifferentDefaultValue annotation =
|
||||
AliasForAttributeWithDifferentDefaultValueClass.class.getAnnotation(AliasForAttributeWithDifferentDefaultValue.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("Misconfigured aliases"));
|
||||
exception.expectMessage(containsString(AliasForAttributeWithDifferentDefaultValue.class.getName()));
|
||||
exception.expectMessage(containsString("attribute 'foo' in annotation"));
|
||||
exception.expectMessage(containsString("attribute 'bar' in annotation"));
|
||||
exception.expectMessage(containsString("same default value"));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasForMetaAnnotationThatIsNotMetaPresent() throws Exception {
|
||||
AliasedComposedContextConfigNotMetaPresent annotation =
|
||||
AliasedComposedContextConfigNotMetaPresentClass.class.getAnnotation(AliasedComposedContextConfigNotMetaPresent.class);
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
exception.expectMessage(startsWith("@AliasFor declaration on attribute 'xmlConfigFile' in annotation"));
|
||||
exception.expectMessage(containsString(AliasedComposedContextConfigNotMetaPresent.class.getName()));
|
||||
exception.expectMessage(containsString("declares an alias for attribute 'location'"));
|
||||
exception.expectMessage(containsString(ContextConfig.class.getName()));
|
||||
exception.expectMessage(containsString("not meta-present"));
|
||||
InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliases() throws Exception {
|
||||
Method method = WebController.class.getMethod("handleMappedWithValueAttribute");
|
||||
WebMapping webMapping = method.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMapping);
|
||||
|
||||
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
|
||||
assertThat(synthesizedWebMapping1, instanceOf(SynthesizedAnnotation.class));
|
||||
assertNotSame(webMapping, synthesizedWebMapping1);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedWebMapping1.name());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping1.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping1.value());
|
||||
|
||||
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMapping);
|
||||
assertThat(synthesizedWebMapping2, instanceOf(SynthesizedAnnotation.class));
|
||||
assertNotSame(webMapping, synthesizedWebMapping2);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedWebMapping2.name());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping2.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping2.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithImplicitAliases() throws Exception {
|
||||
assertAnnotationSynthesisWithImplicitAliases(ValueImplicitAliasesContextConfigClass.class, "value");
|
||||
assertAnnotationSynthesisWithImplicitAliases(Location1ImplicitAliasesContextConfigClass.class, "location1");
|
||||
assertAnnotationSynthesisWithImplicitAliases(XmlImplicitAliasesContextConfigClass.class, "xmlFile");
|
||||
assertAnnotationSynthesisWithImplicitAliases(GroovyImplicitAliasesContextConfigClass.class, "groovyScript");
|
||||
}
|
||||
|
||||
private void assertAnnotationSynthesisWithImplicitAliases(Class<?> clazz, String expected) throws Exception {
|
||||
ImplicitAliasesContextConfig config = clazz.getAnnotation(ImplicitAliasesContextConfig.class);
|
||||
assertNotNull(config);
|
||||
|
||||
ImplicitAliasesContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
|
||||
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
|
||||
|
||||
assertEquals("value: ", expected, synthesizedConfig.value());
|
||||
assertEquals("location1: ", expected, synthesizedConfig.location1());
|
||||
assertEquals("xmlFile: ", expected, synthesizedConfig.xmlFile());
|
||||
assertEquals("groovyScript: ", expected, synthesizedConfig.groovyScript());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithImplicitAliasesWithImpliedAliasNamesOmitted() throws Exception {
|
||||
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
|
||||
ValueImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "value");
|
||||
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
|
||||
LocationsImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "location");
|
||||
assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
|
||||
XmlFilesImplicitAliasesWithImpliedAliasNamesOmittedContextConfigClass.class, "xmlFile");
|
||||
}
|
||||
|
||||
private void assertAnnotationSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
|
||||
Class<?> clazz, String expected) {
|
||||
|
||||
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig config = clazz.getAnnotation(
|
||||
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig.class);
|
||||
assertNotNull(config);
|
||||
|
||||
ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig synthesizedConfig = InternalAnnotationUtils.synthesizeAnnotation(config);
|
||||
assertThat(synthesizedConfig, instanceOf(SynthesizedAnnotation.class));
|
||||
|
||||
assertEquals("value: ", expected, synthesizedConfig.value());
|
||||
assertEquals("locations: ", expected, synthesizedConfig.location());
|
||||
assertEquals("xmlFiles: ", expected, synthesizedConfig.xmlFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithImplicitAliasesForAliasPair() throws Exception {
|
||||
Class<?> clazz = ImplicitAliasesForAliasPairContextConfigClass.class;
|
||||
ImplicitAliasesForAliasPairContextConfig config = clazz.getAnnotation(ImplicitAliasesForAliasPairContextConfig.class);
|
||||
assertNotNull(config);
|
||||
|
||||
ImplicitAliasesForAliasPairContextConfig synthesizedConfig = InternalAnnotationUtils.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 = InternalAnnotationUtils.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 = InternalAnnotationUtils.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;
|
||||
|
|
@ -1245,13 +869,6 @@ public class AnnotationUtilsTests {
|
|||
assertEquals("location: ", "", contextConfig.location());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasesWithDifferentValues() throws Exception {
|
||||
exception.expect(AnnotationConfigurationException.class);
|
||||
ContextConfig contextConfig = InternalAnnotationUtils.synthesizeAnnotation(ContextConfigMismatch.class.getAnnotation(ContextConfig.class));
|
||||
getValue(contextConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationFromMapWithMinimalAttributesWithAttributeAliases() throws Exception {
|
||||
Map<String, Object> map = Collections.singletonMap("location", "test.xml");
|
||||
|
|
@ -1325,16 +942,9 @@ public class AnnotationUtilsTests {
|
|||
public void synthesizeAnnotationFromMapWithAttributeOfIncorrectType() throws Exception {
|
||||
Map<String, Object> map = Collections.singletonMap(VALUE, 42L);
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(either(allOf(startsWith("Attributes map"),
|
||||
containsString("returned a value of type [java.lang.Long]"),
|
||||
containsString("for attribute 'value'"),
|
||||
containsString("but a value of type [java.lang.String] is required"),
|
||||
containsString("as defined by annotation type ["
|
||||
+ Component.class.getName() + "]"))).or(
|
||||
containsString("Attribute 'value' in annotation "
|
||||
+ Component.class.getName()
|
||||
+ " should be of type "
|
||||
+ "java.lang.String but a java.lang.Long value was returned")));
|
||||
exception.expectMessage(containsString(
|
||||
"Attribute 'value' in annotation org.springframework.stereotype.Component "
|
||||
+ "should be compatible with java.lang.String but a java.lang.Long value was returned"));
|
||||
synthesizeAnnotation(map, Component.class, null);
|
||||
}
|
||||
|
||||
|
|
@ -1359,210 +969,6 @@ public class AnnotationUtilsTests {
|
|||
assertEquals("value from synthesized component: ", "webController", synthesizedComponent.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringForSynthesizedAnnotations() throws Exception {
|
||||
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
|
||||
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithAliases);
|
||||
|
||||
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
|
||||
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithPathAndValue);
|
||||
|
||||
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping1);
|
||||
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping2);
|
||||
|
||||
assertThat(webMappingWithAliases.toString(), is(not(synthesizedWebMapping1.toString())));
|
||||
assertToStringForWebMappingWithPathAndValue(synthesizedWebMapping1);
|
||||
assertToStringForWebMappingWithPathAndValue(synthesizedWebMapping2);
|
||||
}
|
||||
|
||||
private void assertToStringForWebMappingWithPathAndValue(WebMapping webMapping) {
|
||||
String string = webMapping.toString();
|
||||
assertThat(string, startsWith("@" + WebMapping.class.getName() + "("));
|
||||
assertThat(string, containsString("value=[/test]"));
|
||||
assertThat(string, containsString("path=[/test]"));
|
||||
assertThat(string, containsString("name=bar"));
|
||||
assertThat(string, containsString("method="));
|
||||
assertThat(string, containsString("[GET, POST]"));
|
||||
assertThat(string, endsWith(")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsForSynthesizedAnnotations() throws Exception {
|
||||
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
|
||||
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithAliases);
|
||||
|
||||
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
|
||||
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithPathAndValue);
|
||||
|
||||
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping1);
|
||||
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping2);
|
||||
|
||||
// Equality amongst standard annotations
|
||||
assertThat(webMappingWithAliases, is(webMappingWithAliases));
|
||||
assertThat(webMappingWithPathAndValue, is(webMappingWithPathAndValue));
|
||||
|
||||
// Inequality amongst standard annotations
|
||||
assertThat(webMappingWithAliases, is(not(webMappingWithPathAndValue)));
|
||||
assertThat(webMappingWithPathAndValue, is(not(webMappingWithAliases)));
|
||||
|
||||
// Equality amongst synthesized annotations
|
||||
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping1));
|
||||
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping2));
|
||||
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping2));
|
||||
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping1));
|
||||
|
||||
// Equality between standard and synthesized annotations
|
||||
assertThat(synthesizedWebMapping1, is(webMappingWithPathAndValue));
|
||||
assertThat(webMappingWithPathAndValue, is(synthesizedWebMapping1));
|
||||
|
||||
// Inequality between standard and synthesized annotations
|
||||
assertThat(synthesizedWebMapping1, is(not(webMappingWithAliases)));
|
||||
assertThat(webMappingWithAliases, is(not(synthesizedWebMapping1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeForSynthesizedAnnotations() throws Exception {
|
||||
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
|
||||
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithAliases);
|
||||
|
||||
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
|
||||
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
|
||||
assertNotNull(webMappingWithPathAndValue);
|
||||
|
||||
WebMapping synthesizedWebMapping1 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping1);
|
||||
WebMapping synthesizedWebMapping2 = InternalAnnotationUtils.synthesizeAnnotation(webMappingWithAliases);
|
||||
assertNotNull(synthesizedWebMapping2);
|
||||
|
||||
// Equality amongst standard annotations
|
||||
assertThat(webMappingWithAliases.hashCode(), is(webMappingWithAliases.hashCode()));
|
||||
assertThat(webMappingWithPathAndValue.hashCode(), is(webMappingWithPathAndValue.hashCode()));
|
||||
|
||||
// Inequality amongst standard annotations
|
||||
assertThat(webMappingWithAliases.hashCode(), is(not(webMappingWithPathAndValue.hashCode())));
|
||||
assertThat(webMappingWithPathAndValue.hashCode(), is(not(webMappingWithAliases.hashCode())));
|
||||
|
||||
// Equality amongst synthesized annotations
|
||||
assertThat(synthesizedWebMapping1.hashCode(), is(synthesizedWebMapping1.hashCode()));
|
||||
assertThat(synthesizedWebMapping2.hashCode(), is(synthesizedWebMapping2.hashCode()));
|
||||
assertThat(synthesizedWebMapping1.hashCode(), is(synthesizedWebMapping2.hashCode()));
|
||||
assertThat(synthesizedWebMapping2.hashCode(), is(synthesizedWebMapping1.hashCode()));
|
||||
|
||||
// Equality between standard and synthesized annotations
|
||||
assertThat(synthesizedWebMapping1.hashCode(), is(webMappingWithPathAndValue.hashCode()));
|
||||
assertThat(webMappingWithPathAndValue.hashCode(), is(synthesizedWebMapping1.hashCode()));
|
||||
|
||||
// Inequality between standard and synthesized annotations
|
||||
assertThat(synthesizedWebMapping1.hashCode(), is(not(webMappingWithAliases.hashCode())));
|
||||
assertThat(webMappingWithAliases.hashCode(), is(not(synthesizedWebMapping1.hashCode())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully reflection-based test that verifies support for
|
||||
* {@linkplain AnnotationUtils#synthesizeAnnotation synthesizing annotations}
|
||||
* across packages with non-public visibility of user types (e.g., a non-public
|
||||
* annotation that uses {@code @AliasFor}).
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void synthesizeNonPublicAnnotationWithAttributeAliasesFromDifferentPackage() throws Exception {
|
||||
Class<?> clazz =
|
||||
ClassUtils.forName("org.springframework.core.annotation.subpackage.NonPublicAliasedAnnotatedClass", null);
|
||||
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
|
||||
ClassUtils.forName("org.springframework.core.annotation.subpackage.NonPublicAliasedAnnotation", null);
|
||||
|
||||
Annotation annotation = clazz.getAnnotation(annotationType);
|
||||
assertNotNull(annotation);
|
||||
Annotation synthesizedAnnotation = InternalAnnotationUtils.synthesizeAnnotation(annotation);
|
||||
assertNotSame(annotation, synthesizedAnnotation);
|
||||
|
||||
assertNotNull(synthesizedAnnotation);
|
||||
assertEquals("name attribute: ", "test", getValue(synthesizedAnnotation, "name"));
|
||||
assertEquals("aliased path attribute: ", "/test", getValue(synthesizedAnnotation, "path"));
|
||||
assertEquals("aliased path attribute: ", "/test", getValue(synthesizedAnnotation, "value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithAttributeAliasesInNestedAnnotations() throws Exception {
|
||||
List<String> expectedLocations = asList("A", "B");
|
||||
|
||||
Hierarchy hierarchy = ConfigHierarchyTestCase.class.getAnnotation(Hierarchy.class);
|
||||
assertNotNull(hierarchy);
|
||||
Hierarchy synthesizedHierarchy = InternalAnnotationUtils.synthesizeAnnotation(hierarchy);
|
||||
assertNotSame(hierarchy, synthesizedHierarchy);
|
||||
assertThat(synthesizedHierarchy, instanceOf(SynthesizedAnnotation.class));
|
||||
|
||||
ContextConfig[] configs = synthesizedHierarchy.value();
|
||||
assertNotNull(configs);
|
||||
assertTrue("nested annotations must be synthesized",
|
||||
stream(configs).allMatch(c -> c instanceof SynthesizedAnnotation));
|
||||
|
||||
List<String> locations = stream(configs).map(ContextConfig::location).collect(toList());
|
||||
assertThat(locations, is(expectedLocations));
|
||||
|
||||
List<String> values = stream(configs).map(ContextConfig::value).collect(toList());
|
||||
assertThat(values, is(expectedLocations));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithArrayOfAnnotations() throws Exception {
|
||||
List<String> expectedLocations = asList("A", "B");
|
||||
|
||||
Hierarchy hierarchy = ConfigHierarchyTestCase.class.getAnnotation(Hierarchy.class);
|
||||
assertNotNull(hierarchy);
|
||||
Hierarchy synthesizedHierarchy = InternalAnnotationUtils.synthesizeAnnotation(hierarchy);
|
||||
assertThat(synthesizedHierarchy, instanceOf(SynthesizedAnnotation.class));
|
||||
|
||||
ContextConfig contextConfig = SimpleConfigTestCase.class.getAnnotation(ContextConfig.class);
|
||||
assertNotNull(contextConfig);
|
||||
|
||||
ContextConfig[] configs = synthesizedHierarchy.value();
|
||||
List<String> locations = stream(configs).map(ContextConfig::location).collect(toList());
|
||||
assertThat(locations, is(expectedLocations));
|
||||
|
||||
// Alter array returned from synthesized annotation
|
||||
configs[0] = contextConfig;
|
||||
|
||||
// Re-retrieve the array from the synthesized annotation
|
||||
configs = synthesizedHierarchy.value();
|
||||
List<String> values = stream(configs).map(ContextConfig::value).collect(toList());
|
||||
assertThat(values, is(expectedLocations));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationWithArrayOfChars() throws Exception {
|
||||
CharsContainer charsContainer = GroupOfCharsClass.class.getAnnotation(CharsContainer.class);
|
||||
assertNotNull(charsContainer);
|
||||
CharsContainer synthesizedCharsContainer = InternalAnnotationUtils.synthesizeAnnotation(charsContainer);
|
||||
assertThat(synthesizedCharsContainer, instanceOf(SynthesizedAnnotation.class));
|
||||
|
||||
char[] chars = synthesizedCharsContainer.chars();
|
||||
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
|
||||
|
||||
// Alter array returned from synthesized annotation
|
||||
chars[0] = '?';
|
||||
|
||||
// Re-retrieve the array from the synthesized annotation
|
||||
chars = synthesizedCharsContainer.chars();
|
||||
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceWithAnnotatedMethods() {
|
||||
assertTrue(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(NonAnnotatedInterface.class).isEmpty());
|
||||
assertFalse(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(AnnotatedInterface.class).isEmpty());
|
||||
assertTrue(InternalAnnotationUtils.getAnnotatedMethodsInBaseType(NullableAnnotatedInterface.class).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@SafeVarargs
|
||||
static <T> T[] asArray(T... arr) {
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultAnnotationAttributeExtractor}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2.1
|
||||
*/
|
||||
public class DefaultAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
|
||||
|
||||
@Override
|
||||
protected AnnotationAttributeExtractor<?> createExtractorFor(Class<?> clazz, String expected, Class<? extends Annotation> annotationType) {
|
||||
return new DefaultAnnotationAttributeExtractor(clazz.getAnnotation(annotationType), clazz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtilsTests.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MapAnnotationAttributeExtractor}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.2.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
|
||||
|
||||
@Override
|
||||
protected AnnotationAttributeExtractor<?> createExtractorFor(Class<?> clazz, String expected, Class<? extends Annotation> annotationType) {
|
||||
Map<String, Object> attributes = Collections.singletonMap(expected, expected);
|
||||
return new MapAnnotationAttributeExtractor(attributes, annotationType, clazz);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void clearCacheBeforeTests() {
|
||||
AnnotationUtils.clearCache();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() throws Exception {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
|
||||
put("groovyScript", "");
|
||||
put("xmlFile", "");
|
||||
put("value", "");
|
||||
put("location1", "");
|
||||
put("location2", "");
|
||||
put("location3", "");
|
||||
put("nonAliasedAttribute", "");
|
||||
put("configClass", Object.class);
|
||||
}};
|
||||
|
||||
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enrichAndValidateAttributesWithImplicitAliases() throws Exception {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>() {{
|
||||
put("groovyScript", "groovy!");
|
||||
}};
|
||||
|
||||
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
|
||||
put("groovyScript", "groovy!");
|
||||
put("xmlFile", "groovy!");
|
||||
put("value", "groovy!");
|
||||
put("location1", "groovy!");
|
||||
put("location2", "groovy!");
|
||||
put("location3", "groovy!");
|
||||
put("nonAliasedAttribute", "");
|
||||
put("configClass", Object.class);
|
||||
}};
|
||||
|
||||
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>() {{
|
||||
// Intentionally storing 'value' as a single String instead of an array.
|
||||
// put("value", asArray("/foo"));
|
||||
put("value", "/foo");
|
||||
put("name", "test");
|
||||
}};
|
||||
|
||||
Map<String, Object> expected = new HashMap<String, Object>() {{
|
||||
put("value", asArray("/foo"));
|
||||
put("path", asArray("/foo"));
|
||||
put("name", "test");
|
||||
put("method", new RequestMethod[0]);
|
||||
}};
|
||||
|
||||
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(attributes, WebMapping.class, null);
|
||||
Map<String, Object> enriched = extractor.getSource();
|
||||
|
||||
assertEquals("attribute map size", expected.size(), enriched.size());
|
||||
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void assertEnrichAndValidateAttributes(Map<String, Object> sourceAttributes, Map<String, Object> expected) throws Exception {
|
||||
Class<? extends Annotation> annotationType = ImplicitAliasesContextConfig.class;
|
||||
|
||||
// Since the ordering of attribute methods returned by the JVM is non-deterministic,
|
||||
// we have to rig the attributeAliasesCache in AnnotationUtils so that the tests
|
||||
// consistently fail in case enrichAndValidateAttributes() is buggy.
|
||||
// Otherwise, these tests would intermittently pass even for an invalid implementation.
|
||||
Field cacheField = InternalAnnotationUtils.class.getDeclaredField("attributeAliasesCache");
|
||||
cacheField.setAccessible(true);
|
||||
Map<Class<? extends Annotation>, MultiValueMap<String, String>> attributeAliasesCache =
|
||||
(Map<Class<? extends Annotation>, MultiValueMap<String, String>>) cacheField.get(null);
|
||||
|
||||
// Declare aliases in an order that will cause enrichAndValidateAttributes() to
|
||||
// fail unless it considers all aliases in the set of implicit aliases.
|
||||
MultiValueMap<String, String> aliases = new LinkedMultiValueMap<>();
|
||||
aliases.put("xmlFile", Arrays.asList("value", "groovyScript", "location1", "location2", "location3"));
|
||||
aliases.put("groovyScript", Arrays.asList("value", "xmlFile", "location1", "location2", "location3"));
|
||||
aliases.put("value", Arrays.asList("xmlFile", "groovyScript", "location1", "location2", "location3"));
|
||||
aliases.put("location1", Arrays.asList("xmlFile", "groovyScript", "value", "location2", "location3"));
|
||||
aliases.put("location2", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location3"));
|
||||
aliases.put("location3", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location2"));
|
||||
|
||||
attributeAliasesCache.put(annotationType, aliases);
|
||||
|
||||
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(sourceAttributes, annotationType, null);
|
||||
Map<String, Object> enriched = extractor.getSource();
|
||||
|
||||
assertEquals("attribute map size", expected.size(), enriched.size());
|
||||
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue