@@ -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
- * 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
- * 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 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,19 +235,22 @@ else if (typeConverter.canConvert(suppliedArg, TypeDescriptor.valueOf(varargsPar
234
235
}
235
236
236
237
237
- // TODO could do with more refactoring around argument handling and varargs
238
238
/**
239
- * Convert a supplied set of arguments into the requested types. If the parameterTypes are related to
240
- * a varargs method then the final entry in the parameterTypes array is going to be an array itself whose
241
- * component type should be used as the conversion target for extraneous arguments. (For example, if the
242
- * parameterTypes are {Integer, String[]} and the input arguments are {Integer, boolean, float} then both
243
- * the boolean and float must be converted to strings). This method does *not* repackage the arguments
244
- * into a form suitable for the varargs invocation - a subsequent call to setupArgumentsForVarargsInvocation handles that.
239
+ * Convert the supplied set of arguments into the parameter types of the supplied
240
+ * {@link Method}.
241
+ * <p>If the supplied method is a varargs method, the final parameter type must be an
242
+ * array whose component type should be used as the conversion target for extraneous
243
+ * arguments. For example, if the parameter types are <code>{Integer, String[]}</code>
244
+ * and the input arguments are <code>{Integer, boolean, float}</code>, then both the
245
+ * {@code boolean} and the {@code float} must be converted to strings.
246
+ * <p>This method does <strong>not</strong> repackage the arguments into a form suitable
247
+ * for the varargs invocation: a subsequent call to
248
+ * {@link #setupArgumentsForVarargsInvocation(Class[], Object...)} is required for that.
245
249
* @param converter the converter to use for type conversions
246
- * @param arguments the arguments to convert to the requested parameter types
247
- * @param method the target Method
248
- * @return true if some kind of conversion occurred on the argument
249
- * @throws SpelEvaluationException if there is a problem with conversion
250
+ * @param arguments the arguments to convert to the required parameter types
251
+ * @param method the target {@code Method}
252
+ * @return {@code true} if some kind of conversion occurred on an argument
253
+ * @throws SpelEvaluationException if a problem occurs during conversion
250
254
*/
251
255
public static boolean convertAllArguments (TypeConverter converter , Object [] arguments , Method method )
252
256
throws SpelEvaluationException {
@@ -256,11 +260,12 @@ public static boolean convertAllArguments(TypeConverter converter, Object[] argu
256
260
}
257
261
258
262
/**
259
- * Takes an input set of argument values and converts them to the types specified as the
260
- * required parameter types. The arguments are converted 'in-place' in the input array.
261
- * @param converter the type converter to use for attempting conversions
262
- * @param arguments the actual arguments that need conversion
263
- * @param executable the target Method or Constructor
263
+ * Convert the supplied set of arguments into the parameter types of the supplied
264
+ * {@link Executable}, taking the varargs position into account.
265
+ * <p>The arguments are converted 'in-place' in the input array.
266
+ * @param converter the converter to use for type conversions
267
+ * @param arguments the arguments to convert to the required parameter types
268
+ * @param executable the target {@code Method} or {@code Constructor}
264
269
* @param varargsPosition the known position of the varargs argument, if any
265
270
* ({@code null} if not varargs)
266
271
* @return {@code true} if some kind of conversion occurred on an argument
@@ -288,30 +293,31 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe
288
293
}
289
294
290
295
MethodParameter methodParam = MethodParameter .forExecutable (executable , varargsPosition );
296
+ TypeDescriptor targetType = new TypeDescriptor (methodParam );
297
+ TypeDescriptor componentTypeDesc = targetType .getElementTypeDescriptor ();
298
+ Assert .state (componentTypeDesc != null , "Component type must not be null for a varargs array" );
291
299
292
300
// If the target is varargs and there is just one more argument, then convert it here.
293
301
if (varargsPosition == arguments .length - 1 ) {
294
302
Object argument = arguments [varargsPosition ];
295
- TypeDescriptor targetType = new TypeDescriptor (methodParam );
296
303
TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
297
304
if (argument == null ) {
298
305
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
299
- TypeDescriptor elementDesc = targetType .getElementTypeDescriptor ();
300
- if (elementDesc != null && elementDesc .getObjectType () == Optional .class ) {
306
+ if (componentTypeDesc .getObjectType () == Optional .class ) {
301
307
arguments [varargsPosition ] = Optional .empty ();
302
308
conversionOccurred = true ;
303
309
}
304
310
}
305
- // If the argument type is equal to the varargs element type, there is no need to
311
+ // If the argument type is equal to the varargs component type, there is no need to
306
312
// convert it or wrap it in an array. For example, using StringToArrayConverter to
307
313
// convert a String containing a comma would result in the String being split and
308
314
// repackaged in an array when it should be used as-is.
309
- else if (!sourceType .equals (targetType . getElementTypeDescriptor () )) {
315
+ else if (!sourceType .equals (componentTypeDesc )) {
310
316
arguments [varargsPosition ] = converter .convertValue (argument , sourceType , targetType );
311
317
}
312
318
// Possible outcomes of the above if-else block:
313
319
// 1) the input argument was null, and nothing was done.
314
- // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
320
+ // 2) the input argument was null; the varargs component type is Optional; and the argument was converted to Optional.empty().
315
321
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
316
322
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
317
323
// 5) the input argument was the wrong type and got converted and wrapped in an array.
@@ -320,13 +326,12 @@ else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
320
326
conversionOccurred = true ; // case 5
321
327
}
322
328
}
323
- // Otherwise, convert remaining arguments to the varargs element type.
329
+ // Otherwise, convert remaining arguments to the varargs component type.
324
330
else {
325
- TypeDescriptor targetType = new TypeDescriptor (methodParam ).getElementTypeDescriptor ();
326
- Assert .state (targetType != null , "No element type" );
327
331
for (int i = varargsPosition ; i < arguments .length ; i ++) {
328
332
Object argument = arguments [i ];
329
- arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), targetType );
333
+ TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
334
+ arguments [i ] = converter .convertValue (argument , sourceType , componentTypeDesc );
330
335
conversionOccurred |= (argument != arguments [i ]);
331
336
}
332
337
}
@@ -335,11 +340,12 @@ else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
335
340
}
336
341
337
342
/**
338
- * Takes an input set of argument values and converts them to the types specified as the
339
- * required parameter types. The arguments are converted 'in-place' in the input array.
340
- * @param converter the type converter to use for attempting conversions
341
- * @param arguments the actual arguments that need conversion
342
- * @param methodHandle the target MethodHandle
343
+ * Convert the supplied set of arguments into the parameter types of the supplied
344
+ * {@link MethodHandle}, taking the varargs position into account.
345
+ * <p>The arguments are converted 'in-place' in the input array.
346
+ * @param converter the converter to use for type conversions
347
+ * @param arguments the arguments to convert to the required parameter types
348
+ * @param methodHandle the target {@code MethodHandle}
343
349
* @param varargsPosition the known position of the varargs argument, if any
344
350
* ({@code null} if not varargs)
345
351
* @return {@code true} if some kind of conversion occurred on an argument
@@ -350,7 +356,7 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
350
356
MethodHandle methodHandle , @ Nullable Integer varargsPosition ) throws EvaluationException {
351
357
352
358
boolean conversionOccurred = false ;
353
- final MethodType methodHandleArgumentTypes = methodHandle .type ();
359
+ MethodType methodHandleArgumentTypes = methodHandle .type ();
354
360
if (varargsPosition == null ) {
355
361
for (int i = 0 ; i < arguments .length ; i ++) {
356
362
Class <?> argumentClass = methodHandleArgumentTypes .parameterType (i );
@@ -374,32 +380,34 @@ public static boolean convertAllMethodHandleArguments(TypeConverter converter, O
374
380
conversionOccurred |= (argument != arguments [i ]);
375
381
}
376
382
377
- final Class <?> varArgClass = methodHandleArgumentTypes .lastParameterType ().componentType ();
383
+ Class <?> varArgClass = methodHandleArgumentTypes .lastParameterType ().componentType ();
378
384
ResolvableType varArgResolvableType = ResolvableType .forClass (varArgClass );
379
- TypeDescriptor varArgContentType = new TypeDescriptor (varArgResolvableType , varArgClass , null );
385
+ TypeDescriptor varArgComponentType = new TypeDescriptor (varArgResolvableType , varArgClass , null );
386
+ TypeDescriptor componentTypeDesc = varArgComponentType .getElementTypeDescriptor ();
387
+ // TODO Determine why componentTypeDesc is null.
388
+ // Assert.state(componentTypeDesc != null, "Component type must not be null for a varargs array");
380
389
381
390
// If the target is varargs and there is just one more argument, then convert it here.
382
391
if (varargsPosition == arguments .length - 1 ) {
383
392
Object argument = arguments [varargsPosition ];
384
393
TypeDescriptor sourceType = TypeDescriptor .forObject (argument );
385
394
if (argument == null ) {
386
395
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
387
- TypeDescriptor elementDesc = varArgContentType .getElementTypeDescriptor ();
388
- if (elementDesc != null && elementDesc .getObjectType () == Optional .class ) {
396
+ if (componentTypeDesc != null && componentTypeDesc .getObjectType () == Optional .class ) {
389
397
arguments [varargsPosition ] = Optional .empty ();
390
398
conversionOccurred = true ;
391
399
}
392
400
}
393
- // If the argument type is equal to the varargs element type, there is no need to
401
+ // If the argument type is equal to the varargs component type, there is no need to
394
402
// convert it or wrap it in an array. For example, using StringToArrayConverter to
395
403
// convert a String containing a comma would result in the String being split and
396
404
// repackaged in an array when it should be used as-is.
397
- else if (!sourceType .equals (varArgContentType . getElementTypeDescriptor () )) {
398
- arguments [varargsPosition ] = converter .convertValue (argument , sourceType , varArgContentType );
405
+ else if (!sourceType .equals (componentTypeDesc )) {
406
+ arguments [varargsPosition ] = converter .convertValue (argument , sourceType , varArgComponentType );
399
407
}
400
408
// Possible outcomes of the above if-else block:
401
409
// 1) the input argument was null, and nothing was done.
402
- // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
410
+ // 2) the input argument was null; the varargs component type is Optional; and the argument was converted to Optional.empty().
403
411
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
404
412
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
405
413
// 5) the input argument was the wrong type and got converted and wrapped in an array.
@@ -408,11 +416,11 @@ else if (!sourceType.equals(varArgContentType.getElementTypeDescriptor())) {
408
416
conversionOccurred = true ; // case 5
409
417
}
410
418
}
411
- // Otherwise, convert remaining arguments to the varargs element type.
419
+ // Otherwise, convert remaining arguments to the varargs component type.
412
420
else {
413
421
for (int i = varargsPosition ; i < arguments .length ; i ++) {
414
422
Object argument = arguments [i ];
415
- arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), varArgContentType );
423
+ arguments [i ] = converter .convertValue (argument , TypeDescriptor .forObject (argument ), varArgComponentType );
416
424
conversionOccurred |= (argument != arguments [i ]);
417
425
}
418
426
}
@@ -448,7 +456,7 @@ private static boolean isFirstEntryInArray(Object value, @Nullable Object possib
448
456
* {@code [1, new String[] {"a", "b"}]} in order to match the expected types.
449
457
* @param requiredParameterTypes the types of the parameters for the invocation
450
458
* @param args the arguments to be set up for the invocation
451
- * @return a repackaged array of arguments where any varargs setup has performed
459
+ * @return a repackaged array of arguments where any varargs setup has been performed
452
460
*/
453
461
public static Object [] setupArgumentsForVarargsInvocation (Class <?>[] requiredParameterTypes , Object ... args ) {
454
462
Assert .notEmpty (requiredParameterTypes , "Required parameter types array must not be empty" );
@@ -505,11 +513,9 @@ enum ArgumentsMatchKind {
505
513
506
514
507
515
/**
508
- * An instance of ArgumentsMatchInfo describes what kind of match was achieved
516
+ * An instance of {@code ArgumentsMatchInfo} describes what kind of match was achieved
509
517
* between two sets of arguments - the set that a method/constructor is expecting
510
- * and the set that are being supplied at the point of invocation. If the kind
511
- * indicates that conversion is required for some of the arguments then the arguments
512
- * that require conversion are listed in the argsRequiringConversion array.
518
+ * and the set that is being supplied at the point of invocation.
513
519
*
514
520
* @param kind the kind of match that was achieved
515
521
*/
@@ -529,7 +535,7 @@ public boolean isMatchRequiringConversion() {
529
535
530
536
@ Override
531
537
public String toString () {
532
- return "ArgumentMatchInfo : " + this .kind ;
538
+ return "ArgumentsMatchInfo : " + this .kind ;
533
539
}
534
540
}
535
541
0 commit comments