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