private Method getBestMatch()

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();
        }
    }