Skip to content

Commit e905f17

Browse files
committed
Refine MergedAnnotation.asMap
Add a convenience method that allows a `MergedAnnotation` to be converted into an `AnnotationAttributes` instance. Also rename the `MapValues` enum to `Adapt` which generally seems to read better. Closes gh-22738
1 parent ef151f5 commit e905f17

File tree

10 files changed

+105
-68
lines changed

10 files changed

+105
-68
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AbstractMergedAnnotation.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ public MergedAnnotation<A> filterDefaultValues() {
166166
return filterAttributes(this::hasNonDefaultValue);
167167
}
168168

169+
@Override
170+
public AnnotationAttributes asAnnotationAttributes(Adapt... adaptations) {
171+
return asMap(mergedAnnotation -> new AnnotationAttributes(getType()), adaptations);
172+
}
173+
169174
@Override
170175
public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition)
171176
throws NoSuchElementException {

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import java.util.stream.Collectors;
2626

2727
import org.springframework.core.BridgeMethodResolver;
28-
import org.springframework.core.annotation.MergedAnnotation.MapValues;
28+
import org.springframework.core.annotation.MergedAnnotation.Adapt;
2929
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
3030
import org.springframework.lang.Nullable;
3131
import org.springframework.util.MultiValueMap;
@@ -500,11 +500,11 @@ public static MultiValueMap<String, Object> getAllAnnotationAttributes(
500500
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
501501
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
502502

503-
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
503+
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
504504
return getAnnotations(element).stream(annotationName)
505505
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
506506
.map(MergedAnnotation::withNonMergedAttributes)
507-
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, mapValues));
507+
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
508508
}
509509

510510
/**
@@ -799,8 +799,8 @@ private static AnnotationAttributes getAnnotationAttributes(MergedAnnotation<?>
799799
if (!annotation.isPresent()) {
800800
return null;
801801
}
802-
return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()),
803-
MapValues.of(classValuesAsString, nestedAnnotationsAsMap));
802+
return annotation.asAnnotationAttributes(
803+
Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
804804
}
805805

806806

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@
2929
import java.util.Map;
3030
import java.util.NoSuchElementException;
3131
import java.util.Set;
32-
import java.util.function.Function;
3332

3433
import org.springframework.core.BridgeMethodResolver;
3534
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
36-
import org.springframework.core.annotation.MergedAnnotation.MapValues;
35+
import org.springframework.core.annotation.MergedAnnotation.Adapt;
3736
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
3837
import org.springframework.lang.Nullable;
3938
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -857,14 +856,11 @@ public static AnnotationAttributes getAnnotationAttributes(
857856
@Nullable AnnotatedElement annotatedElement, Annotation annotation,
858857
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
859858

860-
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap);
859+
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
861860
return MergedAnnotation.from(annotatedElement, annotation)
862861
.withNonMergedAttributes()
863-
.asMap(getAnnotationAttributesFactory(), mapValues);
864-
}
865-
866-
private static Function<MergedAnnotation<?>, AnnotationAttributes> getAnnotationAttributesFactory() {
867-
return (annotation -> new AnnotationAttributes(annotation.getType(), true));
862+
.asMap(mergedAnnotation ->
863+
new AnnotationAttributes(mergedAnnotation.getType(), true), adaptations);
868864
}
869865

870866
/**
@@ -910,7 +906,8 @@ private static Map<String, DefaultValueHolder> computeDefaultValues(
910906
else {
911907
// If we have nested annotations, we need them as nested maps
912908
AnnotationAttributes attributes = MergedAnnotation.from(annotationType)
913-
.asMap(getAnnotationAttributesFactory(), MapValues.ANNOTATION_TO_MAP);
909+
.asMap(annotation ->
910+
new AnnotationAttributes(annotation.getType(), true), Adapt.ANNOTATION_TO_MAP);
914911
for (Map.Entry<String, Object> element : attributes.entrySet()) {
915912
result.put(element.getKey(), new DefaultValueHolder(element.getValue()));
916913
}

spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -422,21 +422,32 @@ <T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attribute
422422
MergedAnnotation<A> withNonMergedAttributes();
423423

424424
/**
425-
* Create an immutable {@link Map} that contains all the annotation attributes.
426-
* <p>The {@link MapValues options} may be used to change the way that values are added.
427-
* @param options map value options
425+
* Create a new mutable {@link AnnotationAttributes} instance from this
426+
* merged annotation.
427+
* <p>The {@link Adapt adaptations} may be used to change the way that values
428+
* are added.
429+
* @param adaptations adaptations that should be applied to the annotation values
428430
* @return an immutable map containing the attributes and values
429431
*/
430-
Map<String, Object> asMap(MapValues... options);
432+
AnnotationAttributes asAnnotationAttributes(Adapt... adaptations);
431433

432434
/**
433-
* Create a {@link Map} of the given type that contains all the annotation attributes.
434-
* <p>The {@link MapValues options} may be used to change the way that values are added.
435+
* Return an immutable {@link Map} that contains all the annotation attributes.
436+
* <p>The {@link Adapt adaptations} may be used to change the way that values are added.
437+
* @param adaptations adaptations that should be applied to the annotation values
438+
* @return an immutable map containing the attributes and values
439+
*/
440+
Map<String, Object> asMap(Adapt... adaptations);
441+
442+
/**
443+
* Create a new {@link Map} instance of the given type that contains all the annotation
444+
* attributes.
445+
* <p>The {@link Adapt adaptations} may be used to change the way that values are added.
435446
* @param factory a map factory
436-
* @param options map value options
447+
* @param adaptations adaptations that should be applied to the annotation values
437448
* @return a map containing the attributes and values
438449
*/
439-
<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options);
450+
<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations);
440451

441452
/**
442453
* Create a type-safe synthesized version of this annotation that can be
@@ -539,24 +550,25 @@ static <A extends Annotation> MergedAnnotation<A> from(
539550

540551

541552
/**
542-
* Options that effect the way map values are
543-
* {@linkplain MergedAnnotation#asMap(MapValues...) converted}.
553+
* Adaptations that can be applied to attributes values when creating
554+
* {@linkplain MergedAnnotation#asMap(Adapt...) Maps} or
555+
* {@link MergedAnnotation#asAnnotationAttributes(Adapt...) AnnotationAttributes}.
544556
*/
545-
enum MapValues {
557+
enum Adapt {
546558

547559
/**
548-
* Add class or class array attributes as strings.
560+
* Adapt class or class array attributes to strings.
549561
*/
550562
CLASS_TO_STRING,
551563

552564
/**
553-
* Convert any nested annotation or annotation arrays to maps rather
565+
* Adapt nested annotation or annotation arrays to maps rather
554566
* than synthesizing the values.
555567
*/
556568
ANNOTATION_TO_MAP;
557569

558-
protected final boolean isIn(MapValues... options) {
559-
for (MapValues candidate : options) {
570+
protected final boolean isIn(Adapt... adaptations) {
571+
for (Adapt candidate : adaptations) {
560572
if (candidate == this) {
561573
return true;
562574
}
@@ -565,16 +577,16 @@ protected final boolean isIn(MapValues... options) {
565577
}
566578

567579
/**
568-
* Factory method to create a {@link MapValues} array from a set of boolean flags.
569-
* @param classToString if {@link MapValues#CLASS_TO_STRING} is included
570-
* @param annotationsToMap if {@link MapValues#ANNOTATION_TO_MAP} is included
571-
* @return a new {@link MapValues} array
580+
* Factory method to create a {@link Adapt} array from a set of boolean flags.
581+
* @param classToString if {@link Adapt#CLASS_TO_STRING} is included
582+
* @param annotationsToMap if {@link Adapt#ANNOTATION_TO_MAP} is included
583+
* @return a new {@link Adapt} array
572584
*/
573-
public static MapValues[] of(boolean classToString, boolean annotationsToMap) {
574-
EnumSet<MapValues> result = EnumSet.noneOf(MapValues.class);
575-
addIfTrue(result, MapValues.CLASS_TO_STRING, classToString);
576-
addIfTrue(result, MapValues.ANNOTATION_TO_MAP, annotationsToMap);
577-
return result.toArray(new MapValues[0]);
585+
public static Adapt[] values(boolean classToString, boolean annotationsToMap) {
586+
EnumSet<Adapt> result = EnumSet.noneOf(Adapt.class);
587+
addIfTrue(result, Adapt.CLASS_TO_STRING, classToString);
588+
addIfTrue(result, Adapt.ANNOTATION_TO_MAP, annotationsToMap);
589+
return result.toArray(new Adapt[0]);
578590
}
579591

580592
private static <T> void addIfTrue(Set<T> result, T value, boolean test) {

spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotationCollectors.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import java.util.stream.Collector;
2727
import java.util.stream.Collector.Characteristics;
2828

29-
import org.springframework.core.annotation.MergedAnnotation.MapValues;
29+
import org.springframework.core.annotation.MergedAnnotation.Adapt;
3030
import org.springframework.util.LinkedMultiValueMap;
3131
import org.springframework.util.MultiValueMap;
3232

@@ -97,39 +97,39 @@ private MergedAnnotationCollectors() {
9797
* Create a new {@link Collector} that accumulates merged annotations to an
9898
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
9999
* added} from each merged annotation
100-
* {@link MergedAnnotation#asMap(MapValues...) as a map}.
100+
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
101101
* @param <A> the annotation type
102-
* @param options the map conversion options
102+
* @param adaptations adaptations that should be applied to the annotation values
103103
* @return a {@link Collector} which collects and synthesizes the
104104
* annotations into a {@link LinkedMultiValueMap}
105-
* @see #toMultiValueMap(Function, MergedAnnotation.MapValues...)
105+
* @see #toMultiValueMap(Function, MergedAnnotation.Adapt...)
106106
*/
107107
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
108-
MapValues... options) {
108+
Adapt... adaptations) {
109109

110-
return toMultiValueMap(Function.identity(), options);
110+
return toMultiValueMap(Function.identity(), adaptations);
111111
}
112112

113113
/**
114114
* Create a new {@link Collector} that accumulates merged annotations to an
115115
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
116116
* added} from each merged annotation
117-
* {@link MergedAnnotation#asMap(MapValues...) as a map}.
117+
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
118118
* @param <A> the annotation type
119-
* @param options the map conversion options
119+
* @param adaptations adaptations that should be applied to the annotation values
120120
* @param finisher the finisher function for the new {@link MultiValueMap}
121121
* @return a {@link Collector} which collects and synthesizes the
122122
* annotations into a {@link LinkedMultiValueMap}
123-
* @see #toMultiValueMap(MergedAnnotation.MapValues...)
123+
* @see #toMultiValueMap(MergedAnnotation.Adapt...)
124124
*/
125125
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
126126
Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher,
127-
MapValues... options) {
127+
Adapt... adaptations) {
128128

129129
Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ?
130130
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
131131
return Collector.of(LinkedMultiValueMap::new,
132-
(map, annotation) -> annotation.asMap(options).forEach(map::add),
132+
(map, annotation) -> annotation.asMap(adaptations).forEach(map::add),
133133
MergedAnnotationCollectors::merge, finisher, characteristics);
134134
}
135135

spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,17 @@ public MergedAnnotation<A> withNonMergedAttributes() {
108108
}
109109

110110
@Override
111-
public Map<String, Object> asMap(MapValues... options) {
111+
public AnnotationAttributes asAnnotationAttributes(Adapt... adaptations) {
112+
return new AnnotationAttributes();
113+
}
114+
115+
@Override
116+
public Map<String, Object> asMap(Adapt... adaptations) {
112117
return Collections.emptyMap();
113118
}
114119

115120
@Override
116-
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) {
121+
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations) {
117122
return factory.apply(this);
118123
}
119124

spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -233,50 +233,50 @@ public MergedAnnotation<A> withNonMergedAttributes() {
233233
}
234234

235235
@Override
236-
public Map<String, Object> asMap(MapValues... options) {
237-
return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), options));
236+
public Map<String, Object> asMap(Adapt... adaptations) {
237+
return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), adaptations));
238238
}
239239

240240
@Override
241-
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) {
241+
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations) {
242242
T map = factory.apply(this);
243243
Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null");
244244
AttributeMethods attributes = this.mapping.getAttributes();
245245
for (int i = 0; i < attributes.size(); i++) {
246246
Method attribute = attributes.get(i);
247247
Object value = (isFiltered(attribute.getName()) ? null :
248-
getValue(i, getTypeForMapOptions(attribute, options)));
248+
getValue(i, getTypeForMapOptions(attribute, adaptations)));
249249
if (value != null) {
250250
map.put(attribute.getName(),
251-
adaptValueForMapOptions(attribute, value, map.getClass(), factory, options));
251+
adaptValueForMapOptions(attribute, value, map.getClass(), factory, adaptations));
252252
}
253253
}
254254
return map;
255255
}
256256

257-
private Class<?> getTypeForMapOptions(Method attribute, MapValues[] options) {
257+
private Class<?> getTypeForMapOptions(Method attribute, Adapt[] adaptations) {
258258
Class<?> attributeType = attribute.getReturnType();
259259
Class<?> componentType = (attributeType.isArray() ? attributeType.getComponentType() : attributeType);
260-
if (MapValues.CLASS_TO_STRING.isIn(options) && componentType == Class.class) {
260+
if (Adapt.CLASS_TO_STRING.isIn(adaptations) && componentType == Class.class) {
261261
return (attributeType.isArray() ? String[].class : String.class);
262262
}
263263
return Object.class;
264264
}
265265

266266
private <T extends Map<String, Object>> Object adaptValueForMapOptions(Method attribute, Object value,
267-
Class<?> mapType, Function<MergedAnnotation<?>, T> factory, MapValues[] options) {
267+
Class<?> mapType, Function<MergedAnnotation<?>, T> factory, Adapt[] adaptations) {
268268

269269
if (value instanceof MergedAnnotation) {
270270
MergedAnnotation<?> annotation = (MergedAnnotation<?>) value;
271-
return (MapValues.ANNOTATION_TO_MAP.isIn(options) ?
272-
annotation.asMap(factory, options) : annotation.synthesize());
271+
return (Adapt.ANNOTATION_TO_MAP.isIn(adaptations) ?
272+
annotation.asMap(factory, adaptations) : annotation.synthesize());
273273
}
274274
if (value instanceof MergedAnnotation[]) {
275275
MergedAnnotation<?>[] annotations = (MergedAnnotation<?>[]) value;
276-
if (MapValues.ANNOTATION_TO_MAP.isIn(options)) {
276+
if (Adapt.ANNOTATION_TO_MAP.isIn(adaptations)) {
277277
Object result = Array.newInstance(mapType, annotations.length);
278278
for (int i = 0; i < annotations.length; i++) {
279-
Array.set(result, i, annotations[i].asMap(factory, options));
279+
Array.set(result, i, annotations[i].asMap(factory, adaptations));
280280
}
281281
return result;
282282
}

spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationCollectorsTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
import org.junit.Test;
2929

30-
import org.springframework.core.annotation.MergedAnnotation.MapValues;
30+
import org.springframework.core.annotation.MergedAnnotation.Adapt;
3131
import org.springframework.util.MultiValueMap;
3232

3333
import static org.assertj.core.api.Assertions.*;
@@ -72,7 +72,7 @@ public void toMultiValueMapCollectsMultiValueMap() {
7272
MultiValueMap<String, Object> map = stream().map(
7373
MergedAnnotation::filterDefaultValues).collect(
7474
MergedAnnotationCollectors.toMultiValueMap(
75-
MapValues.CLASS_TO_STRING));
75+
Adapt.CLASS_TO_STRING));
7676
assertThat(map.get("value")).containsExactly("a", "b", "c");
7777
assertThat(map.get("extra")).containsExactly("java.lang.String",
7878
"java.lang.Integer");

spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import org.junit.internal.ArrayComparisonFailure;
4141

4242
import org.springframework.core.Ordered;
43-
import org.springframework.core.annotation.MergedAnnotation.MapValues;
43+
import org.springframework.core.annotation.MergedAnnotation.Adapt;
4444
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
4545
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
4646
import org.springframework.lang.Nullable;
@@ -1665,7 +1665,7 @@ public void synthesizeFromMapWithNestedMap() throws Exception {
16651665
assertThat(componentScan.value().pattern()).isEqualTo("*Foo");
16661666
Map<String, Object> map = MergedAnnotation.from(componentScan).asMap(
16671667
annotation -> new LinkedHashMap<String, Object>(),
1668-
MapValues.ANNOTATION_TO_MAP);
1668+
Adapt.ANNOTATION_TO_MAP);
16691669
Map<String, Object> filterMap = (Map<String, Object>) map.get("value");
16701670
assertThat(filterMap.get("pattern")).isEqualTo("*Foo");
16711671
filterMap.put("pattern", "newFoo");
@@ -1685,7 +1685,7 @@ public void synthesizeFromMapWithNestedArrayOfMaps() throws Exception {
16851685
assertThat(componentScan).isNotNull();
16861686
Map<String, Object> map = MergedAnnotation.from(componentScan).asMap(
16871687
annotation -> new LinkedHashMap<String, Object>(),
1688-
MapValues.ANNOTATION_TO_MAP);
1688+
Adapt.ANNOTATION_TO_MAP);
16891689
Map<String, Object>[] filters = (Map[]) map.get("excludeFilters");
16901690
List<String> patterns = Arrays.stream(filters).map(
16911691
m -> (String) m.get("pattern")).collect(Collectors.toList());
@@ -2053,6 +2053,17 @@ public void getValueWhenThreeDeepMetaWithValue() {
20532053
"FromValueAttributeMeta");
20542054
}
20552055

2056+
@Test
2057+
public void asAnnotationAttributesReturnsPopulatedAnnotationAttributes() {
2058+
MergedAnnotation<?> annotation = MergedAnnotations.from(
2059+
SpringApplicationConfigurationClass.class).get(
2060+
SpringApplicationConfiguration.class);
2061+
AnnotationAttributes attributes = annotation.asAnnotationAttributes(
2062+
Adapt.CLASS_TO_STRING);
2063+
assertThat(attributes).containsEntry("classes", new String[] { Number.class.getName() });
2064+
assertThat(attributes.annotationType()).isEqualTo(SpringApplicationConfiguration.class);
2065+
}
2066+
20562067
// @formatter:off
20572068

20582069
@Retention(RetentionPolicy.RUNTIME)

0 commit comments

Comments
 (0)