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