@@ -49,20 +49,20 @@ public abstract class ReflectionHelper {
49
49
50
50
/**
51
51
* Compare argument arrays and return information about whether they match.
52
- * <p>A supplied type converter and conversionAllowed flag allow for matches to take
53
- * into account that a type may be transformed into a different type by the converter.
52
+ * <p>The supplied type converter allows for matches to take into account that a type
53
+ * may be transformed into a different type by the converter.
54
54
* @param expectedArgTypes the types the method/constructor is expecting
55
55
* @param suppliedArgTypes the types that are being supplied at the point of invocation
56
56
* @param typeConverter a registered type converter
57
- * @return a MatchInfo object indicating what kind of match it was,
57
+ * @return an {@code ArgumentsMatchInfo} object indicating what kind of match it was,
58
58
* or {@code null} if it was not a match
59
59
*/
60
60
@ Nullable
61
61
static ArgumentsMatchInfo compareArguments (
62
62
List <TypeDescriptor > expectedArgTypes , List <TypeDescriptor > suppliedArgTypes , TypeConverter typeConverter ) {
63
63
64
64
Assert .isTrue (expectedArgTypes .size () == suppliedArgTypes .size (),
65
- "Expected argument types and supplied argument types should be arrays of same length " );
65
+ "Expected argument types and supplied argument types should be lists of the same size " );
66
66
67
67
ArgumentsMatchKind match = ArgumentsMatchKind .EXACT ;
68
68
for (int i = 0 ; i < expectedArgTypes .size () && match != null ; i ++) {
@@ -136,13 +136,14 @@ else if (ClassUtils.isAssignable(paramTypeClazz, superClass)) {
136
136
137
137
/**
138
138
* Compare argument arrays and return information about whether they match.
139
- * <p>A supplied type converter and conversionAllowed flag allow for matches to
140
- * take into account that a type may be transformed into a different type by the
141
- * converter. This variant of {@link #compareArguments} also allows for a varargs match.
139
+ * <p>The supplied type converter allows for matches to take into account that a type
140
+ * may be transformed into a different type by the converter.
141
+ * <p>This variant of {@link #compareArguments(List, List, TypeConverter)} also allows
142
+ * for a varargs match.
142
143
* @param expectedArgTypes the types the method/constructor is expecting
143
144
* @param suppliedArgTypes the types that are being supplied at the point of invocation
144
145
* @param typeConverter a registered type converter
145
- * @return a MatchInfo object indicating what kind of match it was,
146
+ * @return an {@code ArgumentsMatchInfo} object indicating what kind of match it was,
146
147
* or {@code null} if it was not a match
147
148
*/
148
149
@ Nullable
@@ -200,26 +201,26 @@ else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
200
201
// Now... we have the final argument in the method we are checking as a match and we have 0
201
202
// or more other arguments left to pass to it.
202
203
TypeDescriptor varargsDesc = expectedArgTypes .get (expectedArgTypes .size () - 1 );
203
- TypeDescriptor elementDesc = varargsDesc .getElementTypeDescriptor ();
204
- Assert .state (elementDesc != null , "No element type" );
205
- Class <?> varargsParamType = elementDesc .getType ();
204
+ TypeDescriptor componentTypeDesc = varargsDesc .getElementTypeDescriptor ();
205
+ Assert .state (componentTypeDesc != null , "Component type must not be null for a varargs array " );
206
+ Class <?> varargsComponentType = componentTypeDesc .getType ();
206
207
207
208
// All remaining parameters must be of this type or convertible to this type
208
209
for (int i = expectedArgTypes .size () - 1 ; i < suppliedArgTypes .size (); i ++) {
209
210
TypeDescriptor suppliedArg = suppliedArgTypes .get (i );
210
211
if (suppliedArg == null ) {
211
- if (varargsParamType .isPrimitive ()) {
212
+ if (varargsComponentType .isPrimitive ()) {
212
213
match = null ;
213
214
}
214
215
}
215
216
else {
216
- if (varargsParamType != suppliedArg .getType ()) {
217
- if (ClassUtils .isAssignable (varargsParamType , suppliedArg .getType ())) {
217
+ if (varargsComponentType != suppliedArg .getType ()) {
218
+ if (ClassUtils .isAssignable (varargsComponentType , suppliedArg .getType ())) {
218
219
if (match != ArgumentsMatchKind .REQUIRES_CONVERSION ) {
219
220
match = ArgumentsMatchKind .CLOSE ;
220
221
}
221
222
}
222
- else if (typeConverter .canConvert (suppliedArg , TypeDescriptor .valueOf (varargsParamType ))) {
223
+ else if (typeConverter .canConvert (suppliedArg , TypeDescriptor .valueOf (varargsComponentType ))) {
223
224
match = ArgumentsMatchKind .REQUIRES_CONVERSION ;
224
225
}
225
226
else {
@@ -234,25 +235,21 @@ else if (typeConverter.canConvert(suppliedArg, TypeDescriptor.valueOf(varargsPar
234
235
}
235
236
236
237
/**
237
- * Convert the supplied set of arguments into the parameter types specified
238
- * by the supplied {@link Method}.
239
- * <p>The arguments are converted 'in-place' in the input array.
240
- * <p>If the method accepts varargs, the final entry in its parameterTypes
241
- * array is going to be an array itself whose component type will be used as
242
- * the conversion target for any additional arguments. For example, if the
243
- * parameterTypes are {Integer, String[]} and the input arguments are
244
- * {Integer, boolean, float}, then both the boolean and float must be converted
245
- * to strings.
246
- * <p>This method does <strong>not</strong> repackage the arguments into a
247
- * form suitable for the varargs invocation. A subsequent call to
248
- * {@link #setupArgumentsForVarargsInvocation(Class[], Object...)} must be
249
- * used for that.
238
+ * Convert the supplied set of arguments into the parameter types of the supplied
239
+ * {@link Method}.
240
+ * <p>If the supplied method is a varargs method, the final parameter type must be an
241
+ * array whose component type should be used as the conversion target for extraneous
242
+ * arguments. For example, if the parameter types are <code>{Integer, String[]}</code>
243
+ * and the input arguments are <code>{Integer, boolean, float}</code>, then both the
244
+ * {@code boolean} and the {@code float} must be converted to strings.
245
+ * <p>This method does <strong>not</strong> repackage the arguments into a form suitable
246
+ * for the varargs invocation: a subsequent call to
247
+ * {@link #setupArgumentsForVarargsInvocation(Class[], Object...)} is required for that.
250
248
* @param converter the converter to use for type conversions
251
- * @param arguments the arguments to convert to the parameter types of the
252
- * target method
253
- * @param method the target method
254
- * @return true if some kind of conversion occurred on an argument
255
- * @throws SpelEvaluationException if there is a problem with conversion
249
+ * @param arguments the arguments to convert to the required parameter types
250
+ * @param method the target {@code Method}
251
+ * @return {@code true} if some kind of conversion occurred on an argument
252
+ * @throws SpelEvaluationException if a problem occurs during conversion
256
253
*/
257
254
public static boolean convertAllArguments (TypeConverter converter , Object [] arguments , Method method )
258
255
throws SpelEvaluationException {
@@ -262,12 +259,12 @@ public static boolean convertAllArguments(TypeConverter converter, Object[] argu
262
259
}
263
260
264
261
/**
265
- * Takes an input set of argument values and converts them to the parameter
266
- * types of the supplied {@link Executable} (i.e., constructor or method) .
262
+ * Convert the supplied set of arguments into the parameter types of the supplied
263
+ * {@link Executable}, taking the varargs position into account .
267
264
* <p>The arguments are converted 'in-place' in the input array.
268
- * @param converter the type converter to use for attempting conversions
269
- * @param arguments the actual arguments that need conversion
270
- * @param executable the target Method or Constructor
265
+ * @param converter the converter to use for type conversions
266
+ * @param arguments the arguments to convert to the required parameter types
267
+ * @param executable the target {@code Method} or {@code Constructor}
271
268
* @param varargsPosition the known position of the varargs argument, if any
272
269
* ({@code null} if not varargs)
273
270
* @return {@code true} if some kind of conversion occurred on an argument
@@ -295,30 +292,31 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe
295
292
}
296
293
297
294
MethodParameter methodParam = MethodParameter .forExecutable (executable , varargsPosition );
295
+ TypeDescriptor targetType = new TypeDescriptor (methodParam );
296
+ TypeDescriptor componentTypeDesc = targetType .getElementTypeDescriptor ();
297
+ Assert .state (componentTypeDesc != null , "Component type must not be null for a varargs array" );
298
298
299
299
// If the target is varargs and there is just one more argument, then convert it here.
300
300
if (varargsPosition == arguments .length - 1 ) {
301
301
Object argument = arguments [varargsPosition ];
302
- TypeDescriptor targetType = new TypeDescriptor (methodParam );
303
302
TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
304
303
if (argument == null ) {
305
304
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
306
- TypeDescriptor elementDesc = targetType .getElementTypeDescriptor ();
307
- if (elementDesc != null && elementDesc .getObjectType () == Optional .class ) {
305
+ if (componentTypeDesc .getObjectType () == Optional .class ) {
308
306
arguments [varargsPosition ] = Optional .empty ();
309
307
conversionOccurred = true ;
310
308
}
311
309
}
312
- // If the argument type is equal to the varargs element type, there is no need to
310
+ // If the argument type is assignable to the varargs component type, there is no need to
313
311
// convert it or wrap it in an array. For example, using StringToArrayConverter to
314
312
// convert a String containing a comma would result in the String being split and
315
313
// repackaged in an array when it should be used as-is.
316
- else if (!sourceType .equals ( targetType . getElementTypeDescriptor () )) {
314
+ else if (!sourceType .isAssignableTo ( componentTypeDesc )) {
317
315
arguments [varargsPosition ] = converter .convertValue (argument , sourceType , targetType );
318
316
}
319
317
// Possible outcomes of the above if-else block:
320
318
// 1) the input argument was null, and nothing was done.
321
- // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
319
+ // 2) the input argument was null; the varargs component type is Optional; and the argument was converted to Optional.empty().
322
320
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
323
321
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
324
322
// 5) the input argument was the wrong type and got converted and wrapped in an array.
@@ -327,13 +325,12 @@ else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
327
325
conversionOccurred = true ; // case 5
328
326
}
329
327
}
330
- // Otherwise, convert remaining arguments to the varargs element type.
328
+ // Otherwise, convert remaining arguments to the varargs component type.
331
329
else {
332
- TypeDescriptor targetType = new TypeDescriptor (methodParam ).getElementTypeDescriptor ();
333
- Assert .state (targetType != null , "No element type" );
334
330
for (int i = varargsPosition ; i < arguments .length ; i ++) {
335
331
Object argument = arguments [i ];
336
- arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), targetType );
332
+ TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
333
+ arguments [i ] = converter .convertValue (argument , sourceType , componentTypeDesc );
337
334
conversionOccurred |= (argument != arguments [i ]);
338
335
}
339
336
}
@@ -342,12 +339,12 @@ else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
342
339
}
343
340
344
341
/**
345
- * Takes an input set of argument values and converts them to the parameter
346
- * types of the supplied {@link MethodHandle}.
342
+ * Convert the supplied set of arguments into the parameter types of the supplied
343
+ * {@link MethodHandle}, taking the varargs position into account .
347
344
* <p>The arguments are converted 'in-place' in the input array.
348
- * @param converter the type converter to use for attempting conversions
349
- * @param arguments the actual arguments that need conversion
350
- * @param methodHandle the target MethodHandle
345
+ * @param converter the converter to use for type conversions
346
+ * @param arguments the arguments to convert to the required parameter types
347
+ * @param methodHandle the target {@code MethodHandle}
351
348
* @param varargsPosition the known position of the varargs argument, if any
352
349
* ({@code null} if not varargs)
353
350
* @return {@code true} if some kind of conversion occurred on an argument
@@ -358,7 +355,7 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
358
355
MethodHandle methodHandle , @ Nullable Integer varargsPosition ) throws EvaluationException {
359
356
360
357
boolean conversionOccurred = false ;
361
- final MethodType methodHandleArgumentTypes = methodHandle .type ();
358
+ MethodType methodHandleArgumentTypes = methodHandle .type ();
362
359
if (varargsPosition == null ) {
363
360
for (int i = 0 ; i < arguments .length ; i ++) {
364
361
Class <?> argumentClass = methodHandleArgumentTypes .parameterType (i );
@@ -382,32 +379,34 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
382
379
conversionOccurred |= (argument != arguments [i ]);
383
380
}
384
381
385
- final Class <?> varArgClass = methodHandleArgumentTypes .lastParameterType ().componentType ();
382
+ Class <?> varArgClass = methodHandleArgumentTypes .lastParameterType ().componentType ();
386
383
ResolvableType varArgResolvableType = ResolvableType .forClass (varArgClass );
387
- TypeDescriptor varArgContentType = new TypeDescriptor (varArgResolvableType , varArgClass , null );
384
+ TypeDescriptor varArgComponentType = new TypeDescriptor (varArgResolvableType , varArgClass , null );
385
+ TypeDescriptor componentTypeDesc = varArgComponentType .getElementTypeDescriptor ();
386
+ // TODO Determine why componentTypeDesc can be null.
387
+ // Assert.state(componentTypeDesc != null, "Component type must not be null for a varargs array");
388
388
389
389
// If the target is varargs and there is just one more argument, then convert it here.
390
390
if (varargsPosition == arguments .length - 1 ) {
391
391
Object argument = arguments [varargsPosition ];
392
392
TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
393
393
if (argument == null ) {
394
394
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
395
- TypeDescriptor elementDesc = varArgContentType .getElementTypeDescriptor ();
396
- if (elementDesc != null && elementDesc .getObjectType () == Optional .class ) {
395
+ if (componentTypeDesc != null && componentTypeDesc .getObjectType () == Optional .class ) {
397
396
arguments [varargsPosition ] = Optional .empty ();
398
397
conversionOccurred = true ;
399
398
}
400
399
}
401
- // If the argument type is equal to the varargs element type, there is no need to
400
+ // If the argument type is assignable to the varargs component type, there is no need to
402
401
// convert it or wrap it in an array. For example, using StringToArrayConverter to
403
402
// convert a String containing a comma would result in the String being split and
404
403
// repackaged in an array when it should be used as-is.
405
- else if (! sourceType .equals ( varArgContentType . getElementTypeDescriptor () )) {
406
- arguments [varargsPosition ] = converter .convertValue (argument , sourceType , varArgContentType );
404
+ else if (componentTypeDesc != null && ! sourceType .isAssignableTo ( componentTypeDesc )) {
405
+ arguments [varargsPosition ] = converter .convertValue (argument , sourceType , varArgComponentType );
407
406
}
408
407
// Possible outcomes of the above if-else block:
409
408
// 1) the input argument was null, and nothing was done.
410
- // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
409
+ // 2) the input argument was null; the varargs component type is Optional; and the argument was converted to Optional.empty().
411
410
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
412
411
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
413
412
// 5) the input argument was the wrong type and got converted and wrapped in an array.
@@ -416,11 +415,11 @@ else if (!sourceType.equals(varArgContentType.getElementTypeDescriptor())) {
416
415
conversionOccurred = true ; // case 5
417
416
}
418
417
}
419
- // Otherwise, convert remaining arguments to the varargs element type.
418
+ // Otherwise, convert remaining arguments to the varargs component type.
420
419
else {
421
420
for (int i = varargsPosition ; i < arguments .length ; i ++) {
422
421
Object argument = arguments [i ];
423
- arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), varArgContentType );
422
+ arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), varArgComponentType );
424
423
conversionOccurred |= (argument != arguments [i ]);
425
424
}
426
425
}
@@ -456,7 +455,7 @@ private static boolean isFirstEntryInArray(Object value, @Nullable Object possib
456
455
* {@code [1, new String[] {"a", "b"}]} in order to match the expected types.
457
456
* @param requiredParameterTypes the types of the parameters for the invocation
458
457
* @param args the arguments to be set up for the invocation
459
- * @return a repackaged array of arguments where any varargs setup has performed
458
+ * @return a repackaged array of arguments where any varargs setup has been performed
460
459
*/
461
460
public static Object [] setupArgumentsForVarargsInvocation (Class <?>[] requiredParameterTypes , Object ... args ) {
462
461
Assert .notEmpty (requiredParameterTypes , "Required parameter types array must not be empty" );
@@ -513,11 +512,9 @@ enum ArgumentsMatchKind {
513
512
514
513
515
514
/**
516
- * An instance of ArgumentsMatchInfo describes what kind of match was achieved
515
+ * An instance of {@code ArgumentsMatchInfo} describes what kind of match was achieved
517
516
* between two sets of arguments - the set that a method/constructor is expecting
518
- * and the set that are being supplied at the point of invocation. If the kind
519
- * indicates that conversion is required for some of the arguments then the arguments
520
- * that require conversion are listed in the argsRequiringConversion array.
517
+ * and the set that is being supplied at the point of invocation.
521
518
*
522
519
* @param kind the kind of match that was achieved
523
520
*/
@@ -537,7 +534,7 @@ public boolean isMatchRequiringConversion() {
537
534
538
535
@ Override
539
536
public String toString () {
540
- return "ArgumentMatchInfo : " + this .kind ;
537
+ return "ArgumentsMatchInfo : " + this .kind ;
541
538
}
542
539
}
543
540
0 commit comments