@SuppressWarnings("null")
public static Method getMethod(Object base, Object property,
Class<?>[] paramTypes, Object[] paramValues)
throws MethodNotFoundException {
if (base == null || property == null) {
throw new MethodNotFoundException(MessageFactory.get(
"error.method.notfound", base, property,
paramString(paramTypes)));
}
String methodName = (property instanceof String) ? (String) property
: property.toString();
int paramCount;
if (paramTypes == null) {
paramCount = 0;
} else {
paramCount = paramTypes.length;
}
Method[] methods = base.getClass().getMethods();
Map<Method,Integer> candidates = new HashMap<Method,Integer>();
for (Method m : methods) {
if (!m.getName().equals(methodName)) {
// Method name doesn't match
continue;
}
Class<?>[] mParamTypes = m.getParameterTypes();
int mParamCount;
if (mParamTypes == null) {
mParamCount = 0;
} else {
mParamCount = mParamTypes.length;
}
// Check the number of parameters
if (!(paramCount == mParamCount ||
(m.isVarArgs() && paramCount >= mParamCount))) {
// Method has wrong number of parameters
continue;
}
// Check the parameters match
int exactMatch = 0;
boolean noMatch = false;
for (int i = 0; i < mParamCount; i++) {
// Can't be null
if (mParamTypes[i].equals(paramTypes[i])) {
exactMatch++;
} else if (i == (mParamCount - 1) && m.isVarArgs()) {
Class<?> varType = mParamTypes[i].getComponentType();
for (int j = i; j < paramCount; j++) {
if (!isAssignableFrom(paramTypes[j], varType)) {
if (paramValues == null) {
noMatch = true;
break;
} else {
if (!isCoercibleFrom(paramValues[j], varType)) {
noMatch = true;
break;
}
}
}
// Don't treat a varArgs match as an exact match, it can
// lead to a varArgs method matching when the result
// should be ambiguous
}
} else if (!isAssignableFrom(paramTypes[i], mParamTypes[i])) {
if (paramValues == null) {
noMatch = true;
break;
} else {
if (!isCoercibleFrom(paramValues[i], mParamTypes[i])) {
noMatch = true;
break;
}
}
}
}
if (noMatch) {
continue;
}
// If a method is found where every parameter matches exactly,
// return it
if (exactMatch == paramCount) {
return m;
}
candidates.put(m, Integer.valueOf(exactMatch));
}
// Look for the method that has the highest number of parameters where
// the type matches exactly
int bestMatch = 0;
Method match = null;
boolean multiple = false;
for (Map.Entry<Method, Integer> entry : candidates.entrySet()) {
if (entry.getValue().intValue() > bestMatch ||
match == null) {
bestMatch = entry.getValue().intValue();
match = entry.getKey();
multiple = false;
} else if (entry.getValue().intValue() == bestMatch) {
multiple = true;
}
}
if (multiple) {
if (bestMatch == paramCount - 1) {
// Only one parameter is not an exact match - try using the
// super class
match = resolveAmbiguousMethod(candidates.keySet(), paramTypes);
} else {
match = null;
}
if (match == null) {
// If multiple methods have the same matching number of parameters
// the match is ambiguous so throw an exception
throw new MethodNotFoundException(MessageFactory.get(
"error.method.ambiguous", base, property,
paramString(paramTypes)));
}
}
// Handle case where no match at all was found
if (match == null) {
throw new MethodNotFoundException(MessageFactory.get(
"error.method.notfound", base, property,
paramString(paramTypes)));
}
return match;