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