Support MethodHandle invocation with primitive varargs array in SpEL
Prior to this commit, the Spring Expression Language (SpEL) could not invoke a varargs MethodHandle function with a primitive array containing the variable arguments, although that is supported for a varargs Method function. Attempting to do so resulted in the first element of the primitive array being supplied as a single argument to the MethodHandle, effectively ignoring any variable arguments after the first one. This commit addresses this by updating the convertAllMethodHandleArguments(...) method in ReflectionHelper as follows when the user supplies the varargs already packaged in a primitive array. - Regarding conversion, use the wrapper type for a primitive varargs array, since we eventually need an Object array in order to invoke the MethodHandle in FunctionReference#executeFunctionViaMethodHandle(). - When deciding whether to convert a single element passed as varargs, we now check if the argument is an array that is assignable to the varargs array type. - When converting an array supplied as the varargs, we now convert that array to the varargs array type instead of the varargs component type. Note, however, that a SpEL expression cannot provide a primitive array for an Object[] varargs target. This is due to the fact that the ArrayToArrayConverter used by Spring's ConversionService does not support conversion from a primitive array to Object[] -- for example, from int[] to Object[]. See gh-33191 Closes gh-33198
This commit is contained in:
parent
152914a752
commit
e088892fc1
|
@ -384,11 +384,15 @@ public abstract class ReflectionHelper {
|
||||||
conversionOccurred |= (argument != arguments[i]);
|
conversionOccurred |= (argument != arguments[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> varArgClass = methodHandleType.lastParameterType();
|
Class<?> varargsArrayClass = methodHandleType.lastParameterType();
|
||||||
ResolvableType varArgResolvableType = ResolvableType.forClass(varArgClass);
|
// We use the wrapper type for a primitive varargs array, since we eventually
|
||||||
TypeDescriptor targetType = new TypeDescriptor(varArgResolvableType, varArgClass.componentType(), null);
|
// need an Object array in order to invoke the MethodHandle in
|
||||||
TypeDescriptor componentTypeDesc = targetType.getElementTypeDescriptor();
|
// FunctionReference#executeFunctionViaMethodHandle().
|
||||||
Assert.state(componentTypeDesc != null, "Component type must not be null for a varargs array");
|
Class<?> varargsComponentClass = ClassUtils.resolvePrimitiveIfNecessary(varargsArrayClass.componentType());
|
||||||
|
TypeDescriptor varargsArrayType = TypeDescriptor.array(TypeDescriptor.valueOf(varargsComponentClass));
|
||||||
|
Assert.state(varargsArrayType != null, "Array type must not be null for a varargs array");
|
||||||
|
TypeDescriptor varargsComponentType = varargsArrayType.getElementTypeDescriptor();
|
||||||
|
Assert.state(varargsComponentType != null, "Component type must not be null for a varargs array");
|
||||||
|
|
||||||
// If the target is varargs and there is just one more argument, then convert it here.
|
// If the target is varargs and there is just one more argument, then convert it here.
|
||||||
if (varargsPosition == arguments.length - 1) {
|
if (varargsPosition == arguments.length - 1) {
|
||||||
|
@ -396,17 +400,21 @@ public abstract class ReflectionHelper {
|
||||||
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
||||||
if (argument == null) {
|
if (argument == null) {
|
||||||
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
|
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
|
||||||
if (componentTypeDesc.getObjectType() == Optional.class) {
|
if (varargsComponentType.getObjectType() == Optional.class) {
|
||||||
arguments[varargsPosition] = Optional.empty();
|
arguments[varargsPosition] = Optional.empty();
|
||||||
conversionOccurred = true;
|
conversionOccurred = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the argument type is assignable to the varargs component type, there is no need to
|
// If the argument type is assignable to the varargs component type, there is no need to
|
||||||
// convert it or wrap it in an array. For example, using StringToArrayConverter to
|
// convert it. For example, using StringToArrayConverter to convert a String containing a
|
||||||
// convert a String containing a comma would result in the String being split and
|
// comma would result in the String being split and repackaged in an array when it should
|
||||||
// repackaged in an array when it should be used as-is.
|
// be used as-is. Similarly, if the argument is an array that is assignable to the varargs
|
||||||
else if (!sourceType.isAssignableTo(componentTypeDesc)) {
|
// array type, there is no need to convert it.
|
||||||
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
|
else if (!sourceType.isAssignableTo(varargsComponentType) ||
|
||||||
|
(sourceType.isArray() && !sourceType.isAssignableTo(varargsArrayType))) {
|
||||||
|
|
||||||
|
TypeDescriptor targetTypeToUse = (sourceType.isArray() ? varargsArrayType : varargsComponentType);
|
||||||
|
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetTypeToUse);
|
||||||
}
|
}
|
||||||
// Possible outcomes of the above if-else block:
|
// Possible outcomes of the above if-else block:
|
||||||
// 1) the input argument was null, and nothing was done.
|
// 1) the input argument was null, and nothing was done.
|
||||||
|
@ -424,7 +432,7 @@ public abstract class ReflectionHelper {
|
||||||
for (int i = varargsPosition; i < arguments.length; i++) {
|
for (int i = varargsPosition; i < arguments.length; i++) {
|
||||||
Object argument = arguments[i];
|
Object argument = arguments[i];
|
||||||
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
||||||
arguments[i] = converter.convertValue(argument, sourceType, componentTypeDesc);
|
arguments[i] = converter.convertValue(argument, sourceType, varargsComponentType);
|
||||||
conversionOccurred |= (argument != arguments[i]);
|
conversionOccurred |= (argument != arguments[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,6 +245,7 @@ class MethodInvocationTests extends AbstractExpressionTests {
|
||||||
evaluate("aVarargsMethod(1,'a',3.0d)", "[1, a, 3.0]", String.class); // first and last need conversion
|
evaluate("aVarargsMethod(1,'a',3.0d)", "[1, a, 3.0]", String.class); // first and last need conversion
|
||||||
evaluate("aVarargsMethod(new String[]{'a','b','c'})", "[a, b, c]", String.class);
|
evaluate("aVarargsMethod(new String[]{'a','b','c'})", "[a, b, c]", String.class);
|
||||||
evaluate("aVarargsMethod(new String[]{})", "[]", String.class);
|
evaluate("aVarargsMethod(new String[]{})", "[]", String.class);
|
||||||
|
evaluate("aVarargsMethod(new int[]{1, 2, 3})", "[1, 2, 3]", String.class); // needs int[] to String[] conversion
|
||||||
evaluate("aVarargsMethod(null)", "[null]", String.class);
|
evaluate("aVarargsMethod(null)", "[null]", String.class);
|
||||||
evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class);
|
evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class);
|
||||||
evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class);
|
evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class);
|
||||||
|
@ -320,6 +321,7 @@ class MethodInvocationTests extends AbstractExpressionTests {
|
||||||
// Conversion necessary
|
// Conversion necessary
|
||||||
evaluate("formatObjectVarargs('x -> %s %s', 2, 3)", "x -> 2 3", String.class);
|
evaluate("formatObjectVarargs('x -> %s %s', 2, 3)", "x -> 2 3", String.class);
|
||||||
evaluate("formatObjectVarargs('x -> %s %s', 'a', 3.0d)", "x -> a 3.0", String.class);
|
evaluate("formatObjectVarargs('x -> %s %s', 'a', 3.0d)", "x -> a 3.0", String.class);
|
||||||
|
evaluate("formatObjectVarargs('x -> %s %s %s', new Integer[]{1, 2, 3})", "x -> 1 2 3", String.class);
|
||||||
|
|
||||||
// Individual string contains a comma with multiple varargs arguments
|
// Individual string contains a comma with multiple varargs arguments
|
||||||
evaluate("formatObjectVarargs('foo -> %s %s', ',', 'baz')", "foo -> , baz", String.class);
|
evaluate("formatObjectVarargs('foo -> %s %s', ',', 'baz')", "foo -> , baz", String.class);
|
||||||
|
@ -333,6 +335,27 @@ class MethodInvocationTests extends AbstractExpressionTests {
|
||||||
evaluate("formatObjectVarargs('foo -> %s', 'bar,baz')", "foo -> bar,baz", String.class);
|
evaluate("formatObjectVarargs('foo -> %s', 'bar,baz')", "foo -> bar,baz", String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVarargsWithPrimitiveArrayType() {
|
||||||
|
// Calling 'public String formatPrimitiveVarargs(String format, int... nums)' -> effectively String.format(format, args)
|
||||||
|
|
||||||
|
// No var-args and no conversion necessary
|
||||||
|
evaluate("formatPrimitiveVarargs(9)", "9", String.class);
|
||||||
|
|
||||||
|
// No var-args but conversion necessary
|
||||||
|
evaluate("formatPrimitiveVarargs('7')", "7", String.class);
|
||||||
|
|
||||||
|
// No conversion necessary
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s', 9)", "x -> 9", String.class);
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s %s %s', 1, 2, 3)", "x -> 1 2 3", String.class);
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s', new int[]{1})", "x -> 1", String.class);
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s %s %s', new int[]{1, 2, 3})", "x -> 1 2 3", String.class);
|
||||||
|
|
||||||
|
// Conversion necessary
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s %s', '2', '3')", "x -> 2 3", String.class);
|
||||||
|
evaluate("formatPrimitiveVarargs('x -> %s %s', '2', 3.0d)", "x -> 2 3", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVarargsOptionalInvocation() {
|
void testVarargsOptionalInvocation() {
|
||||||
// Calling 'public String optionalVarargsMethod(Optional<String>... values)'
|
// Calling 'public String optionalVarargsMethod(Optional<String>... values)'
|
||||||
|
|
|
@ -66,6 +66,8 @@ class TestScenarioCreator {
|
||||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class));
|
TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class));
|
||||||
testContext.registerFunction("varargsFunction2",
|
testContext.registerFunction("varargsFunction2",
|
||||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", int.class, String[].class));
|
TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", int.class, String[].class));
|
||||||
|
testContext.registerFunction("varargsObjectFunction",
|
||||||
|
TestScenarioCreator.class.getDeclaredMethod("varargsObjectFunction", Object[].class));
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
|
@ -106,6 +108,11 @@ class TestScenarioCreator {
|
||||||
"formatObjectVarargs", MethodType.methodType(String.class, String.class, Object[].class));
|
"formatObjectVarargs", MethodType.methodType(String.class, String.class, Object[].class));
|
||||||
testContext.registerFunction("formatObjectVarargs", formatObjectVarargs);
|
testContext.registerFunction("formatObjectVarargs", formatObjectVarargs);
|
||||||
|
|
||||||
|
// #formatObjectVarargs(format, args...)
|
||||||
|
MethodHandle formatPrimitiveVarargs = MethodHandles.lookup().findStatic(TestScenarioCreator.class,
|
||||||
|
"formatPrimitiveVarargs", MethodType.methodType(String.class, String.class, int[].class));
|
||||||
|
testContext.registerFunction("formatPrimitiveVarargs", formatPrimitiveVarargs);
|
||||||
|
|
||||||
// #add(int, int)
|
// #add(int, int)
|
||||||
MethodHandle add = MethodHandles.lookup().findStatic(TestScenarioCreator.class,
|
MethodHandle add = MethodHandles.lookup().findStatic(TestScenarioCreator.class,
|
||||||
"add", MethodType.methodType(int.class, int.class, int.class));
|
"add", MethodType.methodType(int.class, int.class, int.class));
|
||||||
|
@ -160,6 +167,10 @@ class TestScenarioCreator {
|
||||||
return i + "-" + Arrays.toString(strings);
|
return i + "-" + Arrays.toString(strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String varargsObjectFunction(Object... args) {
|
||||||
|
return Arrays.toString(args);
|
||||||
|
}
|
||||||
|
|
||||||
public static String message(String template, String... args) {
|
public static String message(String template, String... args) {
|
||||||
return template.formatted((Object[]) args);
|
return template.formatted((Object[]) args);
|
||||||
}
|
}
|
||||||
|
@ -168,6 +179,14 @@ class TestScenarioCreator {
|
||||||
return String.format(format, args);
|
return String.format(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatPrimitiveVarargs(String format, int... nums) {
|
||||||
|
Object[] args = new Object[nums.length];
|
||||||
|
for (int i = 0; i < nums.length; i++) {
|
||||||
|
args[i] = nums[i];
|
||||||
|
}
|
||||||
|
return String.format(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
public static int add(int x, int y) {
|
public static int add(int x, int y) {
|
||||||
return x + y;
|
return x + y;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,9 +80,11 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||||
evaluate("#varargsFunction(new String[0])", "[]", String.class);
|
evaluate("#varargsFunction(new String[0])", "[]", String.class);
|
||||||
evaluate("#varargsFunction('a')", "[a]", String.class);
|
evaluate("#varargsFunction('a')", "[a]", String.class);
|
||||||
evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class);
|
evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class);
|
||||||
|
evaluate("#varargsFunction(new String[]{'a','b','c'})", "[a, b, c]", String.class);
|
||||||
// Conversion from int to String
|
// Conversion from int to String
|
||||||
evaluate("#varargsFunction(25)", "[25]", String.class);
|
evaluate("#varargsFunction(25)", "[25]", String.class);
|
||||||
evaluate("#varargsFunction('b',25)", "[b, 25]", String.class);
|
evaluate("#varargsFunction('b',25)", "[b, 25]", String.class);
|
||||||
|
evaluate("#varargsFunction(new int[]{1, 2, 3})", "[1, 2, 3]", String.class);
|
||||||
// Strings that contain a comma
|
// Strings that contain a comma
|
||||||
evaluate("#varargsFunction('a,b')", "[a,b]", String.class);
|
evaluate("#varargsFunction('a,b')", "[a,b]", String.class);
|
||||||
evaluate("#varargsFunction('a', 'x,y', 'd')", "[a, x,y, d]", String.class);
|
evaluate("#varargsFunction('a', 'x,y', 'd')", "[a, x,y, d]", String.class);
|
||||||
|
@ -103,6 +105,21 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||||
// null values
|
// null values
|
||||||
evaluate("#varargsFunction2(9,null)", "9-[null]", String.class);
|
evaluate("#varargsFunction2(9,null)", "9-[null]", String.class);
|
||||||
evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class);
|
evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class);
|
||||||
|
|
||||||
|
evaluate("#varargsObjectFunction()", "[]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction(new String[0])", "[]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction('a')", "[a]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction('a','b','c')", "[a, b, c]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction(new String[]{'a','b','c'})", "[a, b, c]", String.class);
|
||||||
|
// Conversion from int to String
|
||||||
|
evaluate("#varargsObjectFunction(25)", "[25]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction('b',25)", "[b, 25]", String.class);
|
||||||
|
// Strings that contain a comma
|
||||||
|
evaluate("#varargsObjectFunction('a,b')", "[a,b]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction('a', 'x,y', 'd')", "[a, x,y, d]", String.class);
|
||||||
|
// null values
|
||||||
|
evaluate("#varargsObjectFunction(null)", "[null]", String.class);
|
||||||
|
evaluate("#varargsObjectFunction('a',null,'b')", "[a, null, b]", String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-33013
|
@Test // gh-33013
|
||||||
|
@ -110,17 +127,25 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||||
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)
|
// Calling 'public static String formatObjectVarargs(String format, Object... args)' -> String.format(format, args)
|
||||||
|
|
||||||
// No var-args and no conversion necessary
|
// No var-args and no conversion necessary
|
||||||
|
evaluate("#message('x')", "x", String.class);
|
||||||
evaluate("#formatObjectVarargs('x')", "x", String.class);
|
evaluate("#formatObjectVarargs('x')", "x", String.class);
|
||||||
|
|
||||||
// No var-args but conversion necessary
|
// No var-args but conversion necessary
|
||||||
|
evaluate("#message(9)", "9", String.class);
|
||||||
evaluate("#formatObjectVarargs(9)", "9", String.class);
|
evaluate("#formatObjectVarargs(9)", "9", String.class);
|
||||||
|
|
||||||
// No conversion necessary
|
// No conversion necessary
|
||||||
evaluate("#add(3, 4)", 7, Integer.class);
|
evaluate("#add(3, 4)", 7, Integer.class);
|
||||||
|
evaluate("#message('x -> %s %s %s', 'a', 'b', 'c')", "x -> a b c", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s', '')", "x -> ", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', '')", "x -> ", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s', ' ')", "x -> ", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', ' ')", "x -> ", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s', 'a')", "x -> a", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', 'a')", "x -> a", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s %s %s', 'a', 'b', 'c')", "x -> a b c", String.class);
|
evaluate("#formatObjectVarargs('x -> %s %s %s', 'a', 'b', 'c')", "x -> a b c", String.class);
|
||||||
|
evaluate("#message('x -> %s %s %s', new Object[]{'a', 'b', 'c'})", "x -> a b c", String.class); // Object[] instanceof Object[]
|
||||||
|
evaluate("#message('x -> %s %s %s', new String[]{'a', 'b', 'c'})", "x -> a b c", String.class); // String[] instanceof Object[]
|
||||||
|
evaluate("#message('x -> %s %s %s', new Integer[]{1, 2, 3})", "x -> 1 2 3", String.class); // Integer[] instanceof Object[]
|
||||||
|
evaluate("#formatObjectVarargs('x -> %s %s', 2, 3)", "x -> 2 3", String.class); // Integer instanceof Object
|
||||||
|
evaluate("#formatObjectVarargs('x -> %s %s', 'a', 3.0F)", "x -> a 3.0", String.class); // String/Float instanceof Object
|
||||||
evaluate("#formatObjectVarargs('x -> %s', new Object[]{''})", "x -> ", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', new Object[]{''})", "x -> ", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s', new String[]{''})", "x -> ", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', new String[]{''})", "x -> ", String.class);
|
||||||
evaluate("#formatObjectVarargs('x -> %s', new Object[]{' '})", "x -> ", String.class);
|
evaluate("#formatObjectVarargs('x -> %s', new Object[]{' '})", "x -> ", String.class);
|
||||||
|
@ -131,9 +156,12 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||||
evaluate("#formatObjectVarargs('x -> %s %s %s', new String[]{'a', 'b', 'c'})", "x -> a b c", String.class);
|
evaluate("#formatObjectVarargs('x -> %s %s %s', new String[]{'a', 'b', 'c'})", "x -> a b c", String.class);
|
||||||
|
|
||||||
// Conversion necessary
|
// Conversion necessary
|
||||||
evaluate("#add('2', 5.0)", 7, Integer.class);
|
evaluate("#add('2', 5.0)", 7, Integer.class); // String/Double to Integer
|
||||||
evaluate("#formatObjectVarargs('x -> %s %s', 2, 3)", "x -> 2 3", String.class);
|
evaluate("#messageStatic('x -> %s %s %s', 1, 2, 3)", "x -> 1 2 3", String.class); // Integer to String
|
||||||
evaluate("#formatObjectVarargs('x -> %s %s', 'a', 3.0d)", "x -> a 3.0", String.class);
|
evaluate("#messageStatic('x -> %s %s %s', new Integer[]{1, 2, 3})", "x -> 1 2 3", String.class); // Integer[] to String[]
|
||||||
|
evaluate("#messageStatic('x -> %s %s %s', new int[]{1, 2, 3})", "x -> 1 2 3", String.class); // int[] to String[]
|
||||||
|
evaluate("#messageStatic('x -> %s %s %s', new short[]{1, 2, 3})", "x -> 1 2 3", String.class); // short[] to String[]
|
||||||
|
evaluate("#formatObjectVarargs('x -> %s %s %s', new Integer[]{1, 2, 3})", "x -> 1 2 3", String.class); // Integer[] to String[]
|
||||||
|
|
||||||
// Individual string contains a comma with multiple varargs arguments
|
// Individual string contains a comma with multiple varargs arguments
|
||||||
evaluate("#formatObjectVarargs('foo -> %s %s', ',', 'baz')", "foo -> , baz", String.class);
|
evaluate("#formatObjectVarargs('foo -> %s %s', ',', 'baz')", "foo -> , baz", String.class);
|
||||||
|
@ -147,6 +175,29 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||||
evaluate("#formatObjectVarargs('foo -> %s', 'bar,baz')", "foo -> bar,baz", String.class);
|
evaluate("#formatObjectVarargs('foo -> %s', 'bar,baz')", "foo -> bar,baz", String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void functionWithPrimitiveVarargsViaMethodHandle() {
|
||||||
|
// Calling 'public String formatPrimitiveVarargs(String format, int... nums)' -> effectively String.format(format, args)
|
||||||
|
|
||||||
|
// No var-args and no conversion necessary
|
||||||
|
evaluate("#formatPrimitiveVarargs(9)", "9", String.class);
|
||||||
|
|
||||||
|
// No var-args but conversion necessary
|
||||||
|
evaluate("#formatPrimitiveVarargs('7')", "7", String.class);
|
||||||
|
|
||||||
|
// No conversion necessary
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s', 9)", "x -> 9", String.class);
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s %s', 1, 2, 3)", "x -> 1 2 3", String.class);
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s', new int[]{1})", "x -> 1", String.class);
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s %s', new int[]{1, 2, 3})", "x -> 1 2 3", String.class);
|
||||||
|
|
||||||
|
// Conversion necessary
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s', '2', '3')", "x -> 2 3", String.class); // String to int
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s', '2', 3.0F)", "x -> 2 3", String.class); // String/Float to int
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s %s', new Integer[]{1, 2, 3})", "x -> 1 2 3", String.class); // Integer[] to int[]
|
||||||
|
evaluate("#formatPrimitiveVarargs('x -> %s %s %s', new String[]{'1', '2', '3'})", "x -> 1 2 3", String.class); // String[] to int[]
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void functionMethodMustBeStatic() throws Exception {
|
void functionMethodMustBeStatic() throws Exception {
|
||||||
SpelExpressionParser parser = new SpelExpressionParser();
|
SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
|
@ -221,6 +221,14 @@ public class Inventor {
|
||||||
return String.format(format, args);
|
return String.format(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String formatPrimitiveVarargs(String format, int... nums) {
|
||||||
|
Object[] args = new Object[nums.length];
|
||||||
|
for (int i = 0; i < nums.length; i++) {
|
||||||
|
args[i] = nums[i];
|
||||||
|
}
|
||||||
|
return String.format(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Inventor(String... strings) {
|
public Inventor(String... strings) {
|
||||||
if (strings.length > 0) {
|
if (strings.length > 0) {
|
||||||
|
|
Loading…
Reference in New Issue