001/* 002 * Copyright 2002-2012 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 */ 016 017package org.springframework.beans.support; 018 019import java.beans.PropertyEditor; 020import java.lang.reflect.Method; 021 022import org.springframework.beans.PropertyEditorRegistry; 023import org.springframework.beans.SimpleTypeConverter; 024import org.springframework.beans.TypeConverter; 025import org.springframework.beans.TypeMismatchException; 026import org.springframework.util.MethodInvoker; 027import org.springframework.util.ReflectionUtils; 028 029/** 030 * Subclass of {@link MethodInvoker} that tries to convert the given 031 * arguments for the actual target method via a {@link TypeConverter}. 032 * 033 * <p>Supports flexible argument conversions, in particular for 034 * invoking a specific overloaded method. 035 * 036 * @author Juergen Hoeller 037 * @since 1.1 038 * @see org.springframework.beans.BeanWrapperImpl#convertIfNecessary 039 */ 040public class ArgumentConvertingMethodInvoker extends MethodInvoker { 041 042 private TypeConverter typeConverter; 043 044 private boolean useDefaultConverter = true; 045 046 047 /** 048 * Set a TypeConverter to use for argument type conversion. 049 * <p>Default is a {@link org.springframework.beans.SimpleTypeConverter}. 050 * Can be overridden with any TypeConverter implementation, typically 051 * a pre-configured SimpleTypeConverter or a BeanWrapperImpl instance. 052 * @see org.springframework.beans.SimpleTypeConverter 053 * @see org.springframework.beans.BeanWrapperImpl 054 */ 055 public void setTypeConverter(TypeConverter typeConverter) { 056 this.typeConverter = typeConverter; 057 this.useDefaultConverter = false; 058 } 059 060 /** 061 * Return the TypeConverter used for argument type conversion. 062 * <p>Can be cast to {@link org.springframework.beans.PropertyEditorRegistry} 063 * if direct access to the underlying PropertyEditors is desired 064 * (provided that the present TypeConverter actually implements the 065 * PropertyEditorRegistry interface). 066 */ 067 public TypeConverter getTypeConverter() { 068 if (this.typeConverter == null && this.useDefaultConverter) { 069 this.typeConverter = getDefaultTypeConverter(); 070 } 071 return this.typeConverter; 072 } 073 074 /** 075 * Obtain the default TypeConverter for this method invoker. 076 * <p>Called if no explicit TypeConverter has been specified. 077 * The default implementation builds a 078 * {@link org.springframework.beans.SimpleTypeConverter}. 079 * Can be overridden in subclasses. 080 */ 081 protected TypeConverter getDefaultTypeConverter() { 082 return new SimpleTypeConverter(); 083 } 084 085 /** 086 * Register the given custom property editor for all properties of the given type. 087 * <p>Typically used in conjunction with the default 088 * {@link org.springframework.beans.SimpleTypeConverter}; will work with any 089 * TypeConverter that implements the PropertyEditorRegistry interface as well. 090 * @param requiredType type of the property 091 * @param propertyEditor editor to register 092 * @see #setTypeConverter 093 * @see org.springframework.beans.PropertyEditorRegistry#registerCustomEditor 094 */ 095 public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) { 096 TypeConverter converter = getTypeConverter(); 097 if (!(converter instanceof PropertyEditorRegistry)) { 098 throw new IllegalStateException( 099 "TypeConverter does not implement PropertyEditorRegistry interface: " + converter); 100 } 101 ((PropertyEditorRegistry) converter).registerCustomEditor(requiredType, propertyEditor); 102 } 103 104 105 /** 106 * This implementation looks for a method with matching parameter types. 107 * @see #doFindMatchingMethod 108 */ 109 @Override 110 protected Method findMatchingMethod() { 111 Method matchingMethod = super.findMatchingMethod(); 112 // Second pass: look for method where arguments can be converted to parameter types. 113 if (matchingMethod == null) { 114 // Interpret argument array as individual method arguments. 115 matchingMethod = doFindMatchingMethod(getArguments()); 116 } 117 if (matchingMethod == null) { 118 // Interpret argument array as single method argument of array type. 119 matchingMethod = doFindMatchingMethod(new Object[] {getArguments()}); 120 } 121 return matchingMethod; 122 } 123 124 /** 125 * Actually find a method with matching parameter type, i.e. where each 126 * argument value is assignable to the corresponding parameter type. 127 * @param arguments the argument values to match against method parameters 128 * @return a matching method, or {@code null} if none 129 */ 130 protected Method doFindMatchingMethod(Object[] arguments) { 131 TypeConverter converter = getTypeConverter(); 132 if (converter != null) { 133 String targetMethod = getTargetMethod(); 134 Method matchingMethod = null; 135 int argCount = arguments.length; 136 Method[] candidates = ReflectionUtils.getAllDeclaredMethods(getTargetClass()); 137 int minTypeDiffWeight = Integer.MAX_VALUE; 138 Object[] argumentsToUse = null; 139 for (Method candidate : candidates) { 140 if (candidate.getName().equals(targetMethod)) { 141 // Check if the inspected method has the correct number of parameters. 142 Class<?>[] paramTypes = candidate.getParameterTypes(); 143 if (paramTypes.length == argCount) { 144 Object[] convertedArguments = new Object[argCount]; 145 boolean match = true; 146 for (int j = 0; j < argCount && match; j++) { 147 // Verify that the supplied argument is assignable to the method parameter. 148 try { 149 convertedArguments[j] = converter.convertIfNecessary(arguments[j], paramTypes[j]); 150 } 151 catch (TypeMismatchException ex) { 152 // Ignore -> simply doesn't match. 153 match = false; 154 } 155 } 156 if (match) { 157 int typeDiffWeight = getTypeDifferenceWeight(paramTypes, convertedArguments); 158 if (typeDiffWeight < minTypeDiffWeight) { 159 minTypeDiffWeight = typeDiffWeight; 160 matchingMethod = candidate; 161 argumentsToUse = convertedArguments; 162 } 163 } 164 } 165 } 166 } 167 if (matchingMethod != null) { 168 setArguments(argumentsToUse); 169 return matchingMethod; 170 } 171 } 172 return null; 173 } 174 175}