diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 064579c82f8..f1eb05292cd 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -37,7 +37,7 @@ import org.springframework.util.ObjectUtils; /** * Contextual descriptor about a type to convert from or to. - * Capable of representing arrays and generic collection types. + *
Capable of representing arrays and generic collection types. * * @author Keith Donald * @author Andy Clement @@ -345,9 +345,9 @@ public class TypeDescriptor implements Serializable { * from the provided collection or array element. *
Narrows the {@link #getElementTypeDescriptor() elementType} property to the class
* of the provided collection or array element. For example, if this describes a
- * {@code java.util.List<java.lang.Number<} and the element argument is an
+ * {@code java.util.List Annotation and nested type context will be preserved in the narrowed
@@ -388,9 +388,9 @@ public class TypeDescriptor implements Serializable {
* from the provided map key.
* Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property
* to the class of the provided map key. For example, if this describes a
- * {@code java.util.Map<java.lang.Number, java.lang.String<} and the key
+ * {@code java.util.Map Annotation and nested type context will be preserved in the narrowed
@@ -425,9 +425,9 @@ public class TypeDescriptor implements Serializable {
* from the provided map value.
* Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property
* to the class of the provided map value. For example, if this describes a
- * {@code java.util.Map<java.lang.String, java.lang.Number<} and the value
+ * {@code java.util.Map Annotation and nested type context will be preserved in the narrowed
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
index f2f67c0965c..6974d6b8747 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -44,7 +44,7 @@ public interface TypeConverter {
* Convert (or coerce) a value from one type to another, for example from a
* {@code boolean} to a {@code String}.
* The {@link TypeDescriptor} parameters enable support for typed collections:
- * A caller may prefer a {@code List<Integer>}, for example, rather than
+ * A caller may prefer a {@code List For example, if requiredParameterTypes is {@code (int, String[])} because the second parameter
* was declared {@code String...}, then if arguments is {@code [1,"a","b"]} then it must be
* repackaged as {@code [1,new String[]{"a","b"}]} in order to match the expected types.
* @param requiredParameterTypes the types of the parameters for the invocation
@@ -350,23 +358,24 @@ public abstract class ReflectionHelper {
requiredParameterTypes[parameterCount - 1] !=
(args[argumentCount - 1] != null ? args[argumentCount - 1].getClass() : null)) {
- int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
- if (argumentCount >= parameterCount) {
- arraySize = argumentCount - (parameterCount - 1);
- }
-
- // Create an array for the varargs arguments
+ // Create an array for the leading arguments plus the varargs array argument.
Object[] newArgs = new Object[parameterCount];
+ // Copy all leading arguments to the new array, omitting the varargs array argument.
System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1);
// Now sort out the final argument, which is the varargs one. Before entering this method,
// the arguments should have been converted to the box form of the required type.
- Class> componentType = requiredParameterTypes[parameterCount - 1].getComponentType();
- Object repackagedArgs = Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- Array.set(repackagedArgs, i, args[parameterCount - 1 + i]);
+ int varargsArraySize = 0; // zero size array if nothing to pass as the varargs parameter
+ if (argumentCount >= parameterCount) {
+ varargsArraySize = argumentCount - (parameterCount - 1);
}
- newArgs[newArgs.length - 1] = repackagedArgs;
+ Class> componentType = requiredParameterTypes[parameterCount - 1].getComponentType();
+ Object varargsArray = Array.newInstance(componentType, varargsArraySize);
+ for (int i = 0; i < varargsArraySize; i++) {
+ Array.set(varargsArray, i, args[parameterCount - 1 + i]);
+ }
+ // Finally, add the varargs array to the new arguments array.
+ newArgs[newArgs.length - 1] = varargsArray;
return newArgs;
}
return args;
diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java
index 0a025acf723..b2cde1f10ff 100644
--- a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java
+++ b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -46,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
* @author Andy Clement
* @author Phillip Webb
+ * @author Sam Brannen
*/
public class MethodInvocationTests extends AbstractExpressionTests {
@@ -233,26 +234,54 @@ public class MethodInvocationTests extends AbstractExpressionTests {
@Test
public void testVarargsInvocation01() {
- // Calling 'public int aVarargsMethod(String... strings)'
- //evaluate("aVarargsMethod('a','b','c')", 3, Integer.class);
- //evaluate("aVarargsMethod('a')", 1, Integer.class);
+ // Calling 'public int aVarargsMethod(String... strings)' - returns number of arguments
+ evaluate("aVarargsMethod('a','b','c')", 3, Integer.class);
+ evaluate("aVarargsMethod('a')", 1, Integer.class);
evaluate("aVarargsMethod()", 0, Integer.class);
evaluate("aVarargsMethod(1,2,3)", 3, Integer.class); // all need converting to strings
evaluate("aVarargsMethod(1)", 1, Integer.class); // needs string conversion
evaluate("aVarargsMethod(1,'a',3.0d)", 3, Integer.class); // first and last need conversion
- // evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class);
+ evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class);
}
@Test
public void testVarargsInvocation02() {
- // Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int+length_of_strings
+ // Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int + length_of_strings
evaluate("aVarargsMethod2(5,'a','b','c')", 8, Integer.class);
evaluate("aVarargsMethod2(2,'a')", 3, Integer.class);
evaluate("aVarargsMethod2(4)", 4, Integer.class);
evaluate("aVarargsMethod2(8,2,3)", 10, Integer.class);
evaluate("aVarargsMethod2(9)", 9, Integer.class);
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
- // evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
+ evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
+ }
+
+ @Test
+ public void testVarargsInvocation03() {
+ // Calling 'public int aVarargsMethod3(String str1, String... strings)' - returns all strings concatenated with "-"
+
+ // No conversion necessary
+ evaluate("aVarargsMethod3('x')", "x", String.class);
+ evaluate("aVarargsMethod3('x', 'a')", "x-a", String.class);
+ evaluate("aVarargsMethod3('x', 'a', 'b', 'c')", "x-a-b-c", String.class);
+
+ // Conversion necessary
+ evaluate("aVarargsMethod3(9)", "9", String.class);
+ evaluate("aVarargsMethod3(8,2,3)", "8-2-3", String.class);
+ evaluate("aVarargsMethod3('2','a',3.0d)", "2-a-3.0", String.class);
+ evaluate("aVarargsMethod3('8',new String[]{'a','b','c'})", "8-a-b-c", String.class);
+
+ // Individual string contains a comma with multiple varargs arguments
+ evaluate("aVarargsMethod3('foo', ',', 'baz')", "foo-,-baz", String.class);
+ evaluate("aVarargsMethod3('foo', 'bar', ',baz')", "foo-bar-,baz", String.class);
+ evaluate("aVarargsMethod3('foo', 'bar,', 'baz')", "foo-bar,-baz", String.class);
+
+ // Individual string contains a comma with single varargs argument.
+ // Reproduces https://github.com/spring-projects/spring-framework/issues/27582
+ evaluate("aVarargsMethod3('foo', ',')", "foo-,", String.class);
+ evaluate("aVarargsMethod3('foo', ',bar')", "foo-,bar", String.class);
+ evaluate("aVarargsMethod3('foo', 'bar,')", "foo-bar,", String.class);
+ evaluate("aVarargsMethod3('foo', 'bar,baz')", "foo-bar,baz", String.class);
}
@Test
diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java b/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java
index bdf6d79c1dd..34960d982fe 100644
--- a/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java
+++ b/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -28,6 +28,7 @@ import org.springframework.util.ObjectUtils;
///CLOVER:OFF
@SuppressWarnings("unused")
public class Inventor {
+
private String name;
public String _name;
public String _name_;
@@ -202,8 +203,14 @@ public class Inventor {
return strings.length + i;
}
- public Inventor(String... strings) {
+ public String aVarargsMethod3(String str1, String... strings) {
+ if (ObjectUtils.isEmpty(strings)) {
+ return str1;
+ }
+ return str1 + "-" + String.join("-", strings);
+ }
+ public Inventor(String... strings) {
}
public boolean getSomeProperty() {