1 | /* |
2 | * Copyright 2006-2010 the original author or authors. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.springframework.batch.item.adapter; |
17 | |
18 | import java.lang.reflect.Method; |
19 | |
20 | import org.springframework.util.ClassUtils; |
21 | import org.springframework.util.MethodInvoker; |
22 | import org.springframework.util.ReflectionUtils; |
23 | |
24 | /** |
25 | * A {@link MethodInvoker} that is a bit relaxed about its arguments. You can |
26 | * give it arguments in the wrong order or you can give it too many arguments |
27 | * and it will try and find a method that matches a subset. |
28 | * |
29 | * @author Dave Syer |
30 | * |
31 | * @since 2.1 |
32 | */ |
33 | public class HippyMethodInvoker extends MethodInvoker { |
34 | |
35 | @Override |
36 | protected Method findMatchingMethod() { |
37 | String targetMethod = getTargetMethod(); |
38 | Object[] arguments = getArguments(); |
39 | int argCount = arguments.length; |
40 | |
41 | Method[] candidates = ReflectionUtils.getAllDeclaredMethods(getTargetClass()); |
42 | int minTypeDiffWeight = Integer.MAX_VALUE; |
43 | Method matchingMethod = null; |
44 | |
45 | Object[] transformedArguments = null; |
46 | int transformedArgumentCount = 0; |
47 | |
48 | for (int i = 0; i < candidates.length; i++) { |
49 | Method candidate = candidates[i]; |
50 | if (candidate.getName().equals(targetMethod)) { |
51 | Class<?>[] paramTypes = candidate.getParameterTypes(); |
52 | Object[] candidateArguments = new Object[paramTypes.length]; |
53 | int assignedParameterCount = 0; |
54 | boolean assigned = paramTypes.length==0; |
55 | for (int j = 0; j < arguments.length; j++) { |
56 | for (int k = 0; k < paramTypes.length; k++) { |
57 | // Pick the first assignable of the right type that |
58 | // matches this slot and hasn't already been filled... |
59 | if (ClassUtils.isAssignableValue(paramTypes[k], arguments[j]) && candidateArguments[k] == null) { |
60 | candidateArguments[k] = arguments[j]; |
61 | assignedParameterCount++; |
62 | assigned = true; |
63 | break; |
64 | } |
65 | } |
66 | } |
67 | if (assigned && paramTypes.length <= argCount) { |
68 | int typeDiffWeight = getTypeDifferenceWeight(paramTypes, candidateArguments); |
69 | if (typeDiffWeight < minTypeDiffWeight) { |
70 | minTypeDiffWeight = typeDiffWeight; |
71 | matchingMethod = candidate; |
72 | transformedArguments = candidateArguments; |
73 | transformedArgumentCount = assignedParameterCount; |
74 | } |
75 | } |
76 | } |
77 | } |
78 | |
79 | if (transformedArguments == null) { |
80 | throw new IllegalArgumentException("No matching arguments found for method: " + targetMethod); |
81 | } |
82 | |
83 | if (transformedArgumentCount < transformedArguments.length) { |
84 | throw new IllegalArgumentException("Only " + transformedArgumentCount + " out of " |
85 | + transformedArguments.length + " arguments could be assigned."); |
86 | } |
87 | |
88 | setArguments(transformedArguments); |
89 | return matchingMethod; |
90 | |
91 | } |
92 | } |