001/*
002 * Copyright 2006-2010 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.springframework.batch.item.adapter;
017
018import java.lang.reflect.Method;
019
020import org.springframework.util.ClassUtils;
021import org.springframework.util.MethodInvoker;
022import org.springframework.util.ReflectionUtils;
023
024/**
025 * A {@link MethodInvoker} that is a bit relaxed about its arguments. You can
026 * give it arguments in the wrong order or you can give it too many arguments
027 * and it will try and find a method that matches a subset.
028 * 
029 * @author Dave Syer
030 * 
031 * @since 2.1
032 */
033public class HippyMethodInvoker extends MethodInvoker {
034
035        @Override
036        protected Method findMatchingMethod() {
037                String targetMethod = getTargetMethod();
038                Object[] arguments = getArguments();
039                int argCount = arguments.length;
040
041                Method[] candidates = ReflectionUtils.getAllDeclaredMethods(getTargetClass());
042                int minTypeDiffWeight = Integer.MAX_VALUE;
043                Method matchingMethod = null;
044
045                Object[] transformedArguments = null;
046                int transformedArgumentCount = 0;
047
048                for (int i = 0; i < candidates.length; i++) {
049                        Method candidate = candidates[i];
050                        if (candidate.getName().equals(targetMethod)) {
051                                Class<?>[] paramTypes = candidate.getParameterTypes();
052                                Object[] candidateArguments = new Object[paramTypes.length];
053                                int assignedParameterCount = 0;
054                                boolean assigned = paramTypes.length==0;
055                                for (int j = 0; j < arguments.length; j++) {
056                                        for (int k = 0; k < paramTypes.length; k++) {
057                                                // Pick the first assignable of the right type that
058                                                // matches this slot and hasn't already been filled...
059                                                if (ClassUtils.isAssignableValue(paramTypes[k], arguments[j]) && candidateArguments[k] == null) {
060                                                        candidateArguments[k] = arguments[j];
061                                                        assignedParameterCount++;
062                                                        assigned = true;
063                                                        break;
064                                                }
065                                        }
066                                }
067                                if (assigned && paramTypes.length <= argCount) {
068                                        int typeDiffWeight = getTypeDifferenceWeight(paramTypes, candidateArguments);
069                                        if (typeDiffWeight < minTypeDiffWeight) {
070                                                minTypeDiffWeight = typeDiffWeight;
071                                                matchingMethod = candidate;
072                                                transformedArguments = candidateArguments;
073                                                transformedArgumentCount = assignedParameterCount;
074                                        }
075                                }
076                        }
077                }
078
079                if (transformedArguments == null) {
080                        throw new IllegalArgumentException("No matching arguments found for method: " + targetMethod);
081                }
082
083                if (transformedArgumentCount < transformedArguments.length) {
084                        throw new IllegalArgumentException("Only " + transformedArgumentCount + " out of "
085                                        + transformedArguments.length + " arguments could be assigned.");
086                }
087
088                setArguments(transformedArguments);
089                return matchingMethod;
090
091        }
092}