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