1
1
/*
2
- * Copyright 2002-2024 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .expression .spel ;
18
18
19
+ import java .util .Set ;
20
+
21
+ import org .junit .jupiter .api .BeforeEach ;
22
+ import org .junit .jupiter .api .Nested ;
19
23
import org .junit .jupiter .api .Test ;
20
24
21
- import org .springframework .expression .spel .standard .SpelExpressionParser ;
25
+ import org .springframework .core .convert .TypeDescriptor ;
26
+ import org .springframework .core .convert .converter .GenericConverter ;
27
+ import org .springframework .core .convert .support .DefaultConversionService ;
28
+ import org .springframework .expression .Expression ;
29
+ import org .springframework .expression .spel .standard .SpelExpression ;
22
30
import org .springframework .expression .spel .support .StandardEvaluationContext ;
31
+ import org .springframework .expression .spel .support .StandardTypeConverter ;
23
32
import org .springframework .expression .spel .support .StandardTypeLocator ;
33
+ import org .springframework .lang .Nullable ;
24
34
25
35
import static org .assertj .core .api .Assertions .assertThat ;
26
36
import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
@@ -266,11 +276,10 @@ void functionViaMethodHandleForStaticMethodThatAcceptsOnlyVarargs() {
266
276
267
277
@ Test
268
278
void functionMethodMustBeStatic () throws Exception {
269
- SpelExpressionParser parser = new SpelExpressionParser ();
270
- StandardEvaluationContext ctx = new StandardEvaluationContext ();
271
- ctx .setVariable ("notStatic" , this .getClass ().getMethod ("nonStatic" ));
279
+ context .registerFunction ("nonStatic" , this .getClass ().getMethod ("nonStatic" ));
280
+ SpelExpression expression = parser .parseRaw ("#nonStatic()" );
272
281
assertThatExceptionOfType (SpelEvaluationException .class )
273
- .isThrownBy (() -> parser . parseRaw ( "#notStatic()" ). getValue (ctx ))
282
+ .isThrownBy (() -> expression . getValue (context ))
274
283
.satisfies (ex -> assertThat (ex .getMessageCode ()).isEqualTo (FUNCTION_MUST_BE_STATIC ));
275
284
}
276
285
@@ -279,4 +288,75 @@ void functionMethodMustBeStatic() throws Exception {
279
288
public void nonStatic () {
280
289
}
281
290
291
+
292
+ @ Nested // gh-34371
293
+ class VarargsAndPojoToArrayConversionTests {
294
+
295
+ private final StandardEvaluationContext context = TestScenarioCreator .getTestEvaluationContext ();
296
+
297
+ private final ArrayHolder arrayHolder = new ArrayHolder ("a" , "b" , "c" );
298
+
299
+
300
+ @ BeforeEach
301
+ void setUp () {
302
+ DefaultConversionService conversionService = new DefaultConversionService ();
303
+ conversionService .addConverter (new ArrayHolderConverter ());
304
+ context .setTypeConverter (new StandardTypeConverter (conversionService ));
305
+ context .setVariable ("arrayHolder" , arrayHolder );
306
+ }
307
+
308
+ @ Test
309
+ void functionWithVarargsAndPojoToArrayConversion () {
310
+ // #varargsFunction: static String varargsFunction(String... strings) -> Arrays.toString(strings)
311
+ evaluate ("#varargsFunction(#arrayHolder)" , "[a, b, c]" );
312
+
313
+ // #varargsObjectFunction: static String varargsObjectFunction(Object... args) -> Arrays.toString(args)
314
+ //
315
+ // Since ArrayHolder is an "instanceof Object" and Object is the varargs component type,
316
+ // we expect the ArrayHolder not to be converted to an array but rather to be passed
317
+ // "as is" as a single argument to the varargs method.
318
+ evaluate ("#varargsObjectFunction(#arrayHolder)" , "[" + arrayHolder .toString () + "]" );
319
+ }
320
+
321
+ @ Test
322
+ void functionWithVarargsAndPojoToArrayConversionViaMethodHandle () {
323
+ // #varargsFunctionHandle: static String varargsFunction(String... strings) -> Arrays.toString(strings)
324
+ evaluate ("#varargsFunctionHandle(#arrayHolder)" , "[a, b, c]" );
325
+
326
+ // #varargsObjectFunctionHandle: static String varargsObjectFunction(Object... args) -> Arrays.toString(args)
327
+ //
328
+ // Since ArrayHolder is an "instanceof Object" and Object is the varargs component type,
329
+ // we expect the ArrayHolder not to be converted to an array but rather to be passed
330
+ // "as is" as a single argument to the varargs method.
331
+ evaluate ("#varargsObjectFunctionHandle(#arrayHolder)" , "[" + arrayHolder .toString () + "]" );
332
+ }
333
+
334
+ private void evaluate (String expression , Object expectedValue ) {
335
+ Expression expr = parser .parseExpression (expression );
336
+ assertThat (expr ).as ("expression" ).isNotNull ();
337
+ Object value = expr .getValue (context );
338
+ assertThat (value ).as ("expression '" + expression + "'" ).isEqualTo (expectedValue );
339
+ }
340
+
341
+
342
+ record ArrayHolder (String ... array ) {
343
+ }
344
+
345
+ static class ArrayHolderConverter implements GenericConverter {
346
+
347
+ @ Nullable
348
+ @ Override
349
+ public Set <ConvertiblePair > getConvertibleTypes () {
350
+ return Set .of (new ConvertiblePair (ArrayHolder .class , Object [].class ));
351
+ }
352
+
353
+ @ Nullable
354
+ @ Override
355
+ public String [] convert (@ Nullable Object source , TypeDescriptor sourceType , TypeDescriptor targetType ) {
356
+ return ((ArrayHolder ) source ).array ();
357
+ }
358
+ }
359
+
360
+ }
361
+
282
362
}
0 commit comments