1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.support;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.Method;
21 import java.util.List;
22
23 import org.springframework.core.MethodParameter;
24 import org.springframework.core.convert.TypeDescriptor;
25 import org.springframework.expression.EvaluationException;
26 import org.springframework.expression.TypeConverter;
27 import org.springframework.expression.spel.SpelEvaluationException;
28 import org.springframework.util.Assert;
29 import org.springframework.util.ClassUtils;
30 import org.springframework.util.MethodInvoker;
31
32
33
34
35
36
37
38
39
40 public class ReflectionHelper {
41
42
43
44
45
46
47
48
49
50
51
52 static ArgumentsMatchInfo compareArguments(
53 List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
54
55 Assert.isTrue(expectedArgTypes.size() == suppliedArgTypes.size(),
56 "Expected argument types and supplied argument types should be arrays of same length");
57
58 ArgumentsMatchKind match = ArgumentsMatchKind.EXACT;
59 for (int i = 0; i < expectedArgTypes.size() && match != null; i++) {
60 TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
61 TypeDescriptor expectedArg = expectedArgTypes.get(i);
62 if (!expectedArg.equals(suppliedArg)) {
63
64 if (suppliedArg == null) {
65 if (expectedArg.isPrimitive()) {
66 match = null;
67 }
68 }
69 else {
70 if (suppliedArg.isAssignableTo(expectedArg)) {
71 if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) {
72 match = ArgumentsMatchKind.CLOSE;
73 }
74 }
75 else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
76 match = ArgumentsMatchKind.REQUIRES_CONVERSION;
77 }
78 else {
79 match = null;
80 }
81 }
82 }
83 }
84 return (match != null ? new ArgumentsMatchInfo(match) : null);
85 }
86
87
88
89
90 public static int getTypeDifferenceWeight(List<TypeDescriptor> paramTypes, List<TypeDescriptor> argTypes) {
91 int result = 0;
92 for (int i = 0; i < paramTypes.size(); i++) {
93 TypeDescriptor paramType = paramTypes.get(i);
94 TypeDescriptor argType = (i < argTypes.size() ? argTypes.get(i) : null);
95 if (argType == null) {
96 if (paramType.isPrimitive()) {
97 return Integer.MAX_VALUE;
98 }
99 }
100 else {
101 Class<?> paramTypeClazz = paramType.getType();
102 if (!ClassUtils.isAssignable(paramTypeClazz, argType.getType())) {
103 return Integer.MAX_VALUE;
104 }
105 if (paramTypeClazz.isPrimitive()) {
106 paramTypeClazz = Object.class;
107 }
108 Class<?> superClass = argType.getType().getSuperclass();
109 while (superClass != null) {
110 if (paramTypeClazz.equals(superClass)) {
111 result = result + 2;
112 superClass = null;
113 }
114 else if (ClassUtils.isAssignable(paramTypeClazz, superClass)) {
115 result = result + 2;
116 superClass = superClass.getSuperclass();
117 }
118 else {
119 superClass = null;
120 }
121 }
122 if (paramTypeClazz.isInterface()) {
123 result = result + 1;
124 }
125 }
126 }
127 return result;
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 static ArgumentsMatchInfo compareArgumentsVarargs(
142 List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
143
144 Assert.isTrue(expectedArgTypes != null && expectedArgTypes.size() > 0,
145 "Expected arguments must at least include one array (the vargargs parameter)");
146 Assert.isTrue(expectedArgTypes.get(expectedArgTypes.size() - 1).isArray(),
147 "Final expected argument should be array type (the varargs parameter)");
148
149 ArgumentsMatchKind match = ArgumentsMatchKind.EXACT;
150
151
152
153
154 int argCountUpToVarargs = expectedArgTypes.size() - 1;
155 for (int i = 0; i < argCountUpToVarargs && match != null; i++) {
156 TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
157 TypeDescriptor expectedArg = expectedArgTypes.get(i);
158 if (suppliedArg == null) {
159 if (expectedArg.isPrimitive()) {
160 match = null;
161 }
162 }
163 else {
164 if (!expectedArg.equals(suppliedArg)) {
165 if (suppliedArg.isAssignableTo(expectedArg)) {
166 if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) {
167 match = ArgumentsMatchKind.CLOSE;
168 }
169 }
170 else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
171 match = ArgumentsMatchKind.REQUIRES_CONVERSION;
172 }
173 else {
174 match = null;
175 }
176 }
177 }
178 }
179
180
181 if (match == null) {
182 return null;
183 }
184
185 if (suppliedArgTypes.size() == expectedArgTypes.size() &&
186 expectedArgTypes.get(expectedArgTypes.size() - 1).equals(
187 suppliedArgTypes.get(suppliedArgTypes.size() - 1))) {
188
189
190 }
191 else {
192
193
194 TypeDescriptor varargsDesc = expectedArgTypes.get(expectedArgTypes.size() - 1);
195 Class<?> varargsParamType = varargsDesc.getElementTypeDescriptor().getType();
196
197
198 for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) {
199 TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
200 if (suppliedArg == null) {
201 if (varargsParamType.isPrimitive()) {
202 match = null;
203 }
204 }
205 else {
206 if (varargsParamType != suppliedArg.getType()) {
207 if (ClassUtils.isAssignable(varargsParamType, suppliedArg.getType())) {
208 if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) {
209 match = ArgumentsMatchKind.CLOSE;
210 }
211 }
212 else if (typeConverter.canConvert(suppliedArg, TypeDescriptor.valueOf(varargsParamType))) {
213 match = ArgumentsMatchKind.REQUIRES_CONVERSION;
214 }
215 else {
216 match = null;
217 }
218 }
219 }
220 }
221 }
222
223 return (match != null ? new ArgumentsMatchInfo(match) : null);
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 public static boolean convertAllArguments(TypeConverter converter, Object[] arguments, Method method) throws SpelEvaluationException {
242 Integer varargsPosition = method.isVarArgs() ? method.getParameterTypes().length-1:null;
243 return convertArguments(converter, arguments, method, varargsPosition);
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257 static boolean convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor,
258 Integer varargsPosition) throws EvaluationException {
259
260 boolean conversionOccurred = false;
261 if (varargsPosition == null) {
262 for (int i = 0; i < arguments.length; i++) {
263 TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i));
264 Object argument = arguments[i];
265 arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
266 conversionOccurred |= (argument != arguments[i]);
267 }
268 }
269 else {
270
271 for (int i = 0; i < varargsPosition; i++) {
272 TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i));
273 Object argument = arguments[i];
274 arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
275 conversionOccurred |= (argument != arguments[i]);
276 }
277 MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition);
278 if (varargsPosition == arguments.length - 1) {
279
280
281 TypeDescriptor targetType = new TypeDescriptor(methodParam);
282 Object argument = arguments[varargsPosition];
283 TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
284 arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
285
286
287
288
289 if (argument != arguments[varargsPosition] &&
290 !isFirstEntryInArray(argument, arguments[varargsPosition])) {
291 conversionOccurred = true;
292 }
293 }
294 else {
295
296 TypeDescriptor targetType = new TypeDescriptor(methodParam).getElementTypeDescriptor();
297 for (int i = varargsPosition; i < arguments.length; i++) {
298 Object argument = arguments[i];
299 arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
300 conversionOccurred |= (argument != arguments[i]);
301 }
302 }
303 }
304 return conversionOccurred;
305 }
306
307
308
309
310
311
312
313 private static boolean isFirstEntryInArray(Object value, Object possibleArray) {
314 if (possibleArray == null) {
315 return false;
316 }
317 Class<?> type = possibleArray.getClass();
318 if (!type.isArray() || Array.getLength(possibleArray) == 0 ||
319 !ClassUtils.isAssignableValue(type.getComponentType(), value)) {
320 return false;
321 }
322 Object arrayValue = Array.get(possibleArray, 0);
323 return (type.getComponentType().isPrimitive() ? arrayValue.equals(value) : arrayValue == value);
324 }
325
326
327
328
329
330
331
332
333
334
335 public static Object[] setupArgumentsForVarargsInvocation(Class<?>[] requiredParameterTypes, Object... args) {
336
337 int parameterCount = requiredParameterTypes.length;
338 int argumentCount = args.length;
339
340
341 if (parameterCount != args.length ||
342 requiredParameterTypes[parameterCount - 1] !=
343 (args[argumentCount - 1] != null ? args[argumentCount - 1].getClass() : null)) {
344
345 int arraySize = 0;
346 if (argumentCount >= parameterCount) {
347 arraySize = argumentCount - (parameterCount - 1);
348 }
349
350
351 Object[] newArgs = new Object[parameterCount];
352 System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1);
353
354
355
356 Class<?> componentType = requiredParameterTypes[parameterCount - 1].getComponentType();
357 Object repackagedArgs = Array.newInstance(componentType, arraySize);
358 for (int i = 0; i < arraySize; i++) {
359 Array.set(repackagedArgs, i, args[parameterCount - 1 + i]);
360 }
361 newArgs[newArgs.length - 1] = repackagedArgs;
362 return newArgs;
363 }
364 return args;
365 }
366
367
368 static enum ArgumentsMatchKind {
369
370
371 EXACT,
372
373
374 CLOSE,
375
376
377 REQUIRES_CONVERSION
378 }
379
380
381
382
383
384
385
386
387
388 static class ArgumentsMatchInfo {
389
390 private final ArgumentsMatchKind kind;
391
392 ArgumentsMatchInfo(ArgumentsMatchKind kind) {
393 this.kind = kind;
394 }
395
396 public boolean isExactMatch() {
397 return (this.kind == ArgumentsMatchKind.EXACT);
398 }
399
400 public boolean isCloseMatch() {
401 return (this.kind == ArgumentsMatchKind.CLOSE);
402 }
403
404 public boolean isMatchRequiringConversion() {
405 return (this.kind == ArgumentsMatchKind.REQUIRES_CONVERSION);
406 }
407
408 @Override
409 public String toString() {
410 return "ArgumentMatchInfo: " + this.kind;
411 }
412 }
413
414 }