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}