public static Method getMethod()

in java/org/apache/el/util/ReflectionUtil.java [135:321]


    public static Method getMethod(EvaluationContext ctx, 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;
        }

        Class<?> clazz = base.getClass();

        // Fast path: when no arguments exist, there can only be one matching method and no need for coercion.
        if (paramCount == 0) {
            try {
                Method method = clazz.getMethod(methodName, paramTypes);
                return getMethod(clazz, base, method);
            } catch (NoSuchMethodException | SecurityException e) {
                // Fall through to broader, slower logic
            }
        }

        Method[] methods = clazz.getMethods();
        Map<Method,MatchResult> candidates = new HashMap<>();

        for (Method m : methods) {
            if (!m.getName().equals(methodName)) {
                // Method name doesn't match
                continue;
            }

            Class<?>[] mParamTypes = m.getParameterTypes();
            int mParamCount = mParamTypes.length;

            // Check the number of parameters
            // Multiple tests to improve readability
            if (!m.isVarArgs() && paramCount != mParamCount) {
                // Method has wrong number of parameters
                continue;
            }
            if (m.isVarArgs() && paramCount < mParamCount - 1) {
                // Method has wrong number of parameters
                continue;
            }
            if (m.isVarArgs() && paramCount == mParamCount && paramValues != null && paramValues.length > paramCount &&
                    !paramTypes[mParamCount - 1].isArray()) {
                // Method arguments don't match
                continue;
            }
            if (m.isVarArgs() && paramCount > mParamCount && paramValues != null && paramValues.length != paramCount) {
                // Might match a different varargs method
                continue;
            }
            if (!m.isVarArgs() && paramValues != null && paramCount != paramValues.length) {
                // Might match a different varargs method
                continue;
            }

            // Check the parameters match
            int exactMatch = 0;
            int assignableMatch = 0;
            int coercibleMatch = 0;
            int varArgsMatch = 0;
            boolean noMatch = false;
            for (int i = 0; i < mParamCount; i++) {
                // Can't be null
                if (m.isVarArgs() && i == (mParamCount - 1)) {
                    if (i == paramCount || (paramValues != null && paramValues.length == i)) {
                        // Var args defined but nothing is passed as varargs
                        // Use MAX_VALUE so this matches only if nothing else does
                        varArgsMatch = Integer.MAX_VALUE;
                        break;
                    }
                    Class<?> varType = mParamTypes[i].getComponentType();
                    for (int j = i; j < paramCount; j++) {
                        if (isAssignableFrom(paramTypes[j], varType)) {
                            assignableMatch++;
                            varArgsMatch++;
                        } else {
                            if (paramValues == null) {
                                noMatch = true;
                                break;
                            } else {
                                if (isCoercibleFrom(ctx, paramValues[j], varType)) {
                                    coercibleMatch++;
                                    varArgsMatch++;
                                } else {
                                    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 (mParamTypes[i].equals(paramTypes[i])) {
                        exactMatch++;
                    } else if (paramTypes[i] != null && isAssignableFrom(paramTypes[i], mParamTypes[i])) {
                        assignableMatch++;
                    } else {
                        if (paramValues == null) {
                            noMatch = true;
                            break;
                        } else {
                            if (isCoercibleFrom(ctx, paramValues[i], mParamTypes[i])) {
                                coercibleMatch++;
                            } else {
                                noMatch = true;
                                break;
                            }
                        }
                    }
                }
            }
            if (noMatch) {
                continue;
            }

            // If a method is found where every parameter matches exactly,
            // and no vars args are present, return it
            if (exactMatch == paramCount && varArgsMatch == 0) {
                Method result = getMethod(clazz, base, m);
                if (result == null) {
                    throw new MethodNotFoundException(
                            MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes)));
                }
                return result;
            }

            candidates.put(m, new MatchResult(m.isVarArgs(), exactMatch, assignableMatch, coercibleMatch, varArgsMatch,
                    m.isBridge()));
        }

        // Look for the method that has the highest number of parameters where
        // the type matches exactly
        MatchResult bestMatch = new MatchResult(true, 0, 0, 0, 0, true);
        Method match = null;
        boolean multiple = false;
        for (Map.Entry<Method,MatchResult> entry : candidates.entrySet()) {
            int cmp = entry.getValue().compareTo(bestMatch);
            if (cmp > 0 || match == null) {
                bestMatch = entry.getValue();
                match = entry.getKey();
                multiple = false;
            } else if (cmp == 0) {
                multiple = true;
            }
        }
        if (multiple) {
            if (bestMatch.exactCount() == 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)));
        }

        Method result = getMethod(clazz, base, match);
        if (result == null) {
            throw new MethodNotFoundException(
                    MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes)));
        }
        return result;
    }