001/* 002 * Copyright 2002-2014 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.expression.spel.support; 018 019import java.lang.reflect.Constructor; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Comparator; 023import java.util.List; 024 025import org.springframework.core.MethodParameter; 026import org.springframework.core.convert.TypeDescriptor; 027import org.springframework.expression.AccessException; 028import org.springframework.expression.ConstructorExecutor; 029import org.springframework.expression.ConstructorResolver; 030import org.springframework.expression.EvaluationContext; 031import org.springframework.expression.EvaluationException; 032import org.springframework.expression.TypeConverter; 033 034/** 035 * A constructor resolver that uses reflection to locate the constructor that should be invoked. 036 * 037 * @author Andy Clement 038 * @author Juergen Hoeller 039 * @since 3.0 040 */ 041public class ReflectiveConstructorResolver implements ConstructorResolver { 042 043 /** 044 * Locate a constructor on the type. There are three kinds of match that might occur: 045 * <ol> 046 * <li>An exact match where the types of the arguments match the types of the constructor 047 * <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor 048 * <li>A match where we are able to convert the arguments into those expected by the constructor, according to the 049 * registered type converter. 050 * </ol> 051 */ 052 @Override 053 public ConstructorExecutor resolve(EvaluationContext context, String typeName, List<TypeDescriptor> argumentTypes) 054 throws AccessException { 055 056 try { 057 TypeConverter typeConverter = context.getTypeConverter(); 058 Class<?> type = context.getTypeLocator().findType(typeName); 059 Constructor<?>[] ctors = type.getConstructors(); 060 061 Arrays.sort(ctors, new Comparator<Constructor<?>>() { 062 @Override 063 public int compare(Constructor<?> c1, Constructor<?> c2) { 064 int c1pl = c1.getParameterTypes().length; 065 int c2pl = c2.getParameterTypes().length; 066 return (c1pl < c2pl ? -1 : (c1pl > c2pl ? 1 : 0)); 067 } 068 }); 069 070 Constructor<?> closeMatch = null; 071 Constructor<?> matchRequiringConversion = null; 072 073 for (Constructor<?> ctor : ctors) { 074 Class<?>[] paramTypes = ctor.getParameterTypes(); 075 List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length); 076 for (int i = 0; i < paramTypes.length; i++) { 077 paramDescriptors.add(new TypeDescriptor(new MethodParameter(ctor, i))); 078 } 079 ReflectionHelper.ArgumentsMatchInfo matchInfo = null; 080 if (ctor.isVarArgs() && argumentTypes.size() >= paramTypes.length - 1) { 081 // *sigh* complicated 082 // Basically.. we have to have all parameters match up until the varargs one, then the rest of what is 083 // being provided should be 084 // the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - 085 // or the final parameter 086 // we are supplied does match exactly (it is an array already). 087 matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); 088 } 089 else if (paramTypes.length == argumentTypes.size()) { 090 // worth a closer look 091 matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter); 092 } 093 if (matchInfo != null) { 094 if (matchInfo.isExactMatch()) { 095 return new ReflectiveConstructorExecutor(ctor); 096 } 097 else if (matchInfo.isCloseMatch()) { 098 closeMatch = ctor; 099 } 100 else if (matchInfo.isMatchRequiringConversion()) { 101 matchRequiringConversion = ctor; 102 } 103 } 104 } 105 106 if (closeMatch != null) { 107 return new ReflectiveConstructorExecutor(closeMatch); 108 } 109 else if (matchRequiringConversion != null) { 110 return new ReflectiveConstructorExecutor(matchRequiringConversion); 111 } 112 else { 113 return null; 114 } 115 } 116 catch (EvaluationException ex) { 117 throw new AccessException("Failed to resolve constructor", ex); 118 } 119 } 120 121}