in velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java [209:321]
private Method getBestMatch(List<Method> methods, Class<?>[] args)
{
List<Match> bestMatches = new LinkedList<>();
Class<?>[] unboxedArgs = new Class<?>[args.length];
for (int i = 0; i < args.length; ++i)
{
unboxedArgs[i] = IntrospectionUtils.getUnboxedClass(args[i]);
}
for (Method method : methods)
{
int applicability = getApplicability(method, unboxedArgs);
if (applicability > NOT_CONVERTIBLE)
{
Match match = new Match(method, applicability, unboxedArgs);
if (bestMatches.size() == 0)
{
bestMatches.add(match);
}
else
{
/* filter existing matches */
boolean keepMethod = true;
for (ListIterator<Match> it = bestMatches.listIterator(); keepMethod && it.hasNext();)
{
Match best = it.next();
/* do not retain match if it's more specific than (or incomparable to) provided (unboxed) arguments
* while one of the best matches is less specific
*/
if (best.specificity == LESS_SPECIFIC && match.specificity < EQUIVALENT) /* != LESS_SPECIFIC && != EQUIVALENT */
{
keepMethod = false;
}
/* drop considered best match if match is less specific than (unboxed) provided args while
* the considered best match is more specific or incomparable
*/
else if (match.specificity == LESS_SPECIFIC && best.specificity < EQUIVALENT) /* != LESS_SPECIFIC && != EQUIVALENT */
{
it.remove();
}
/* compare applicability */
else if (best.applicability > match.applicability)
{
keepMethod = false;
}
else if (best.applicability < match.applicability)
{
it.remove();
}
/* compare methods between them */
else
{
/* but only if some provided args are non null and not Object */
if (onlyNullOrObjects(args))
{
/* in this case we only favor non-varrags methods */
if (match.varargs != best.varargs)
{
if (match.varargs)
{
keepMethod = false;
}
else if (best.varargs)
{
it.remove();
}
}
}
else
{
switch (compare(match.methodTypes, best.methodTypes))
{
case LESS_SPECIFIC:
keepMethod = false;
break;
case MORE_SPECIFIC:
it.remove();
break;
case INCOMPARABLE:
/* Java compiler favors non-vararg methods. Let's do the same. */
if (match.varargs != best.varargs)
{
if (match.varargs)
{
keepMethod = false;
}
else if (best.varargs)
{
it.remove();
}
}
/* otherwise it's an equivalent match */
break;
case EQUIVALENT:
break;
}
}
}
}
if (keepMethod)
{
bestMatches.add(match);
}
}
}
}
switch (bestMatches.size())
{
case 0: return null;
case 1: return bestMatches.get(0).method;
default: throw new AmbiguousException();
}
}