diff --git a/spring-core/src/main/java/org/springframework/core/Conventions.java b/spring-core/src/main/java/org/springframework/core/Conventions.java index 83b72918130..a1b3c62315e 100644 --- a/spring-core/src/main/java/org/springframework/core/Conventions.java +++ b/spring-core/src/main/java/org/springframework/core/Conventions.java @@ -56,6 +56,9 @@ public abstract class Conventions { Serializable.class, Externalizable.class, Cloneable.class, Comparable.class))); } + private static final ReactiveAdapterRegistry reactiveAdapterRegistry = + new ReactiveAdapterRegistry(); + /** * Determine the conventional variable name for the supplied {@code Object} @@ -112,6 +115,7 @@ public abstract class Conventions { Assert.notNull(parameter, "MethodParameter must not be null"); Class valueClass; boolean pluralize = false; + String reactiveSuffix = ""; if (parameter.getParameterType().isArray()) { valueClass = parameter.getParameterType().getComponentType(); @@ -127,10 +131,16 @@ public abstract class Conventions { } else { valueClass = parameter.getParameterType(); + + ReactiveAdapter adapter = reactiveAdapterRegistry.getAdapter(valueClass); + if (adapter != null && !adapter.getDescriptor().isNoValue()) { + reactiveSuffix = ClassUtils.getShortName(valueClass); + valueClass = parameter.nested().getNestedParameterType(); + } } String name = ClassUtils.getShortNameAsProperty(valueClass); - return (pluralize ? pluralize(name) : name); + return (pluralize ? pluralize(name) : name + reactiveSuffix); } /** @@ -179,6 +189,7 @@ public abstract class Conventions { Class valueClass; boolean pluralize = false; + String reactiveSuffix = ""; if (resolvedType.isArray()) { valueClass = resolvedType.getComponentType(); @@ -203,10 +214,16 @@ public abstract class Conventions { } else { valueClass = resolvedType; + + ReactiveAdapter adapter = reactiveAdapterRegistry.getAdapter(valueClass); + if (adapter != null && !adapter.getDescriptor().isNoValue()) { + reactiveSuffix = ClassUtils.getShortName(valueClass); + valueClass = ResolvableType.forMethodReturnType(method).getGeneric(0).resolve(); + } } String name = ClassUtils.getShortNameAsProperty(valueClass); - return (pluralize ? pluralize(name) : name); + return (pluralize ? pluralize(name) : name + reactiveSuffix); } /** diff --git a/spring-core/src/test/java/org/springframework/core/ConventionsTests.java b/spring-core/src/test/java/org/springframework/core/ConventionsTests.java index a22aef86d18..1496b7ba5cf 100644 --- a/spring-core/src/test/java/org/springframework/core/ConventionsTests.java +++ b/spring-core/src/test/java/org/springframework/core/ConventionsTests.java @@ -16,14 +16,23 @@ package org.springframework.core; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import java.util.Set; +import io.reactivex.Observable; +import io.reactivex.Single; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.tests.sample.objects.TestObject; +import org.springframework.util.ClassUtils; import static org.junit.Assert.assertEquals; @@ -41,8 +50,15 @@ public class ConventionsTests { @Test public void simpleObject() { + assertEquals("Incorrect singular variable name", "testObject", Conventions.getVariableName(new TestObject())); + + assertEquals("Incorrect singular variable name", "testObject", + Conventions.getVariableNameForParameter(getMethodParameter(TestObject.class))); + + assertEquals("Incorrect singular variable name", "testObject", + Conventions.getVariableNameForReturnType(getMethodForReturnType(TestObject.class))); } @Test @@ -53,8 +69,15 @@ public class ConventionsTests { @Test public void list() { + assertEquals("Incorrect plural List form", "testObjectList", Conventions.getVariableName(Collections.singletonList(new TestObject()))); + + assertEquals("Incorrect plural List form", "testObjectList", + Conventions.getVariableNameForParameter(getMethodParameter(List.class))); + + assertEquals("Incorrect plural List form", "testObjectList", + Conventions.getVariableNameForReturnType(getMethodForReturnType(List.class))); } @Test @@ -65,8 +88,47 @@ public class ConventionsTests { @Test public void set() { + assertEquals("Incorrect plural Set form", "testObjectList", Conventions.getVariableName(Collections.singleton(new TestObject()))); + + assertEquals("Incorrect plural Set form", "testObjectList", + Conventions.getVariableNameForParameter(getMethodParameter(Set.class))); + + assertEquals("Incorrect plural Set form", "testObjectList", + Conventions.getVariableNameForReturnType(getMethodForReturnType(Set.class))); + } + + @Test + public void reactiveParameters() throws Exception { + + assertEquals("testObjectMono", + Conventions.getVariableNameForParameter(getMethodParameter(Mono.class))); + + assertEquals("testObjectFlux", + Conventions.getVariableNameForParameter(getMethodParameter(Flux.class))); + + assertEquals("testObjectSingle", + Conventions.getVariableNameForParameter(getMethodParameter(Single.class))); + + assertEquals("testObjectObservable", + Conventions.getVariableNameForParameter(getMethodParameter(Observable.class))); + } + + @Test + public void reactiveReturnTypes() throws Exception { + + assertEquals("testObjectMono", + Conventions.getVariableNameForReturnType(getMethodForReturnType(Mono.class))); + + assertEquals("testObjectFlux", + Conventions.getVariableNameForReturnType(getMethodForReturnType(Flux.class))); + + assertEquals("testObjectSingle", + Conventions.getVariableNameForReturnType(getMethodForReturnType(Single.class))); + + assertEquals("testObjectObservable", + Conventions.getVariableNameForReturnType(getMethodForReturnType(Observable.class))); } @Test @@ -84,4 +146,48 @@ public class ConventionsTests { assertEquals(desiredResult, Conventions.getQualifiedAttributeName(cls, baseName)); } + + private static MethodParameter getMethodParameter(Class parameterType) { + Method method = ClassUtils.getMethod(TestBean.class, "handle", (Class[]) null); + for (int i=0; i < method.getParameterCount(); i++) { + if (parameterType.equals(method.getParameterTypes()[i])) { + return new MethodParameter(method, i); + } + } + throw new IllegalArgumentException("Parameter type not found: " + parameterType); + } + + private static Method getMethodForReturnType(Class returnType) { + return Arrays.stream(TestBean.class.getMethods()) + .filter(method -> method.getReturnType().equals(returnType)) + .findFirst() + .orElseThrow(() -> + new IllegalArgumentException("Unique return type not found: " + returnType)); + } + + + @SuppressWarnings("unused") + private static class TestBean { + + public void handle(TestObject to, + List toList, Set toSet, + Mono toMono, Flux toFlux, + Single toSingle, Observable toObservable) { } + + public TestObject handleTo() { return null; } + + public List handleToList() { return null; } + + public Set handleToSet() { return null; } + + public Mono handleToMono() { return null; } + + public Flux handleToFlux() { return null; } + + public Single handleToSingle() { return null; } + + public Observable handleToObservable() { return null; } + + } + }