Allow single element to override array in synthesized annotation
This commit picks up where 8ff9e818a5
left off.
Specifically, this commit introduces support that allows a single
element attribute to override an array attribute with a matching
component type when synthesizing annotations (e.g., in annotations
synthesized from attributes that have been merged from the annotation
hierarchy above a composed annotation).
Issue: SPR-13972
This commit is contained in:
parent
e086a5d58b
commit
eb654dc177
|
@ -18,6 +18,7 @@ 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;
|
||||
|
@ -132,8 +133,16 @@ class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttrib
|
|||
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?
|
||||
if (Annotation.class.isAssignableFrom(requiredReturnType) &&
|
||||
else if (Annotation.class.isAssignableFrom(requiredReturnType) &&
|
||||
Map.class.isAssignableFrom(actualReturnType)) {
|
||||
Class<? extends Annotation> nestedAnnotationType =
|
||||
(Class<? extends Annotation>) requiredReturnType;
|
||||
|
|
|
@ -33,8 +33,11 @@ import javax.annotation.Resource;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.ArrayComparisonFailure;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtilsTests.WebController;
|
||||
import org.springframework.core.annotation.AnnotationUtilsTests.WebMapping;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
@ -44,6 +47,7 @@ import static java.util.stream.Collectors.*;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotatedElementUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtilsTests.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotatedElementUtils}.
|
||||
|
@ -379,11 +383,21 @@ public class AnnotatedElementUtilsTests {
|
|||
assertGetMergedAnnotation(TransitiveImplicitAliasesContextConfigClass.class, "test.groovy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMergedAnnotationWithTransitiveImplicitAliasesWithSingleElementOverridingAnArrayViaAliasFor() {
|
||||
assertGetMergedAnnotation(SingleLocationTransitiveImplicitAliasesContextConfigClass.class, "test.groovy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMergedAnnotationWithTransitiveImplicitAliasesWithSkippedLevel() {
|
||||
assertGetMergedAnnotation(TransitiveImplicitAliasesWithSkippedLevelContextConfigClass.class, "test.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMergedAnnotationWithTransitiveImplicitAliasesWithSkippedLevelWithSingleElementOverridingAnArrayViaAliasFor() {
|
||||
assertGetMergedAnnotation(SingleLocationTransitiveImplicitAliasesWithSkippedLevelContextConfigClass.class, "test.xml");
|
||||
}
|
||||
|
||||
private void assertGetMergedAnnotation(Class<?> element, String... expected) {
|
||||
String name = ContextConfig.class.getName();
|
||||
ContextConfig contextConfig = getMergedAnnotation(element, ContextConfig.class);
|
||||
|
@ -560,6 +574,53 @@ public class AnnotatedElementUtilsTests {
|
|||
assertEquals("TX qualifier via synthesized annotation.", qualifier, annotation.qualifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesOnClassWithAttributeAliasInComposedAnnotationAndNestedAnnotationsInTargetAnnotation() {
|
||||
AnnotationAttributes attributes = assertComponentScanAttributes(TestComponentScanClass.class,
|
||||
"com.example.app.test");
|
||||
|
||||
Filter[] excludeFilters = attributes.getAnnotationArray("excludeFilters", Filter.class);
|
||||
assertNotNull(excludeFilters);
|
||||
|
||||
List<String> patterns = stream(excludeFilters).map(Filter::pattern).collect(toList());
|
||||
assertEquals(asList("*Test", "*Tests"), patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that {@link AnnotationUtils#postProcessAnnotationAttributes}
|
||||
* uses {@code ObjectUtils.nullSafeEquals()} to check for equality between annotation
|
||||
* attributes since attributes may be arrays.
|
||||
*/
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesOnClassWithBothAttributesOfAnAliasPairDeclared() {
|
||||
assertComponentScanAttributes(ComponentScanWithBasePackagesAndValueAliasClass.class, "com.example.app.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesWithSingleElementOverridingAnArrayViaConvention() {
|
||||
assertComponentScanAttributes(ConventionBasedSinglePackageComponentScanClass.class, "com.example.app.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesWithSingleElementOverridingAnArrayViaAliasFor() {
|
||||
assertComponentScanAttributes(AliasForBasedSinglePackageComponentScanClass.class, "com.example.app.test");
|
||||
}
|
||||
|
||||
private AnnotationAttributes assertComponentScanAttributes(Class<?> element, String... expected) {
|
||||
AnnotationAttributes attributes = findMergedAnnotationAttributes(element, ComponentScan.class);
|
||||
|
||||
assertNotNull("Should find @ComponentScan on " + element, attributes);
|
||||
assertArrayEquals("value: ", expected, attributes.getStringArray("value"));
|
||||
assertArrayEquals("basePackages: ", expected, attributes.getStringArray("basePackages"));
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
return AnnotatedElementUtils.findMergedAnnotationAttributes(element, annotationType.getName(), false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationWithAttributeAliasesInTargetAnnotation() {
|
||||
Class<?> element = AliasedTransactionalComponentClass.class;
|
||||
|
@ -593,38 +654,6 @@ public class AnnotatedElementUtilsTests {
|
|||
assertArrayEquals("value", propFiles, testPropSource.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesOnClassWithAttributeAliasInComposedAnnotationAndNestedAnnotationsInTargetAnnotation() {
|
||||
String[] expected = asArray("com.example.app.test");
|
||||
Class<?> element = TestComponentScanClass.class;
|
||||
AnnotationAttributes attributes = findMergedAnnotationAttributes(element, ComponentScan.class);
|
||||
|
||||
assertNotNull("Should find @ComponentScan on " + element, attributes);
|
||||
assertArrayEquals("basePackages for " + element, expected, attributes.getStringArray("basePackages"));
|
||||
|
||||
Filter[] excludeFilters = attributes.getAnnotationArray("excludeFilters", Filter.class);
|
||||
assertNotNull(excludeFilters);
|
||||
|
||||
List<String> patterns = stream(excludeFilters).map(Filter::pattern).collect(toList());
|
||||
assertEquals(asList("*Test", "*Tests"), patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that {@link AnnotationUtils#postProcessAnnotationAttributes}
|
||||
* uses {@code ObjectUtils.nullSafeEquals()} to check for equality between annotation
|
||||
* attributes since attributes may be arrays.
|
||||
*/
|
||||
@Test
|
||||
public void findMergedAnnotationAttributesOnClassWithBothAttributesOfAnAliasPairDeclared() {
|
||||
String[] expected = asArray("com.example.app.test");
|
||||
Class<?> element = ComponentScanWithBasePackagesAndValueAliasClass.class;
|
||||
AnnotationAttributes attributes = findMergedAnnotationAttributes(element, ComponentScan.class);
|
||||
|
||||
assertNotNull("Should find @ComponentScan on " + element, attributes);
|
||||
assertArrayEquals("value: ", expected, attributes.getStringArray("value"));
|
||||
assertArrayEquals("basePackages: ", expected, attributes.getStringArray("basePackages"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() {
|
||||
final String[] EMPTY = new String[0];
|
||||
|
@ -638,6 +667,24 @@ public class AnnotatedElementUtilsTests {
|
|||
assertArrayEquals("classes for " + element, new Class<?>[] {Number.class}, contextConfig.classes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationWithSingleElementOverridingAnArrayViaConvention() throws Exception {
|
||||
assertWebMapping(WebController.class.getMethod("postMappedWithPathAttribute"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMergedAnnotationWithSingleElementOverridingAnArrayViaAliasFor() throws Exception {
|
||||
assertWebMapping(WebController.class.getMethod("getMappedWithValueAttribute"));
|
||||
assertWebMapping(WebController.class.getMethod("getMappedWithPathAttribute"));
|
||||
}
|
||||
|
||||
private void assertWebMapping(AnnotatedElement element) throws ArrayComparisonFailure {
|
||||
WebMapping webMapping = findMergedAnnotation(element, WebMapping.class);
|
||||
assertNotNull(webMapping);
|
||||
assertArrayEquals("value attribute: ", asArray("/test"), webMapping.value());
|
||||
assertArrayEquals("path attribute: ", asArray("/test"), webMapping.path());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception {
|
||||
Constructor<?> deprecatedCtor = Date.class.getConstructor(String.class);
|
||||
|
@ -651,28 +698,10 @@ public class AnnotatedElementUtilsTests {
|
|||
assertEquals(SpringAppConfigClass.class.getAnnotation(Resource.class), findMergedAnnotation(SpringAppConfigClass.class, Resource.class));
|
||||
}
|
||||
|
||||
|
||||
private Set<String> names(Class<?>... classes) {
|
||||
return stream(classes).map(Class::getName).collect(toSet());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
// The following "varargs" suppression is necessary for javac from OpenJDK
|
||||
// (1.8.0_60-b27); however, Eclipse warns that it's unnecessary. See the following
|
||||
// Eclipse issues for details.
|
||||
//
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=344783
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=349669#c10
|
||||
// @SuppressWarnings("varargs")
|
||||
private static <T> T[] asArray(T... arr) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType) {
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
return AnnotatedElementUtils.findMergedAnnotationAttributes(element, annotationType.getName(), false, false);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
@ -881,6 +910,17 @@ public class AnnotatedElementUtilsTests {
|
|||
String[] groovy() default {};
|
||||
}
|
||||
|
||||
@ImplicitAliasesContextConfig
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface SingleLocationTransitiveImplicitAliasesContextConfig {
|
||||
|
||||
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "xmlFiles")
|
||||
String xml() default "";
|
||||
|
||||
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "groovyScripts")
|
||||
String groovy() default "";
|
||||
}
|
||||
|
||||
@ImplicitAliasesContextConfig
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TransitiveImplicitAliasesWithSkippedLevelContextConfig {
|
||||
|
@ -892,6 +932,17 @@ public class AnnotatedElementUtilsTests {
|
|||
String[] groovy() default {};
|
||||
}
|
||||
|
||||
@ImplicitAliasesContextConfig
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface SingleLocationTransitiveImplicitAliasesWithSkippedLevelContextConfig {
|
||||
|
||||
@AliasFor(annotation = ContextConfig.class, attribute = "locations")
|
||||
String xml() default "";
|
||||
|
||||
@AliasFor(annotation = ImplicitAliasesContextConfig.class, attribute = "groovyScripts")
|
||||
String groovy() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid because the configuration declares a value for 'value' and
|
||||
* requires a value for the aliased 'locations'. So we likely end up with
|
||||
|
@ -958,6 +1009,21 @@ public class AnnotatedElementUtilsTests {
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface TestComponentScan {
|
||||
|
||||
@AliasFor(attribute = "basePackages", annotation = ComponentScan.class)
|
||||
String[] packages();
|
||||
}
|
||||
|
||||
@ComponentScan
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ConventionBasedSinglePackageComponentScan {
|
||||
|
||||
String basePackages();
|
||||
}
|
||||
|
||||
@ComponentScan
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface AliasForBasedSinglePackageComponentScan {
|
||||
|
||||
@AliasFor(attribute = "basePackages", annotation = ComponentScan.class)
|
||||
String pkg();
|
||||
}
|
||||
|
@ -1132,10 +1198,18 @@ public class AnnotatedElementUtilsTests {
|
|||
static class TransitiveImplicitAliasesContextConfigClass {
|
||||
}
|
||||
|
||||
@SingleLocationTransitiveImplicitAliasesContextConfig(groovy = "test.groovy")
|
||||
static class SingleLocationTransitiveImplicitAliasesContextConfigClass {
|
||||
}
|
||||
|
||||
@TransitiveImplicitAliasesWithSkippedLevelContextConfig(xml = "test.xml")
|
||||
static class TransitiveImplicitAliasesWithSkippedLevelContextConfigClass {
|
||||
}
|
||||
|
||||
@SingleLocationTransitiveImplicitAliasesWithSkippedLevelContextConfig(xml = "test.xml")
|
||||
static class SingleLocationTransitiveImplicitAliasesWithSkippedLevelContextConfigClass {
|
||||
}
|
||||
|
||||
@ComposedImplicitAliasesContextConfig
|
||||
static class ComposedImplicitAliasesContextConfigClass {
|
||||
}
|
||||
|
@ -1152,10 +1226,18 @@ public class AnnotatedElementUtilsTests {
|
|||
static class ComponentScanWithBasePackagesAndValueAliasClass {
|
||||
}
|
||||
|
||||
@TestComponentScan(pkg = "com.example.app.test")
|
||||
@TestComponentScan(packages = "com.example.app.test")
|
||||
static class TestComponentScanClass {
|
||||
}
|
||||
|
||||
@ConventionBasedSinglePackageComponentScan(basePackages = "com.example.app.test")
|
||||
static class ConventionBasedSinglePackageComponentScanClass {
|
||||
}
|
||||
|
||||
@AliasForBasedSinglePackageComponentScan(pkg = "com.example.app.test")
|
||||
static class AliasForBasedSinglePackageComponentScanClass {
|
||||
}
|
||||
|
||||
@SpringAppConfig(Number.class)
|
||||
static class SpringAppConfigClass {
|
||||
}
|
||||
|
|
|
@ -465,8 +465,8 @@ public class AnnotationUtilsTests {
|
|||
assertNotNull(attributes);
|
||||
assertEquals(WebMapping.class, attributes.annotationType());
|
||||
assertEquals("name attribute: ", "foo", attributes.getString("name"));
|
||||
assertEquals("value attribute: ", "/test", attributes.getString(VALUE));
|
||||
assertEquals("path attribute: ", "/test", attributes.getString("path"));
|
||||
assertArrayEquals("value attribute: ", asArray("/test"), attributes.getStringArray(VALUE));
|
||||
assertArrayEquals("path attribute: ", asArray("/test"), attributes.getStringArray("path"));
|
||||
|
||||
method = WebController.class.getMethod("handleMappedWithPathAttribute");
|
||||
webMapping = method.getAnnotation(WebMapping.class);
|
||||
|
@ -474,15 +474,18 @@ public class AnnotationUtilsTests {
|
|||
assertNotNull(attributes);
|
||||
assertEquals(WebMapping.class, attributes.annotationType());
|
||||
assertEquals("name attribute: ", "bar", attributes.getString("name"));
|
||||
assertEquals("value attribute: ", "/test", attributes.getString(VALUE));
|
||||
assertEquals("path attribute: ", "/test", attributes.getString("path"));
|
||||
assertArrayEquals("value attribute: ", asArray("/test"), attributes.getStringArray(VALUE));
|
||||
assertArrayEquals("path attribute: ", asArray("/test"), attributes.getStringArray("path"));
|
||||
}
|
||||
|
||||
@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("values of [{/enigma}] and [{/test}]"));
|
||||
|
||||
method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes");
|
||||
webMapping = method.getAnnotation(WebMapping.class);
|
||||
Method method = WebController.class.getMethod("handleMappedWithDifferentPathAndValueAttributes");
|
||||
WebMapping webMapping = method.getAnnotation(WebMapping.class);
|
||||
getAnnotationAttributes(webMapping);
|
||||
}
|
||||
|
||||
|
@ -835,8 +838,8 @@ public class AnnotationUtilsTests {
|
|||
assertSame(synthesizedWebMapping, synthesizedAgainWebMapping);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedAgainWebMapping.name());
|
||||
assertEquals("aliased path attribute: ", "/test", synthesizedAgainWebMapping.path());
|
||||
assertEquals("actual value attribute: ", "/test", synthesizedAgainWebMapping.value());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedAgainWebMapping.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedAgainWebMapping.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -956,16 +959,16 @@ public class AnnotationUtilsTests {
|
|||
assertNotSame(webMapping, synthesizedWebMapping1);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedWebMapping1.name());
|
||||
assertEquals("aliased path attribute: ", "/test", synthesizedWebMapping1.path());
|
||||
assertEquals("actual value attribute: ", "/test", synthesizedWebMapping1.value());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping1.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping1.value());
|
||||
|
||||
WebMapping synthesizedWebMapping2 = synthesizeAnnotation(webMapping);
|
||||
assertThat(synthesizedWebMapping2, instanceOf(SynthesizedAnnotation.class));
|
||||
assertNotSame(webMapping, synthesizedWebMapping2);
|
||||
|
||||
assertEquals("name attribute: ", "foo", synthesizedWebMapping2.name());
|
||||
assertEquals("aliased path attribute: ", "/test", synthesizedWebMapping2.path());
|
||||
assertEquals("actual value attribute: ", "/test", synthesizedWebMapping2.value());
|
||||
assertArrayEquals("aliased path attribute: ", asArray("/test"), synthesizedWebMapping2.path());
|
||||
assertArrayEquals("actual value attribute: ", asArray("/test"), synthesizedWebMapping2.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1182,6 +1185,21 @@ public class AnnotationUtilsTests {
|
|||
assertEquals("location: ", "test.xml", contextConfig.location());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements() throws Exception {
|
||||
Map<String, Object> map = Collections.singletonMap("value", "/foo");
|
||||
Get get = synthesizeAnnotation(map, Get.class, null);
|
||||
assertNotNull(get);
|
||||
assertEquals("value: ", "/foo", get.value());
|
||||
assertEquals("path: ", "/foo", get.path());
|
||||
|
||||
map = Collections.singletonMap("path", "/foo");
|
||||
get = synthesizeAnnotation(map, Get.class, null);
|
||||
assertNotNull(get);
|
||||
assertEquals("value: ", "/foo", get.value());
|
||||
assertEquals("path: ", "/foo", get.path());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void synthesizeAnnotationFromMapWithImplicitAttributeAliases() throws Exception {
|
||||
assertAnnotationSynthesisFromMapWithImplicitAliases("value");
|
||||
|
@ -1287,8 +1305,8 @@ public class AnnotationUtilsTests {
|
|||
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("value=[/test]"));
|
||||
assertThat(string, containsString("path=[/test]"));
|
||||
assertThat(string, containsString("name=bar"));
|
||||
assertThat(string, containsString("method="));
|
||||
assertThat(string, containsString("[GET, POST]"));
|
||||
|
@ -1461,6 +1479,18 @@ public class AnnotationUtilsTests {
|
|||
assertArrayEquals(new char[] { 'x', 'y', 'z' }, chars);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
// The following "varargs" suppression is necessary for javac from OpenJDK
|
||||
// (1.8.0_60-b27); however, Eclipse warns that it's unnecessary. See the following
|
||||
// Eclipse issues for details.
|
||||
//
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=344783
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=349669#c10
|
||||
// @SuppressWarnings("varargs")
|
||||
static <T> T[] asArray(T... arr) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
@Component("meta1")
|
||||
@Order
|
||||
|
@ -1768,14 +1798,40 @@ public class AnnotationUtilsTests {
|
|||
String name();
|
||||
|
||||
@AliasFor("path")
|
||||
String value() default "";
|
||||
String[] value() default "";
|
||||
|
||||
@AliasFor(attribute = "value")
|
||||
String path() default "";
|
||||
String[] path() default "";
|
||||
|
||||
RequestMethod[] method() default {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock of {@code org.springframework.web.bind.annotation.GetMapping}, except
|
||||
* that the String arrays are overridden with single String elements.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@WebMapping(method = RequestMethod.GET, name = "")
|
||||
@interface Get {
|
||||
|
||||
@AliasFor(annotation = WebMapping.class)
|
||||
String value() default "";
|
||||
|
||||
@AliasFor(annotation = WebMapping.class)
|
||||
String path() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock of {@code org.springframework.web.bind.annotation.PostMapping}, except
|
||||
* that the path is overridden by convention with single String element.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@WebMapping(method = RequestMethod.POST, name = "")
|
||||
@interface Post {
|
||||
|
||||
String path() default "";
|
||||
}
|
||||
|
||||
@Component("webController")
|
||||
static class WebController {
|
||||
|
||||
|
@ -1787,6 +1843,18 @@ public class AnnotationUtilsTests {
|
|||
public void handleMappedWithPathAttribute() {
|
||||
}
|
||||
|
||||
@Get("/test")
|
||||
public void getMappedWithValueAttribute() {
|
||||
}
|
||||
|
||||
@Get(path = "/test")
|
||||
public void getMappedWithPathAttribute() {
|
||||
}
|
||||
|
||||
@Post(path = "/test")
|
||||
public void postMappedWithPathAttribute() {
|
||||
}
|
||||
|
||||
/**
|
||||
* mapping is logically "equal" to handleMappedWithPathAttribute().
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
|
@ -26,11 +26,14 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig;
|
||||
import org.springframework.core.annotation.AnnotationUtilsTests.RequestMethod;
|
||||
import org.springframework.core.annotation.AnnotationUtilsTests.WebMapping;
|
||||
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}.
|
||||
|
@ -38,6 +41,7 @@ import static org.junit.Assert.*;
|
|||
* @author Sam Brannen
|
||||
* @since 4.2.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
|
||||
|
||||
@Before
|
||||
|
@ -46,7 +50,6 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
|
||||
|
@ -64,7 +67,6 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("serial")
|
||||
public void enrichAndValidateAttributesWithImplicitAliases() {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>() {{
|
||||
put("groovyScript", "groovy!");
|
||||
|
@ -84,6 +86,31 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
|
|||
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() {
|
||||
// @formatter:off
|
||||
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]);
|
||||
}};
|
||||
// @formatter:on
|
||||
|
||||
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) {
|
||||
Class<? extends Annotation> annotationType = ImplicitAliasesContextConfig.class;
|
||||
|
@ -114,8 +141,7 @@ public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnno
|
|||
Map<String, Object> enriched = extractor.getSource();
|
||||
|
||||
assertEquals("attribute map size", expected.size(), enriched.size());
|
||||
expected.keySet().stream().forEach( attr ->
|
||||
assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expected.get(attr))));
|
||||
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue