Skip to content

Commit

Permalink
Test status quo for null in varargs in SpEL expressions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sbrannen committed Dec 10, 2021
1 parent 96a7fc6 commit e03e62c
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 58 deletions.
@@ -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.
Expand Down Expand Up @@ -46,6 +46,7 @@
*
* @author Andy Clement
* @author Phillip Webb
* @author Sam Brannen
*/
public class MethodInvocationTests extends AbstractExpressionTests {

Expand Down Expand Up @@ -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<String>... 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
Expand Down
@@ -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.
Expand All @@ -16,6 +16,7 @@

package org.springframework.expression.spel;

import java.util.Arrays;
import java.util.GregorianCalendar;

import org.springframework.expression.spel.support.StandardEvaluationContext;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

}
Expand Up @@ -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
Expand Down
@@ -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.
Expand All @@ -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;

Expand Down Expand Up @@ -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<String>... values) {
return Arrays.toString(values);
}

public Inventor(String... strings) {
Expand Down

0 comments on commit e03e62c

Please sign in to comment.