From e03e62c76a8081c01aabc25401f30c2f956f3a70 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 10 Dec 2021 11:18:10 +0100 Subject: [PATCH] Test status quo for null in varargs in SpEL expressions This commit also points out that `null` supplied as a single value for a varargs array of type Optional will be kept as `null` instead of being converted to Optional.empty(); whereas, if more than one value is passed to such a varargs array a null value will be properly converted to Optional.empty(). See gh-27719 --- .../spel/MethodInvocationTests.java | 63 ++++++++++++++----- .../expression/spel/TestScenarioCreator.java | 32 +++------- .../spel/VariableAndFunctionTests.java | 27 +++++--- .../spel/testresources/Inventor.java | 21 ++++--- 4 files changed, 85 insertions(+), 58 deletions(-) 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 e2dd8fc6ff1d..a0304081f410 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 @@ * * @author Andy Clement * @author Phillip Webb + * @author Sam Brannen */ public class MethodInvocationTests extends AbstractExpressionTests { @@ -232,26 +233,54 @@ public void testAddingMethodResolvers() { @Test public void testVarargsInvocation01() { - // Calling 'public int aVarargsMethod(String... strings)' - //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); + // Calling 'public String aVarargsMethod(String... strings)' + evaluate("aVarargsMethod('a','b','c')", "[a, b, c]", String.class); + evaluate("aVarargsMethod('a')", "[a]", String.class); + evaluate("aVarargsMethod()", "[]", String.class); + evaluate("aVarargsMethod(1,2,3)", "[1, 2, 3]", String.class); // all need converting to strings + evaluate("aVarargsMethod(1)", "[1]", String.class); // needs string 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[]{})", "[]", String.class); + evaluate("aVarargsMethod(null)", "[null]", String.class); + evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class); + evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class); } @Test public void testVarargsInvocation02() { - // 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); + // Calling 'public String aVarargsMethod2(int i, String... strings)' + evaluate("aVarargsMethod2(5,'a','b','c')", "5-[a, b, c]", String.class); + evaluate("aVarargsMethod2(2,'a')", "2-[a]", String.class); + evaluate("aVarargsMethod2(4)", "4-[]", String.class); + evaluate("aVarargsMethod2(8,2,3)", "8-[2, 3]", String.class); + evaluate("aVarargsMethod2(2,'a',3.0d)", "2-[a, 3.0]", String.class); + evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", "8-[a, b, c]", String.class); + evaluate("aVarargsMethod2(8,new String[]{})", "8-[]", String.class); + evaluate("aVarargsMethod2(8,null)", "8-[null]", String.class); + evaluate("aVarargsMethod2(8,null,'a')", "8-[null, a]", String.class); + evaluate("aVarargsMethod2(8,'a',null,'b')", "8-[a, null, b]", String.class); + } + + @Test + public void testVarargsOptionalInvocation() { + // Calling 'public String optionalVarargsMethod(Optional... values)' + evaluate("optionalVarargsMethod()", "[]", String.class); + evaluate("optionalVarargsMethod(new String[0])", "[]", String.class); + evaluate("optionalVarargsMethod('a')", "[Optional[a]]", String.class); + evaluate("optionalVarargsMethod('a','b','c')", "[Optional[a], Optional[b], Optional[c]]", String.class); + evaluate("optionalVarargsMethod(9)", "[Optional[9]]", String.class); + evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class); + evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class); + evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class); + // The following should actually evaluate to [Optional.empty] instead of [null], + // but ReflectionHelper.convertArguments() passes the array type instead of + // the array's component type as the target type to the ConversionService, and + // GenericConversionService.convertNullSource() therefore fails to convert null + // to Optional.empty(). + evaluate("optionalVarargsMethod(null)", "[null]", String.class); + evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class); + evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class); } @Test diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java index eb60acd79808..4bb3f7da0cc3 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 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. @@ -16,6 +16,7 @@ package org.springframework.expression.spel; +import java.util.Arrays; import java.util.GregorianCalendar; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -51,10 +52,10 @@ private static void populateFunctions(StandardEvaluationContext testContext) { TestScenarioCreator.class.getDeclaredMethod("reverseInt", Integer.TYPE, Integer.TYPE, Integer.TYPE)); testContext.registerFunction("reverseString", TestScenarioCreator.class.getDeclaredMethod("reverseString", String.class)); - testContext.registerFunction("varargsFunctionReverseStringsAndMerge", - TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge", String[].class)); - testContext.registerFunction("varargsFunctionReverseStringsAndMerge2", - TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge2", Integer.TYPE, String[].class)); + testContext.registerFunction("varargsFunction", + TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class)); + testContext.registerFunction("varargsFunction2", + TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", Integer.TYPE, String[].class)); } catch (Exception ex) { throw new IllegalStateException(ex); @@ -108,25 +109,12 @@ public static String reverseString(String input) { return backwards.toString(); } - public static String varargsFunctionReverseStringsAndMerge(String... strings) { - StringBuilder sb = new StringBuilder(); - if (strings != null) { - for (int i = strings.length - 1; i >= 0; i--) { - sb.append(strings[i]); - } - } - return sb.toString(); + public static String varargsFunction(String... strings) { + return Arrays.toString(strings); } - public static String varargsFunctionReverseStringsAndMerge2(int j, String... strings) { - StringBuilder sb = new StringBuilder(); - sb.append(j); - if (strings != null) { - for (int i = strings.length - 1; i >= 0; i--) { - sb.append(strings[i]); - } - } - return sb.toString(); + public static String varargsFunction2(int i, String... strings) { + return String.valueOf(i) + "-" + Arrays.toString(strings); } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java index 0333e20ff961..e97ff53c35e0 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java @@ -58,16 +58,23 @@ public void testFunctionAccess02() { @Test public void testCallVarargsFunction() { - evaluate("#varargsFunctionReverseStringsAndMerge('a','b','c')", "cba", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('a')", "a", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge()", "", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('b',25)", "25b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge(25)", "25", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(2,'a')", "2a", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(3)", "3", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class); + evaluate("#varargsFunction()", "[]", String.class); + evaluate("#varargsFunction(new String[0])", "[]", String.class); + evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class); + evaluate("#varargsFunction('a')", "[a]", String.class); + evaluate("#varargsFunction('b',25)", "[b, 25]", String.class); + evaluate("#varargsFunction(25)", "[25]", String.class); + evaluate("#varargsFunction(null)", "[null]", String.class); + evaluate("#varargsFunction('a',null,'b')", "[a, null, b]", String.class); + + evaluate("#varargsFunction2(9)", "9-[]", String.class); + evaluate("#varargsFunction2(9, new String[0])", "9-[]", String.class); + evaluate("#varargsFunction2(9,'a','b','c')", "9-[a, b, c]", String.class); + evaluate("#varargsFunction2(9,'a')", "9-[a]", String.class); + evaluate("#varargsFunction2(9,'b',25)", "9-[b, 25]", String.class); + evaluate("#varargsFunction2(9,25)", "9-[25]", String.class); + evaluate("#varargsFunction2(9,null)", "9-[null]", String.class); + evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", 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 bdf6d79c1ddc..664908ce7272 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. @@ -17,11 +17,13 @@ package org.springframework.expression.spel.testresources; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.springframework.util.ObjectUtils; @@ -190,16 +192,17 @@ public String joinThreeStrings(String a, String b, String c) { return a + b + c; } - public int aVarargsMethod(String... strings) { - if (strings == null) - return 0; - return strings.length; + public String aVarargsMethod(String... strings) { + return Arrays.toString(strings); } - public int aVarargsMethod2(int i, String... strings) { - if (strings == null) - return i; - return strings.length + i; + public String aVarargsMethod2(int i, String... strings) { + return String.valueOf(i) + "-" + Arrays.toString(strings); + } + + @SuppressWarnings("unchecked") + public String optionalVarargsMethod(Optional... values) { + return Arrays.toString(values); } public Inventor(String... strings) {