    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.reflect;
    018
    019import java.lang.annotation.Annotation;
    020import java.lang.reflect.Array;
    021import java.lang.reflect.InvocationTargetException;
    022import java.lang.reflect.Method;
    023import java.lang.reflect.Type;
    024import java.lang.reflect.TypeVariable;
    025import java.util.ArrayList;
    026import java.util.Arrays;
    027import java.util.Comparator;
    028import java.util.Iterator;
    029import java.util.LinkedHashSet;
    030import java.util.List;
    031import java.util.Map;
    032import java.util.Objects;
    033import java.util.Set;
    034import java.util.TreeMap;
    035import java.util.stream.Collectors;
    036import java.util.stream.Stream;
    037
    038import org.apache.commons.lang3.ArrayUtils;
    039import org.apache.commons.lang3.ClassUtils;
    040import org.apache.commons.lang3.ClassUtils.Interfaces;
    041import org.apache.commons.lang3.Validate;
    042
    043/**
    044 * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
    045 * Differences from the BeanUtils version may be noted, especially where similar functionality
    046 * already existed within Lang.
    047 *
    048 * <h2>Known Limitations</h2>
    049 * <h3>Accessing Public Methods In A Default Access Superclass</h3>
    050 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
    051 * Reflection locates these methods fine and correctly assigns them as {@code public}.
    052 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
    053 *
    054 * <p>{@link MethodUtils} contains a workaround for this situation.
    055 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
    056 * If this call succeeds, then the method can be invoked as normal.
    057 * This call will only succeed when the application has sufficient security privileges.
    058 * If this call fails then the method may fail.</p>
    059 *
    060 * @since 2.5
    061 */
    062public class MethodUtils {
    063
    064    private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
    065
    066    /**
    067     * {@link MethodUtils} instances should NOT be constructed in standard programming.
    068     * Instead, the class should be used as
    069     * {@code MethodUtils.getAccessibleMethod(method)}.
    070     *
    071     * <p>This constructor is {@code public} to permit tools that require a JavaBean
    072     * instance to operate.</p>
    073     */
    074    public MethodUtils() {
    075    }
    076
    077    /**
    078     * Invokes a named method without parameters.
    079     *
    080     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    081     *
    082     * <p>This is a convenient wrapper for
    083     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    084     * </p>
    085     *
    086     * @param object invoke method on this object
    087     * @param methodName get method with this name
    088     * @return The value returned by the invoked method
    089     *
    090     * @throws NoSuchMethodException if there is no such accessible method
    091     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    092     * @throws IllegalAccessException if the requested method is not accessible via reflection
    093     *
    094     *  @since 3.4
    095     */
    096    public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
    097            IllegalAccessException, InvocationTargetException {
    098        return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    099    }
    100
    101    /**
    102     * Invokes a named method without parameters.
    103     *
    104     * <p>This is a convenient wrapper for
    105     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
    106     * </p>
    107     *
    108     * @param object invoke method on this object
    109     * @param forceAccess force access to invoke method even if it's not accessible
    110     * @param methodName get method with this name
    111     * @return The value returned by the invoked method
    112     *
    113     * @throws NoSuchMethodException if there is no such accessible method
    114     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    115     * @throws IllegalAccessException if the requested method is not accessible via reflection
    116     *
    117     * @since 3.5
    118     */
    119    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
    120            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    121        return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    122    }
    123
    124    /**
    125     * Invokes a named method whose parameter type matches the object type.
    126     *
    127     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    128     *
    129     * <p>This method supports calls to methods taking primitive parameters
    130     * via passing in wrapping classes. So, for example, a {@link Boolean} object
    131     * would match a {@code boolean} primitive.</p>
    132     *
    133     * <p>This is a convenient wrapper for
    134     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    135     * </p>
    136     *
    137     * @param object invoke method on this object
    138     * @param methodName get method with this name
    139     * @param args use these arguments - treat null as empty array
    140     * @return The value returned by the invoked method
    141     *
    142     * @throws NoSuchMethodException if there is no such accessible method
    143     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    144     * @throws IllegalAccessException if the requested method is not accessible via reflection
    145     * @throws NullPointerException if the object or method name are {@code null}
    146     */
    147    public static Object invokeMethod(final Object object, final String methodName,
    148            Object... args) throws NoSuchMethodException,
    149            IllegalAccessException, InvocationTargetException {
    150        args = ArrayUtils.nullToEmpty(args);
    151        return invokeMethod(object, methodName, args, ClassUtils.toClass(args));
    152    }
    153
    154    /**
    155     * Invokes a named method whose parameter type matches the object type.
    156     *
    157     * <p>This method supports calls to methods taking primitive parameters
    158     * via passing in wrapping classes. So, for example, a {@link Boolean} object
    159     * would match a {@code boolean} primitive.</p>
    160     *
    161     * <p>This is a convenient wrapper for
    162     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
    163     * </p>
    164     *
    165     * @param object invoke method on this object
    166     * @param forceAccess force access to invoke method even if it's not accessible
    167     * @param methodName get method with this name
    168     * @param args use these arguments - treat null as empty array
    169     * @return The value returned by the invoked method
    170     *
    171     * @throws NoSuchMethodException if there is no such accessible method
    172     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    173     * @throws IllegalAccessException if the requested method is not accessible via reflection
    174     * @throws NullPointerException if the object or method name are {@code null}
    175     * @since 3.5
    176     */
    177    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
    178            Object... args) throws NoSuchMethodException,
    179            IllegalAccessException, InvocationTargetException {
    180        args = ArrayUtils.nullToEmpty(args);
    181        return invokeMethod(object, forceAccess, methodName, args, ClassUtils.toClass(args));
    182    }
    183
    184    /**
    185     * Invokes a named method whose parameter type matches the object type.
    186     *
    187     * <p>This method supports calls to methods taking primitive parameters
    188     * via passing in wrapping classes. So, for example, a {@link Boolean} object
    189     * would match a {@code boolean} primitive.</p>
    190     *
    191     * @param object invoke method on this object
    192     * @param forceAccess force access to invoke method even if it's not accessible
    193     * @param methodName get method with this name
    194     * @param args use these arguments - treat null as empty array
    195     * @param parameterTypes match these parameters - treat null as empty array
    196     * @return The value returned by the invoked method
    197     *
    198     * @throws NoSuchMethodException if there is no such accessible method
    199     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    200     * @throws IllegalAccessException if the requested method is not accessible via reflection
    201     * @throws NullPointerException if the object or method name are {@code null}
    202     * @since 3.5
    203     */
    204    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, Object[] args, Class<?>[] parameterTypes)
    205        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    206        Objects.requireNonNull(object, "object");
    207        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    208        args = ArrayUtils.nullToEmpty(args);
    209
    210        final String messagePrefix;
    211        final Method method;
    212
    213        final Class<? extends Object> cls = object.getClass();
    214        if (forceAccess) {
    215            messagePrefix = "No such method: ";
    216            method = getMatchingMethod(cls, methodName, parameterTypes);
    217            if (method != null && !method.isAccessible()) {
    218                method.setAccessible(true);
    219            }
    220        } else {
    221            messagePrefix = "No such accessible method: ";
    222            method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
    223        }
    224
    225        if (method == null) {
    226            throw new NoSuchMethodException(messagePrefix + methodName + "() on object: " + cls.getName());
    227        }
    228        args = toVarArgs(method, args);
    229
    230        return method.invoke(object, args);
    231    }
    232
    233    /**
    234     * Invokes a named method whose parameter type matches the object type.
    235     *
    236     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    237     *
    238     * <p>This method supports calls to methods taking primitive parameters
    239     * via passing in wrapping classes. So, for example, a {@link Boolean} object
    240     * would match a {@code boolean} primitive.</p>
    241     *
    242     * @param object invoke method on this object
    243     * @param methodName get method with this name
    244     * @param args use these arguments - treat null as empty array
    245     * @param parameterTypes match these parameters - treat null as empty array
    246     * @return The value returned by the invoked method
    247     *
    248     * @throws NoSuchMethodException if there is no such accessible method
    249     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    250     * @throws IllegalAccessException if the requested method is not accessible via reflection
    251     */
    252    public static Object invokeMethod(final Object object, final String methodName,
    253            final Object[] args, final Class<?>[] parameterTypes)
    254            throws NoSuchMethodException, IllegalAccessException,
    255            InvocationTargetException {
    256        return invokeMethod(object, false, methodName, args, parameterTypes);
    257    }
    258
    259    /**
    260     * Invokes a method whose parameter types match exactly the object
    261     * types.
    262     *
    263     * <p>This uses reflection to invoke the method obtained from a call to
    264     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
    265     *
    266     * @param object invoke method on this object
    267     * @param methodName get method with this name
    268     * @return The value returned by the invoked method
    269     *
    270     * @throws NoSuchMethodException if there is no such accessible method
    271     * @throws InvocationTargetException wraps an exception thrown by the
    272     *  method invoked
    273     * @throws IllegalAccessException if the requested method is not accessible
    274     *  via reflection
    275     *
    276     * @since 3.4
    277     */
    278    public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
    279            IllegalAccessException, InvocationTargetException {
    280        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    281    }
    282
    283    /**
    284     * Invokes a method with no parameters.
    285     *
    286     * <p>This uses reflection to invoke the method obtained from a call to
    287     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
    288     *
    289     * @param object invoke method on this object
    290     * @param methodName get method with this name
    291     * @param args use these arguments - treat null as empty array
    292     * @return The value returned by the invoked method
    293     *
    294     * @throws NoSuchMethodException if there is no such accessible method
    295     * @throws InvocationTargetException wraps an exception thrown by the
    296     *  method invoked
    297     * @throws IllegalAccessException if the requested method is not accessible
    298     *  via reflection
    299     * @throws NullPointerException if the object or method name are {@code null}
    300     */
    301    public static Object invokeExactMethod(final Object object, final String methodName,
    302            Object... args) throws NoSuchMethodException,
    303            IllegalAccessException, InvocationTargetException {
    304        args = ArrayUtils.nullToEmpty(args);
    305        return invokeExactMethod(object, methodName, args, ClassUtils.toClass(args));
    306    }
    307
    308    /**
    309     * Invokes a method whose parameter types match exactly the parameter
    310     * types given.
    311     *
    312     * <p>This uses reflection to invoke the method obtained from a call to
    313     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    314     *
    315     * @param object invoke method on this object
    316     * @param methodName get method with this name
    317     * @param args use these arguments - treat null as empty array
    318     * @param parameterTypes match these parameters - treat {@code null} as empty array
    319     * @return The value returned by the invoked method
    320     *
    321     * @throws NoSuchMethodException if there is no such accessible method
    322     * @throws InvocationTargetException wraps an exception thrown by the
    323     *  method invoked
    324     * @throws IllegalAccessException if the requested method is not accessible
    325     *  via reflection
    326     * @throws NullPointerException if the object or method name are {@code null}
    327     */
    328    public static Object invokeExactMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
    329        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    330        Objects.requireNonNull(object, "object");
    331        args = ArrayUtils.nullToEmpty(args);
    332        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    333        final Class<?> cls = object.getClass();
    334        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
    335        if (method == null) {
    336            throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + cls.getName());
    337        }
    338        return method.invoke(object, args);
    339    }
    340
    341    /**
    342     * Invokes a {@code static} method whose parameter types match exactly the parameter
    343     * types given.
    344     *
    345     * <p>This uses reflection to invoke the method obtained from a call to
    346     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    347     *
    348     * @param cls invoke static method on this class
    349     * @param methodName get method with this name
    350     * @param args use these arguments - treat {@code null} as empty array
    351     * @param parameterTypes match these parameters - treat {@code null} as empty array
    352     * @return The value returned by the invoked method
    353     *
    354     * @throws NoSuchMethodException if there is no such accessible method
    355     * @throws InvocationTargetException wraps an exception thrown by the
    356     *  method invoked
    357     * @throws IllegalAccessException if the requested method is not accessible
    358     *  via reflection
    359     */
    360    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
    361            Object[] args, Class<?>[] parameterTypes)
    362            throws NoSuchMethodException, IllegalAccessException,
    363            InvocationTargetException {
    364        args = ArrayUtils.nullToEmpty(args);
    365        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    366        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
    367        if (method == null) {
    368            throw new NoSuchMethodException("No such accessible method: "
    369                    + methodName + "() on class: " + cls.getName());
    370        }
    371        return method.invoke(null, args);
    372    }
    373
    374    /**
    375     * Invokes a named {@code static} method whose parameter type matches the object type.
    376     *
    377     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    378     *
    379     * <p>This method supports calls to methods taking primitive parameters
    380     * via passing in wrapping classes. So, for example, a {@link Boolean} class
    381     * would match a {@code boolean} primitive.</p>
    382     *
    383     * <p>This is a convenient wrapper for
    384     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
    385     * </p>
    386     *
    387     * @param cls invoke static method on this class
    388     * @param methodName get method with this name
    389     * @param args use these arguments - treat {@code null} as empty array
    390     * @return The value returned by the invoked method
    391     *
    392     * @throws NoSuchMethodException if there is no such accessible method
    393     * @throws InvocationTargetException wraps an exception thrown by the
    394     *  method invoked
    395     * @throws IllegalAccessException if the requested method is not accessible
    396     *  via reflection
    397     */
    398    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
    399            Object... args) throws NoSuchMethodException,
    400            IllegalAccessException, InvocationTargetException {
    401        args = ArrayUtils.nullToEmpty(args);
    402        return invokeStaticMethod(cls, methodName, args, ClassUtils.toClass(args));
    403    }
    404
    405    /**
    406     * Invokes a named {@code static} method whose parameter type matches the object type.
    407     *
    408     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    409     *
    410     * <p>This method supports calls to methods taking primitive parameters
    411     * via passing in wrapping classes. So, for example, a {@link Boolean} class
    412     * would match a {@code boolean} primitive.</p>
    413     *
    414     * @param cls invoke static method on this class
    415     * @param methodName get method with this name
    416     * @param args use these arguments - treat {@code null} as empty array
    417     * @param parameterTypes match these parameters - treat {@code null} as empty array
    418     * @return The value returned by the invoked method
    419     *
    420     * @throws NoSuchMethodException if there is no such accessible method
    421     * @throws InvocationTargetException wraps an exception thrown by the
    422     *  method invoked
    423     * @throws IllegalAccessException if the requested method is not accessible
    424     *  via reflection
    425     */
    426    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
    427            Object[] args, Class<?>[] parameterTypes)
    428            throws NoSuchMethodException, IllegalAccessException,
    429            InvocationTargetException {
    430        args = ArrayUtils.nullToEmpty(args);
    431        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    432        final Method method = getMatchingAccessibleMethod(cls, methodName,
    433                parameterTypes);
    434        if (method == null) {
    435            throw new NoSuchMethodException("No such accessible method: "
    436                    + methodName + "() on class: " + cls.getName());
    437        }
    438        args = toVarArgs(method, args);
    439        return method.invoke(null, args);
    440    }
    441
    442    private static Object[] toVarArgs(final Method method, Object[] args) {
    443        if (method.isVarArgs()) {
    444            final Class<?>[] methodParameterTypes = method.getParameterTypes();
    445            args = getVarArgs(args, methodParameterTypes);
    446        }
    447        return args;
    448    }
    449
    450    /**
    451     * Given an arguments array passed to a varargs method, return an array of arguments in the canonical form,
    452     * i.e. an array with the declared number of parameters, and whose last parameter is an array of the varargs type.
    453     *
    454     * @param args the array of arguments passed to the varags method
    455     * @param methodParameterTypes the declared array of method parameter types
    456     * @return an array of the variadic arguments passed to the method
    457     * @since 3.5
    458     */
    459    static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) {
    460        if (args.length == methodParameterTypes.length && (args[args.length - 1] == null ||
    461                args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
    462            // The args array is already in the canonical form for the method.
    463            return args;
    464        }
    465
    466        // Construct a new array matching the method's declared parameter types.
    467        final Object[] newArgs = new Object[methodParameterTypes.length];
    468
    469        // Copy the normal (non-varargs) parameters
    470        System.arraycopy(args, 0, newArgs, 0, methodParameterTypes.length - 1);
    471
    472        // Construct a new array for the variadic parameters
    473        final Class<?> varArgComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
    474        final int varArgLength = args.length - methodParameterTypes.length + 1;
    475
    476        Object varArgsArray = Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength);
    477        // Copy the variadic arguments into the varargs array.
    478        System.arraycopy(args, methodParameterTypes.length - 1, varArgsArray, 0, varArgLength);
    479
    480        if (varArgComponentType.isPrimitive()) {
    481            // unbox from wrapper type to primitive type
    482            varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
    483        }
    484
    485        // Store the varargs array in the last position of the array to return
    486        newArgs[methodParameterTypes.length - 1] = varArgsArray;
    487
    488        // Return the canonical varargs array.
    489        return newArgs;
    490    }
    491
    492    /**
    493     * Invokes a {@code static} method whose parameter types match exactly the object
    494     * types.
    495     *
    496     * <p>This uses reflection to invoke the method obtained from a call to
    497     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    498     *
    499     * @param cls invoke static method on this class
    500     * @param methodName get method with this name
    501     * @param args use these arguments - treat {@code null} as empty array
    502     * @return The value returned by the invoked method
    503     *
    504     * @throws NoSuchMethodException if there is no such accessible method
    505     * @throws InvocationTargetException wraps an exception thrown by the
    506     *  method invoked
    507     * @throws IllegalAccessException if the requested method is not accessible
    508     *  via reflection
    509     */
    510    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
    511            Object... args) throws NoSuchMethodException,
    512            IllegalAccessException, InvocationTargetException {
    513        args = ArrayUtils.nullToEmpty(args);
    514        return invokeExactStaticMethod(cls, methodName, args, ClassUtils.toClass(args));
    515    }
    516
    517    /**
    518     * Returns an accessible method (that is, one that can be invoked via
    519     * reflection) with given name and parameters. If no such method
    520     * can be found, return {@code null}.
    521     * This is just a convenience wrapper for
    522     * {@link #getAccessibleMethod(Method)}.
    523     *
    524     * @param cls get method from this class
    525     * @param methodName get method with this name
    526     * @param parameterTypes with these parameters types
    527     * @return The accessible method
    528     */
    529    public static Method getAccessibleMethod(final Class<?> cls, final String methodName,
    530        final Class<?>... parameterTypes) {
    531        try {
    532            return getAccessibleMethod(cls.getMethod(methodName, parameterTypes));
    533        } catch (final NoSuchMethodException e) {
    534            return null;
    535        }
    536    }
    537
    538    /**
    539     * Returns an accessible method (that is, one that can be invoked via
    540     * reflection) that implements the specified Method. If no such method
    541     * can be found, return {@code null}.
    542     *
    543     * @param method The method that we wish to call
    544     * @return The accessible method
    545     */
    546    public static Method getAccessibleMethod(Method method) {
    547        if (!MemberUtils.isAccessible(method)) {
    548            return null;
    549        }
    550        // If the declaring class is public, we are done
    551        final Class<?> cls = method.getDeclaringClass();
    552        if (ClassUtils.isPublic(cls)) {
    553            return method;
    554        }
    555        final String methodName = method.getName();
    556        final Class<?>[] parameterTypes = method.getParameterTypes();
    557
    558        // Check the implemented interfaces and subinterfaces
    559        method = getAccessibleMethodFromInterfaceNest(cls, methodName,
    560                parameterTypes);
    561
    562        // Check the superclass chain
    563        if (method == null) {
    564            method = getAccessibleMethodFromSuperclass(cls, methodName,
    565                    parameterTypes);
    566        }
    567        return method;
    568    }
    569
    570    /**
    571     * Returns an accessible method (that is, one that can be invoked via
    572     * reflection) by scanning through the superclasses. If no such method
    573     * can be found, return {@code null}.
    574     *
    575     * @param cls Class to be checked
    576     * @param methodName Method name of the method we wish to call
    577     * @param parameterTypes The parameter type signatures
    578     * @return the accessible method or {@code null} if not found
    579     */
    580    private static Method getAccessibleMethodFromSuperclass(final Class<?> cls,
    581            final String methodName, final Class<?>... parameterTypes) {
    582        Class<?> parentClass = cls.getSuperclass();
    583        while (parentClass != null) {
    584            if (ClassUtils.isPublic(parentClass)) {
    585                try {
    586                    return parentClass.getMethod(methodName, parameterTypes);
    587                } catch (final NoSuchMethodException e) {
    588                    return null;
    589                }
    590            }
    591            parentClass = parentClass.getSuperclass();
    592        }
    593        return null;
    594    }
    595
    596    /**
    597     * Returns an accessible method (that is, one that can be invoked via
    598     * reflection) that implements the specified method, by scanning through
    599     * all implemented interfaces and subinterfaces. If no such method
    600     * can be found, return {@code null}.
    601     *
    602     * <p>There isn't any good reason why this method must be {@code private}.
    603     * It is because there doesn't seem any reason why other classes should
    604     * call this rather than the higher level methods.</p>
    605     *
    606     * @param cls Parent class for the interfaces to be checked
    607     * @param methodName Method name of the method we wish to call
    608     * @param parameterTypes The parameter type signatures
    609     * @return the accessible method or {@code null} if not found
    610     */
    611    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
    612            final String methodName, final Class<?>... parameterTypes) {
    613        // Search up the superclass chain
    614        for (; cls != null; cls = cls.getSuperclass()) {
    615
    616            // Check the implemented interfaces of the parent class
    617            final Class<?>[] interfaces = cls.getInterfaces();
    618            for (final Class<?> anInterface : interfaces) {
    619                // Is this interface public?
    620                if (!ClassUtils.isPublic(anInterface)) {
    621                    continue;
    622                }
    623                // Does the method exist on this interface?
    624                try {
    625                    return anInterface.getDeclaredMethod(methodName,
    626                            parameterTypes);
    627                } catch (final NoSuchMethodException ignored) {
    628                    /*
    629                     * Swallow, if no method is found after the loop then this
    630                     * method returns null.
    631                     */
    632                }
    633                // Recursively check our parent interfaces
    634                final Method method = getAccessibleMethodFromInterfaceNest(anInterface,
    635                        methodName, parameterTypes);
    636                if (method != null) {
    637                    return method;
    638                }
    639            }
    640        }
    641        return null;
    642    }
    643
    644    /**
    645     * Finds an accessible method that matches the given name and has compatible parameters.
    646     * Compatible parameters mean that every method parameter is assignable from
    647     * the given parameters.
    648     * In other words, it finds a method with the given name
    649     * that will take the parameters given.
    650     *
    651     * <p>This method is used by
    652     * {@link
    653     * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    654     * </p>
    655     *
    656     * <p>This method can match primitive parameter by passing in wrapper classes.
    657     * For example, a {@link Boolean} will match a primitive {@code boolean}
    658     * parameter.
    659     * </p>
    660     *
    661     * @param cls find method in this class
    662     * @param methodName find method with this name
    663     * @param parameterTypes find method with most compatible parameters
    664     * @return The accessible method
    665     */
    666    public static Method getMatchingAccessibleMethod(final Class<?> cls,
    667        final String methodName, final Class<?>... parameterTypes) {
    668        try {
    669            return MemberUtils.setAccessibleWorkaround(cls.getMethod(methodName, parameterTypes));
    670        } catch (final NoSuchMethodException ignored) {
    671            // Swallow the exception
    672        }
    673        // search through all methods
    674        final Method[] methods = cls.getMethods();
    675        final List<Method> matchingMethods = Stream.of(methods)
    676            .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, parameterTypes)).collect(Collectors.toList());
    677
    678        // Sort methods by signature to force deterministic result
    679        matchingMethods.sort(METHOD_BY_SIGNATURE);
    680
    681        Method bestMatch = null;
    682        for (final Method method : matchingMethods) {
    683            // get accessible version of method
    684            final Method accessibleMethod = getAccessibleMethod(method);
    685            if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) {
    686                bestMatch = accessibleMethod;
    687            }
    688        }
    689        if (bestMatch != null) {
    690            MemberUtils.setAccessibleWorkaround(bestMatch);
    691        }
    692
    693        if (bestMatch != null && bestMatch.isVarArgs() && bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) {
    694            final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
    695            final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
    696            final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
    697
    698            final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
    699            final String parameterTypeName = lastParameterType == null ? null : lastParameterType.getName();
    700            final String parameterTypeSuperClassName = lastParameterType == null ? null : lastParameterType.getSuperclass().getName();
    701
    702            if (parameterTypeName != null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName)
    703                && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
    704                return null;
    705            }
    706        }
    707
    708        return bestMatch;
    709    }
    710
    711    /**
    712     * Retrieves a method whether or not it's accessible. If no such method
    713     * can be found, return {@code null}.
    714     * @param cls The class that will be subjected to the method search
    715     * @param methodName The method that we wish to call
    716     * @param parameterTypes Argument class types
    717     * @throws IllegalStateException if there is no unique result
    718     * @throws NullPointerException if the class is {@code null}
    719     * @return The method
    720     *
    721     * @since 3.5
    722     */
    723    public static Method getMatchingMethod(final Class<?> cls, final String methodName,
    724            final Class<?>... parameterTypes) {
    725        Objects.requireNonNull(cls, "cls");
    726        Validate.notEmpty(methodName, "methodName");
    727
    728        final List<Method> methods = Stream.of(cls.getDeclaredMethods())
    729                .filter(method -> method.getName().equals(methodName))
    730                .collect(Collectors.toList());
    731
    732        ClassUtils.getAllSuperclasses(cls).stream()
    733                .map(Class::getDeclaredMethods)
    734                .flatMap(Stream::of)
    735                .filter(method -> method.getName().equals(methodName))
    736                .forEach(methods::add);
    737
    738        for (final Method method : methods) {
    739            if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
    740                return method;
    741            }
    742        }
    743
    744        final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
    745
    746        methods.stream()
    747                .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
    748                .forEach(method -> {
    749                    final int distance = distance(parameterTypes, method.getParameterTypes());
    750                    final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
    751                    candidatesAtDistance.add(method);
    752                });
    753
    754        if (candidates.isEmpty()) {
    755            return null;
    756        }
    757
    758        final List<Method> bestCandidates = candidates.values().iterator().next();
    759        if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
    760                bestCandidates.get(1).getDeclaringClass())) {
    761            return bestCandidates.get(0);
    762        }
    763
    764        throw new IllegalStateException(
    765                String.format("Found multiple candidates for method %s on class %s : %s",
    766                        methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")),
    767                        cls.getName(),
    768                        bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]")))
    769        );
    770    }
    771
    772    /**
    773     * Returns the aggregate number of inheritance hops between assignable argument class types.  Returns -1
    774     * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.
    775     * @param fromClassArray the Class array to calculate the distance from.
    776     * @param toClassArray the Class array to calculate the distance to.
    777     * @return the aggregate number of inheritance hops between assignable argument class types.
    778     */
    779    private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
    780        int answer = 0;
    781
    782        if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
    783            return -1;
    784        }
    785        for (int offset = 0; offset < fromClassArray.length; offset++) {
    786            // Note InheritanceUtils.distance() uses different scoring system.
    787            final Class<?> aClass = fromClassArray[offset];
    788            final Class<?> toClass = toClassArray[offset];
    789            if (aClass == null || aClass.equals(toClass)) {
    790                continue;
    791            }
    792            if (ClassUtils.isAssignable(aClass, toClass, true)
    793                    && !ClassUtils.isAssignable(aClass, toClass, false)) {
    794                answer++;
    795            } else {
    796                answer = answer + 2;
    797            }
    798        }
    799
    800        return answer;
    801    }
    802
    803    /**
    804     * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
    805     * @param method lowest to consider
    806     * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false
    807     * @return Set&lt;Method&gt; in ascending order from sub- to superclass
    808     * @throws NullPointerException if the specified method is {@code null}
    809     * @since 3.2
    810     */
    811    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
    812        Objects.requireNonNull(method, "method");
    813        final Set<Method> result = new LinkedHashSet<>();
    814        result.add(method);
    815
    816        final Class<?>[] parameterTypes = method.getParameterTypes();
    817
    818        final Class<?> declaringClass = method.getDeclaringClass();
    819
    820        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
    821        //skip the declaring class :P
    822        hierarchy.next();
    823        hierarchyTraversal: while (hierarchy.hasNext()) {
    824            final Class<?> c = hierarchy.next();
    825            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
    826            if (m == null) {
    827                continue;
    828            }
    829            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
    830                // matches without generics
    831                result.add(m);
    832                continue;
    833            }
    834            // necessary to get arguments every time in the case that we are including interfaces
    835            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
    836            for (int i = 0; i < parameterTypes.length; i++) {
    837                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
    838                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
    839                if (!TypeUtils.equals(childType, parentType)) {
    840                    continue hierarchyTraversal;
    841                }
    842            }
    843            result.add(m);
    844        }
    845        return result;
    846    }
    847
    848    /**
    849     * Gets all class level public methods of the given class that are annotated with the given annotation.
    850     * @param cls
    851     *            the {@link Class} to query
    852     * @param annotationCls
    853     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
    854     * @return an array of Methods (possibly empty).
    855     * @throws NullPointerException if the class or annotation are {@code null}
    856     * @since 3.4
    857     */
    858    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    859        return getMethodsWithAnnotation(cls, annotationCls, false, false);
    860    }
    861
    862    /**
    863     * Gets all class level public methods of the given class that are annotated with the given annotation.
    864     * @param cls
    865     *            the {@link Class} to query
    866     * @param annotationCls
    867     *            the {@link Annotation} that must be present on a method to be matched
    868     * @return a list of Methods (possibly empty).
    869     * @throws NullPointerException
    870     *            if the class or annotation are {@code null}
    871     * @since 3.4
    872     */
    873    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    874        return getMethodsListWithAnnotation(cls, annotationCls, false, false);
    875    }
    876
    877    /**
    878     * Gets all methods of the given class that are annotated with the given annotation.
    879     * @param cls
    880     *            the {@link Class} to query
    881     * @param annotationCls
    882     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
    883     * @param searchSupers
    884     *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
    885     * @param ignoreAccess
    886     *            determines if non-public methods should be considered
    887     * @return an array of Methods (possibly empty).
    888     * @throws NullPointerException if the class or annotation are {@code null}
    889     * @since 3.6
    890     */
    891    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls,
    892        final boolean searchSupers, final boolean ignoreAccess) {
    893        return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
    894    }
    895
    896    /**
    897     * Gets all methods of the given class that are annotated with the given annotation.
    898     * @param cls
    899     *            the {@link Class} to query
    900     * @param annotationCls
    901     *            the {@link Annotation} that must be present on a method to be matched
    902     * @param searchSupers
    903     *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
    904     * @param ignoreAccess
    905     *            determines if non-public methods should be considered
    906     * @return a list of Methods (possibly empty).
    907     * @throws NullPointerException if either the class or annotation class is {@code null}
    908     * @since 3.6
    909     */
    910    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls,
    911                                                            final Class<? extends Annotation> annotationCls,
    912                                                            final boolean searchSupers, final boolean ignoreAccess) {
    913
    914        Objects.requireNonNull(cls, "cls");
    915        Objects.requireNonNull(annotationCls, "annotationCls");
    916        final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>();
    917        classes.add(0, cls);
    918        final List<Method> annotatedMethods = new ArrayList<>();
    919        classes.forEach(acls -> {
    920            final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods();
    921            Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add);
    922        });
    923        return annotatedMethods;
    924    }
    925
    926    /**
    927     * Gets the annotation object with the given annotation type that is present on the given method
    928     * or optionally on any equivalent method in super classes and interfaces. Returns null if the annotation
    929     * type was not present.
    930     *
    931     * <p>Stops searching for an annotation once the first annotation of the specified type has been
    932     * found. Additional annotations of the specified type will be silently ignored.</p>
    933     * @param <A>
    934     *            the annotation type
    935     * @param method
    936     *            the {@link Method} to query
    937     * @param annotationCls
    938     *            the {@link Annotation} to check if is present on the method
    939     * @param searchSupers
    940     *            determines if a lookup in the entire inheritance hierarchy of the given class is performed
    941     *            if the annotation was not directly present
    942     * @param ignoreAccess
    943     *            determines if underlying method has to be accessible
    944     * @return the first matching annotation, or {@code null} if not found
    945     * @throws NullPointerException if either the method or annotation class is {@code null}
    946     * @since 3.6
    947     */
    948    public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls,
    949                                                         final boolean searchSupers, final boolean ignoreAccess) {
    950
    951        Objects.requireNonNull(method, "method");
    952        Objects.requireNonNull(annotationCls, "annotationCls");
    953        if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
    954            return null;
    955        }
    956
    957        A annotation = method.getAnnotation(annotationCls);
    958
    959        if (annotation == null && searchSupers) {
    960            final Class<?> mcls = method.getDeclaringClass();
    961            final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
    962            for (final Class<?> acls : classes) {
    963                final Method equivalentMethod = ignoreAccess ? MethodUtils.getMatchingMethod(acls, method.getName(), method.getParameterTypes())
    964                    : MethodUtils.getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes());
    965                if (equivalentMethod != null) {
    966                    annotation = equivalentMethod.getAnnotation(annotationCls);
    967                    if (annotation != null) {
    968                        break;
    969                    }
    970                }
    971            }
    972        }
    973
    974        return annotation;
    975    }
    976
    977    /**
    978     * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and
    979     * {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one
    980     * from interfaces, and so on in a breadth first way.
    981     *
    982     * @param cls  the class to look up, may be {@code null}
    983     * @return the combined {@link List} of superclasses and interfaces in order
    984     * going up from this one
    985     *  {@code null} if null input
    986     */
    987    private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
    988        if (cls == null) {
    989            return null;
    990        }
    991
    992        final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
    993        final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
    994        int superClassIndex = 0;
    995        final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
    996        int interfaceIndex = 0;
    997        while (interfaceIndex < allInterfaces.size() ||
    998                superClassIndex < allSuperclasses.size()) {
    999            final Class<?> acls;
    1000            if (interfaceIndex >= allInterfaces.size()) {
    1001                acls = allSuperclasses.get(superClassIndex++);
    1002            } else if (superClassIndex >= allSuperclasses.size() || !(superClassIndex < interfaceIndex)) {
    1003                acls = allInterfaces.get(interfaceIndex++);
    1004            } else {
    1005                acls = allSuperclasses.get(superClassIndex++);
    1006            }
    1007            allSuperClassesAndInterfaces.add(acls);
    1008        }
    1009        return allSuperClassesAndInterfaces;
    1010    }
    1011}