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