001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.reflect;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Array;
021import java.lang.reflect.Executable;
022import java.lang.reflect.InvocationTargetException;
023import java.lang.reflect.Method;
024import java.lang.reflect.Type;
025import java.lang.reflect.TypeVariable;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collections;
029import java.util.Comparator;
030import java.util.Iterator;
031import java.util.LinkedHashSet;
032import java.util.List;
033import java.util.Map;
034import java.util.Objects;
035import java.util.Set;
036import java.util.TreeMap;
037import java.util.stream.Collectors;
038import java.util.stream.Stream;
039
040import org.apache.commons.lang3.ArrayUtils;
041import org.apache.commons.lang3.ClassUtils;
042import org.apache.commons.lang3.ClassUtils.Interfaces;
043import org.apache.commons.lang3.Validate;
044import org.apache.commons.lang3.stream.LangCollectors;
045import org.apache.commons.lang3.stream.Streams;
046
047/**
048 * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
049 * Differences from the BeanUtils version may be noted, especially where similar functionality
050 * already existed within Lang.
051 *
052 * <h2>Known Limitations</h2>
053 * <h3>Accessing Public Methods In A Default Access Superclass</h3>
054 * <p>
055 * There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
056 * Reflection locates these methods fine and correctly assigns them as {@code public}.
057 * However, an {@link IllegalAccessException} is thrown if the method is invoked.
058 * </p>
059 *
060 * <p>
061 * {@link MethodUtils} contains a workaround for this situation.
062 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
063 * If this call succeeds, then the method can be invoked as normal.
064 * This call will only succeed when the application has sufficient security privileges.
065 * If this call fails then the method may fail.
066 * </p>
067 *
068 * @since 2.5
069 */
070public class MethodUtils {
071
072    private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
073
074    /**
075     * Computes the aggregate number of inheritance hops between assignable argument class types.  Returns -1
076     * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.
077     *
078     * @param fromClassArray The Class array to calculate the distance from.
079     * @param toClassArray The Class array to calculate the distance to.
080     * @return The aggregate number of inheritance hops between assignable argument class types.
081     */
082    private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
083        int answer = 0;
084        if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
085            return -1;
086        }
087        for (int offset = 0; offset < fromClassArray.length; offset++) {
088            // Note InheritanceUtils.distance() uses different scoring system.
089            final Class<?> aClass = fromClassArray[offset];
090            final Class<?> toClass = toClassArray[offset];
091            if (aClass == null || aClass.equals(toClass)) {
092                continue;
093            }
094            if (ClassUtils.isAssignable(aClass, toClass, true) && !ClassUtils.isAssignable(aClass, toClass, false)) {
095                // Autoboxing/unboxing conversion. When a primitive is boxed, rank the exact
096                // wrapper ahead of any of its supertypes so the most specific overload wins.
097                answer++;
098                if (aClass.isPrimitive() && !ClassUtils.primitiveToWrapper(aClass).equals(toClass)) {
099                    answer += 2;
100                }
101            } else {
102                answer += 2;
103            }
104        }
105        return answer;
106    }
107
108    /**
109     * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
110     * {@code null}.
111     *
112     * @param cls The implementing class, may be null.
113     * @param method The method that we wish to call, may be null.
114     * @return The accessible method or null.
115     * @since 3.19.0
116     */
117    public static Method getAccessibleMethod(final Class<?> cls, final Method method) {
118        if (!MemberUtils.isPublic(method)) {
119            return null;
120        }
121        // If the declaring class is public, we are done
122        if (ClassUtils.isPublic(cls)) {
123            return method;
124        }
125        final String methodName = method.getName();
126        final Class<?>[] parameterTypes = method.getParameterTypes();
127        // Check the implemented interfaces and subinterfaces
128        final Method method2 = getAccessibleMethodFromInterfaceNest(cls, methodName, parameterTypes);
129        // Check the superclass chain
130        return method2 != null ? method2 : getAccessibleMethodFromSuperclass(cls, methodName, parameterTypes);
131    }
132
133    /**
134     * 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
135     * {@code null}. This is just a convenience wrapper for {@link #getAccessibleMethod(Method)}.
136     *
137     * @param cls            get method from this class.
138     * @param methodName     get method with this name.
139     * @param parameterTypes with these parameters types.
140     * @return The accessible method.
141     */
142    public static Method getAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
143        return getAccessibleMethod(getMethodObject(cls, methodName, parameterTypes));
144    }
145
146    /**
147     * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
148     * {@code null}.
149     *
150     * @param method The method that we wish to call, may be null.
151     * @return The accessible method
152     */
153    public static Method getAccessibleMethod(final Method method) {
154        return method != null ? getAccessibleMethod(method.getDeclaringClass(), method) : null;
155    }
156
157    /**
158     * Gets an accessible method (that is, one that can be invoked via
159     * reflection) that implements the specified method, by scanning through
160     * all implemented interfaces and subinterfaces. If no such method
161     * can be found, return {@code null}.
162     *
163     * <p>
164     * There isn't any good reason why this method must be {@code private}.
165     * It is because there doesn't seem any reason why other classes should
166     * call this rather than the higher level methods.
167     * </p>
168     *
169     * @param cls Parent class for the interfaces to be checked.
170     * @param methodName Method name of the method we wish to call.
171     * @param parameterTypes The parameter type signatures.
172     * @return The accessible method or {@code null} if not found.
173     */
174    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
175        // Search up the superclass chain
176        for (; cls != null; cls = cls.getSuperclass()) {
177            // Check the implemented interfaces of the parent class
178            final Class<?>[] interfaces = cls.getInterfaces();
179            for (final Class<?> anInterface : interfaces) {
180                // Is this interface public?
181                if (!ClassUtils.isPublic(anInterface)) {
182                    continue;
183                }
184                // Does the method exist on this interface? A static or private one is not inherited.
185                try {
186                    final Method declared = anInterface.getDeclaredMethod(methodName, parameterTypes);
187                    if (MemberUtils.isPublic(declared) && !MemberUtils.isStatic(declared)) {
188                        return declared;
189                    }
190                } catch (final NoSuchMethodException ignored) {
191                    /*
192                     * Swallow, if no method is found after the loop then this method returns null.
193                     */
194                }
195                // Recursively check our parent interfaces
196                final Method method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
197                if (method != null) {
198                    return method;
199                }
200            }
201        }
202        return null;
203    }
204
205    /**
206     * Gets an accessible method (that is, one that can be invoked via
207     * reflection) by scanning through the superclasses. If no such method
208     * can be found, return {@code null}.
209     *
210     * @param cls Class to be checked.
211     * @param methodName Method name of the method we wish to call.
212     * @param parameterTypes The parameter type signatures.
213     * @return The accessible method or {@code null} if not found.
214     */
215    private static Method getAccessibleMethodFromSuperclass(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
216        Class<?> parentClass = cls.getSuperclass();
217        while (parentClass != null) {
218            if (ClassUtils.isPublic(parentClass)) {
219                return getMethodObject(parentClass, methodName, parameterTypes);
220            }
221            parentClass = parentClass.getSuperclass();
222        }
223        return null;
224    }
225
226    /**
227     * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one from
228     * interfaces, and so on in a breadth first way.
229     *
230     * @param cls The class to look up, may be {@code null}.
231     * @return The combined {@link List} of superclasses and interfaces in order going up from this one {@code null} if null input.
232     */
233    private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
234        if (cls == null) {
235            return null;
236        }
237        final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
238        final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
239        int superClassIndex = 0;
240        final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
241        int interfaceIndex = 0;
242        while (interfaceIndex < allInterfaces.size() || superClassIndex < allSuperclasses.size()) {
243            final Class<?> acls;
244            if (interfaceIndex >= allInterfaces.size() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) {
245                acls = allSuperclasses.get(superClassIndex++);
246            } else {
247                acls = allInterfaces.get(interfaceIndex++);
248            }
249            allSuperClassesAndInterfaces.add(acls);
250        }
251        return allSuperClassesAndInterfaces;
252    }
253
254    /**
255     * Gets the annotation object with the given annotation type that is present on the given method or optionally on any equivalent method in super classes and
256     * interfaces. Returns null if the annotation type was not present.
257     *
258     * <p>
259     * Stops searching for an annotation once the first annotation of the specified type has been found. Additional annotations of the specified type will be
260     * silently ignored.
261     * </p>
262     *
263     * @param <A>           the annotation type.
264     * @param method        The {@link Method} to query, may be null.
265     * @param annotationCls The {@link Annotation} to check if is present on the method.
266     * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class is performed if the annotation was not directly
267     *                      present.
268     * @param ignoreAccess  determines if underlying method has to be accessible.
269     * @return The first matching annotation, or {@code null} if not found.
270     * @throws NullPointerException Thrown if either the method or annotation class is {@code null}.
271     * @throws SecurityException    Thrown if an underlying accessible object's method denies the request.
272     * @see SecurityManager#checkPermission
273     * @since 3.6
274     */
275    public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls, final boolean searchSupers,
276            final boolean ignoreAccess) {
277        Objects.requireNonNull(method, "method");
278        Objects.requireNonNull(annotationCls, "annotationCls");
279        if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
280            return null;
281        }
282        A annotation = method.getAnnotation(annotationCls);
283        if (annotation == null && searchSupers) {
284            final Class<?> mcls = method.getDeclaringClass();
285            final String methodName = method.getName();
286            final Class<?>[] paramTypes = method.getParameterTypes();
287            final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
288            for (final Class<?> acls : classes) {
289                // First, attempt an exact parameter-type match (getDeclaredMethod) to
290                // find a true override. This avoids matching unrelated overloads that
291                // are merely assignable-compatible (e.g. process(Integer) vs
292                // process(Number)).
293                Method equivalentMethod = null;
294                try {
295                    equivalentMethod = acls.getDeclaredMethod(methodName, paramTypes);
296                } catch (final NoSuchMethodException ignored) {
297                    // No exact match; check for generic-bridge scenario: the declaring
298                    // class may use a type variable whose erased form is Object (or
299                    // another bound). In that case the parent method's erased
300                    // parameter types differ from the child's concrete types, so we
301                    // scan declared methods for a same-name method whose *erased*
302                    // parameter count matches and whose erased types are assignable
303                    // from our concrete types.
304                    for (final Method candidate : acls.getDeclaredMethods()) {
305                        if (!candidate.getName().equals(methodName)) {
306                            continue;
307                        }
308                        final Class<?>[] candidateParams = candidate.getParameterTypes();
309                        if (candidateParams.length != paramTypes.length) {
310                            continue;
311                        }
312                        // Require that every concrete param type is assignable to the
313                        // candidate's (erased) param type AND that the candidate is
314                        // generic (has at least one TypeVariable in its generic
315                        // parameter types). This prevents matching plain overloads.
316                        boolean genericMatch = false;
317                        boolean paramsMatch = true;
318                        final java.lang.reflect.Type[] genericParams = candidate.getGenericParameterTypes();
319                        for (int i = 0; i < candidateParams.length; i++) {
320                            if (genericParams[i] instanceof java.lang.reflect.TypeVariable) {
321                                genericMatch = true;
322                            }
323                            if (!ClassUtils.isAssignable(paramTypes[i], candidateParams[i], true)) {
324                                paramsMatch = false;
325                                break;
326                            }
327                        }
328                        if (paramsMatch && genericMatch) {
329                            equivalentMethod = candidate;
330                            break;
331                        }
332                    }
333                }
334                if (equivalentMethod != null && (ignoreAccess || MemberUtils.isAccessible(equivalentMethod))) {
335                    annotation = equivalentMethod.getAnnotation(annotationCls);
336                    if (annotation != null) {
337                        break;
338                    }
339                }
340            }
341        }
342        return annotation;
343    }
344
345    private static Method getInvokeMethod(final boolean forceAccess, final String methodName, final Class<?>[] parameterTypes, final Class<?> cls) {
346        final Method method;
347        if (forceAccess) {
348            method = getMatchingMethod(cls, methodName, parameterTypes);
349            AccessibleObjects.setAccessible(method);
350        } else {
351            method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
352        }
353        return method;
354    }
355
356    /**
357     * Gets an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable
358     * from the given parameters. In other words, it finds a method with the given name that will take the parameters given.
359     *
360     * <p>
361     * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
362     * </p>
363     * <p>
364     * This method can match primitive parameter by passing in wrapper classes. For example, a {@link Boolean} will match a primitive {@code boolean} parameter.
365     * </p>
366     *
367     * @param cls            find method in this class.
368     * @param methodName     find method with this name.
369     * @param requestTypes find method with most compatible parameters.
370     * @return The accessible method or null.
371     * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
372     * @see SecurityManager#checkPermission
373     */
374    public static Method getMatchingAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... requestTypes) {
375        final Method candidate = getMethodObject(cls, methodName, requestTypes);
376        if (candidate != null) {
377            // The exact match may be declared on a non-public class, so prefer the public
378            // declaration the way the search below does; a static method hides, so it is kept.
379            final Method accessibleCandidate = MemberUtils.isStatic(candidate) ? null : getAccessibleMethod(cls, candidate);
380            return MemberUtils.setAccessibleWorkaround(accessibleCandidate != null ? accessibleCandidate : candidate);
381        }
382        // search through all methods
383        final Method[] methods = cls.getMethods();
384        final List<Method> matchingMethods = Stream.of(methods)
385                .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, requestTypes)).collect(Collectors.toList());
386        // Sort methods by signature to force deterministic result
387        matchingMethods.sort(METHOD_BY_SIGNATURE);
388        Method bestMatch = null;
389        for (final Method method : matchingMethods) {
390            // get accessible version of method
391            final Method accessibleMethod = getAccessibleMethod(method);
392            if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, requestTypes) < 0)) {
393                bestMatch = accessibleMethod;
394            }
395        }
396        if (bestMatch != null) {
397            MemberUtils.setAccessibleWorkaround(bestMatch);
398            if (bestMatch.isVarArgs()) {
399                final Class<?>[] bestMatchParameterTypes = bestMatch.getParameterTypes();
400                final Class<?> varArgType = bestMatchParameterTypes[bestMatchParameterTypes.length - 1].getComponentType();
401                for (int paramIdx = bestMatchParameterTypes.length - 1; paramIdx < requestTypes.length; paramIdx++) {
402                    final Class<?> parameterType = requestTypes[paramIdx];
403                    if (!ClassUtils.isAssignable(parameterType, varArgType, true)) {
404                        return null;
405                    }
406                }
407            }
408        }
409        return bestMatch;
410    }
411
412    /**
413     * Gets a method whether or not it's accessible. If no such method can be found, return {@code null}.
414     *
415     * @param cls            The class that will be subjected to the method search.
416     * @param methodName     The method that we wish to call.
417     * @param parameterTypes Argument class types.
418     * @throws IllegalStateException Thrown if there is no unique result.
419     * @throws NullPointerException  Thrown if the class is {@code null}.
420     * @return The method.
421     * @since 3.5
422     */
423    public static Method getMatchingMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
424        Objects.requireNonNull(cls, "cls");
425        Validate.notEmpty(methodName, "methodName");
426        final List<Method> methods = Stream.of(cls.getDeclaredMethods())
427                .filter(method -> method.getName().equals(methodName))
428                .collect(Collectors.toList());
429        final List<Class<?>> allSuperclassesAndInterfaces = getAllSuperclassesAndInterfaces(cls);
430        Collections.reverse(allSuperclassesAndInterfaces);
431        allSuperclassesAndInterfaces.stream()
432                .map(Class::getDeclaredMethods)
433                .flatMap(Stream::of)
434                .filter(method -> method.getName().equals(methodName))
435                .forEach(methods::add);
436        for (final Method method : methods) {
437            if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
438                return method;
439            }
440        }
441        final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
442        methods.stream()
443            .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
444            .forEach(method -> {
445                 final int distance = distance(parameterTypes, method.getParameterTypes());
446                 final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
447                 candidatesAtDistance.add(method);
448        });
449        if (candidates.isEmpty()) {
450            return null;
451        }
452        final List<Method> bestCandidates = candidates.values().iterator().next();
453        if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
454                bestCandidates.get(1).getDeclaringClass())) {
455            return bestCandidates.get(0);
456        }
457        throw new IllegalStateException(String.format("Found multiple candidates for method %s on class %s : %s",
458                methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")), cls.getName(),
459                bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]"))));
460    }
461
462    /**
463     * Gets a Method, or {@code null} if a checked {@link Class#getMethod(String, Class...) } exception is thrown.
464     *
465     * @param cls            Receiver for {@link Class#getMethod(String, Class...)}.
466     * @param name           The name of the method.
467     * @param parameterTypes The list of parameters.
468     * @return A Method or {@code null}.
469     * @see SecurityManager#checkPermission
470     * @see Class#getMethod(String, Class...)
471     * @since 3.15.0
472     */
473    public static Method getMethodObject(final Class<?> cls, final String name, final Class<?>... parameterTypes) {
474        try {
475            return name != null && cls != null ? cls.getMethod(name, parameterTypes) : null;
476        } catch (final NoSuchMethodException | SecurityException e) {
477            return null;
478        }
479    }
480
481    /**
482     * Gets all class level public methods of the given class that are annotated with the given annotation.
483     *
484     * @param cls           The {@link Class} to query.
485     * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
486     * @return A list of Methods (possibly empty).
487     * @throws NullPointerException Thrown if the class or annotation are {@code null}.
488     * @since 3.4
489     */
490    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
491        return getMethodsListWithAnnotation(cls, annotationCls, false, false);
492    }
493
494    /**
495     * Gets all methods of the given class that are annotated with the given annotation.
496     *
497     * @param cls           The {@link Class} to query.
498     * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
499     * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
500     * @param ignoreAccess  determines if non-public methods should be considered.
501     * @return A list of Methods (possibly empty).
502     * @throws NullPointerException Thrown if either the class or annotation class is {@code null}.
503     * @since 3.6
504     */
505    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
506            final boolean ignoreAccess) {
507        Objects.requireNonNull(cls, "cls");
508        Objects.requireNonNull(annotationCls, "annotationCls");
509        final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>();
510        classes.add(0, cls);
511        final List<Method> annotatedMethods = new ArrayList<>();
512        classes.forEach(acls -> {
513            final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods();
514            Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add);
515        });
516        return annotatedMethods;
517    }
518
519    /**
520     * Gets all class level public methods of the given class that are annotated with the given annotation.
521     *
522     * @param cls           The {@link Class} to query.
523     * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
524     * @return An array of Methods (possibly empty).
525     * @throws NullPointerException Thrown if the class or annotation are {@code null}.
526     * @since 3.4
527     */
528    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
529        return getMethodsWithAnnotation(cls, annotationCls, false, false);
530    }
531
532    /**
533     * Gets all methods of the given class that are annotated with the given annotation.
534     *
535     * @param cls           The {@link Class} to query.
536     * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
537     * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
538     * @param ignoreAccess  determines if non-public methods should be considered.
539     * @return An array of Methods (possibly empty).
540     * @throws NullPointerException Thrown if the class or annotation are {@code null}.
541     * @since 3.6
542     */
543    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
544            final boolean ignoreAccess) {
545        return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
546    }
547
548    /**
549     * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
550     *
551     * @param method lowest to consider.
552     * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false.
553     * @return A {@code Set<Method>} in ascending order from subclass to superclass.
554     * @throws NullPointerException Thrown if the specified method is {@code null}.
555     * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
556     * @see SecurityManager#checkPermission
557     * @since 3.2
558     */
559    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
560        Objects.requireNonNull(method, "method");
561        final Set<Method> result = new LinkedHashSet<>();
562        result.add(method);
563        final Class<?>[] parameterTypes = method.getParameterTypes();
564        final Class<?> declaringClass = method.getDeclaringClass();
565        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
566        //skip the declaring class :P
567        hierarchy.next();
568        hierarchyTraversal: while (hierarchy.hasNext()) {
569            final Class<?> c = hierarchy.next();
570            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
571            if (m == null) {
572                continue;
573            }
574            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
575                // matches without generics
576                result.add(m);
577                continue;
578            }
579            // necessary to get arguments every time in the case that we are including interfaces
580            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
581            for (int i = 0; i < parameterTypes.length; i++) {
582                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
583                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
584                if (!TypeUtils.equals(childType, parentType)) {
585                    continue hierarchyTraversal;
586                }
587            }
588            result.add(m);
589        }
590        return result;
591    }
592
593    /**
594     * Invokes a method whose parameter types match exactly the object type.
595     *
596     * <p>
597     * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
598     * </p>
599     *
600     * @param object     invoke method on this object.
601     * @param methodName get method with this name.
602     * @return The value returned by the invoked method.
603     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
604     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
605     *                                     inaccessible.
606     * @throws IllegalArgumentException    Thrown if:
607     *                                     <ul>
608     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
609     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
610     *                                     <li>the number of actual and formal parameters differ;</li>
611     *                                     </ul>
612     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
613     * @throws NullPointerException        Thrown if the specified {@code object} is null.
614     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
615     * @since 3.4
616     */
617    public static Object invokeExactMethod(final Object object, final String methodName)
618            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
619        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
620    }
621
622    /**
623     * Invokes a method whose parameter types match exactly the object types.
624     *
625     * <p>
626     * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
627     * </p>
628     *
629     * @param object     invoke method on this object.
630     * @param methodName get method with this name.
631     * @param args       use these arguments - treat null as empty array.
632     * @return The value returned by the invoked method.
633     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
634     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
635     *                                     inaccessible.
636     * @throws IllegalArgumentException    Thrown if:
637     *                                     <ul>
638     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
639     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
640     *                                     <li>the number of actual and formal parameters differ;</li>
641     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
642     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
643     *                                     method invocation conversion.</li>
644     *                                     </ul>
645     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
646     * @throws NullPointerException        Thrown if the specified {@code object} is null.
647     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
648     */
649    public static Object invokeExactMethod(final Object object, final String methodName, final Object... args)
650            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
651        final Object[] actuals = ArrayUtils.nullToEmpty(args);
652        return invokeExactMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
653    }
654
655    /**
656     * Invokes a method whose parameter types match exactly the parameter types given.
657     *
658     * <p>
659     * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
660     * </p>
661     *
662     * @param object         Invokes a method on this object.
663     * @param methodName     Gets a method with this name.
664     * @param args           Method arguments - treat null as empty array.
665     * @param parameterTypes Match these parameters - treat {@code null} as empty array.
666     * @return The value returned by the invoked method.
667     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
668     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
669     *                                     inaccessible.
670     * @throws IllegalArgumentException    Thrown if:
671     *                                     <ul>
672     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
673     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
674     *                                     <li>the number of actual and formal parameters differ;</li>
675     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
676     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
677     *                                     method invocation conversion.</li>
678     *                                     </ul>
679     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
680     * @throws NullPointerException        Thrown if the specified {@code object} is null.
681     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
682     */
683    public static Object invokeExactMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
684            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
685        final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
686        final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
687        final Method method = getAccessibleMethod(cls, methodName, paramTypes);
688        requireNonNull(method, cls, methodName, paramTypes);
689        return method.invoke(object, ArrayUtils.nullToEmpty(args));
690    }
691
692    /**
693     * Invokes a {@code static} method whose parameter types match exactly the object types.
694     *
695     * <p>
696     * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
697     * </p>
698     *
699     * @param cls        invoke static method on this class.
700     * @param methodName get method with this name.
701     * @param args       use these arguments - treat {@code null} as empty array.
702     * @return The value returned by the invoked method.
703     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
704     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
705     *                                     inaccessible.
706     * @throws IllegalArgumentException    Thrown if:
707     *                                     <ul>
708     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
709     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
710     *                                     <li>the number of actual and formal parameters differ;</li>
711     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
712     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
713     *                                     method invocation conversion.</li>
714     *                                     </ul>
715     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
716     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
717     */
718    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object... args)
719            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
720        final Object[] actuals = ArrayUtils.nullToEmpty(args);
721        return invokeExactStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
722    }
723
724    /**
725     * Invokes a {@code static} method whose parameter types match exactly the parameter types given.
726     *
727     * <p>
728     * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
729     * </p>
730     *
731     * @param cls            invoke static method on this class.
732     * @param methodName     get method with this name.
733     * @param args           use these arguments - treat {@code null} as empty array.
734     * @param parameterTypes match these parameters - treat {@code null} as empty array.
735     * @return The value returned by the invoked method.
736     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
737     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
738     *                                     inaccessible.
739     * @throws IllegalArgumentException    Thrown if:
740     *                                     <ul>
741     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
742     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
743     *                                     <li>the number of actual and formal parameters differ;</li>
744     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
745     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
746     *                                     method invocation conversion.</li>
747     *                                     </ul>
748     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
749     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
750     */
751    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
752            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
753        final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
754        final Method method = getAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(paramTypes));
755        requireNonNull(method, cls, methodName, paramTypes);
756        return method.invoke(null, ArrayUtils.nullToEmpty(args));
757    }
758
759    /**
760     * Invokes a named method without parameters.
761     *
762     * <p>
763     * This is a convenient wrapper for
764     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
765     * </p>
766     *
767     * @param object invoke method on this object.
768     * @param forceAccess force access to invoke method even if it's not accessible.
769     * @param methodName get method with this name.
770     * @return The value returned by the invoked method.
771     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
772     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
773     *                                     inaccessible.
774     * @throws IllegalArgumentException    Thrown if:
775     *                                     <ul>
776     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
777     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
778     *                                     <li>the number of actual and formal parameters differ;</li>
779     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
780     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
781     *                                     method invocation conversion.</li>
782     *                                     </ul>
783     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
784     * @throws NullPointerException        Thrown if the specified {@code object} is null.
785     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
786     * @see SecurityManager#checkPermission
787     * @since 3.5
788     */
789    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
790            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
791        return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
792    }
793
794    /**
795     * Invokes a named method whose parameter type matches the object type.
796     *
797     * <p>
798     * This method supports calls to methods taking primitive parameters
799     * via passing in wrapping classes. So, for example, a {@link Boolean} object
800     * would match a {@code boolean} primitive.
801     * </p>
802     * <p>
803     * This is a convenient wrapper for
804     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
805     * </p>
806     *
807     * @param object invoke method on this object.
808     * @param forceAccess force access to invoke method even if it's not accessible.
809     * @param methodName get method with this name.
810     * @param args use these arguments - treat null as empty array.
811     * @return The value returned by the invoked method.
812     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
813     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
814     *                                     inaccessible.
815     * @throws IllegalArgumentException    Thrown if:
816     *                                     <ul>
817     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
818     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
819     *                                     <li>the number of actual and formal parameters differ;</li>
820     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
821     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
822     *                                     method invocation conversion.</li>
823     *                                     </ul>
824     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
825     * @throws NullPointerException        Thrown if the specified {@code object} is null.
826     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
827     * @see SecurityManager#checkPermission
828     * @since 3.5
829     */
830    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object... args)
831            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
832        final Object[] actuals = ArrayUtils.nullToEmpty(args);
833        return invokeMethod(object, forceAccess, methodName, actuals, ClassUtils.toClass(actuals));
834    }
835
836    /**
837     * Invokes a named method whose parameter type matches the object type.
838     *
839     * <p>
840     * This method supports calls to methods taking primitive parameters
841     * via passing in wrapping classes. So, for example, a {@link Boolean} object
842     * would match a {@code boolean} primitive.
843     * </p>
844     *
845     * @param object invoke method on this object.
846     * @param forceAccess force access to invoke method even if it's not accessible.
847     * @param methodName get method with this name.
848     * @param args use these arguments - treat null as empty array.
849     * @param parameterTypes match these parameters - treat null as empty array.
850     * @return The value returned by the invoked method.
851     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
852     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
853     *                                     inaccessible.
854     * @throws IllegalArgumentException    Thrown if:
855     *                                     <ul>
856     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
857     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
858     *                                     <li>the number of actual and formal parameters differ;</li>
859     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
860     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
861     *                                     method invocation conversion.</li>
862     *                                     </ul>
863     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
864     * @throws NullPointerException        Thrown if the specified {@code object} is null.
865     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
866     * @see SecurityManager#checkPermission
867     * @since 3.5
868     */
869    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
870            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
871        final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
872        final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
873        final Method method = getInvokeMethod(forceAccess, methodName, paramTypes, cls);
874        requireNonNull(method, cls, methodName, paramTypes);
875        return method.invoke(object, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
876    }
877
878    /**
879     * Invokes a named method without parameters.
880     *
881     * <p>
882     * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
883     * </p>
884     * <p>
885     * This is a convenient wrapper for
886     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
887     * </p>
888     *
889     * @param object invoke method on this object.
890     * @param methodName get method with this name.
891     * @return The value returned by the invoked method.
892     * @throws NoSuchMethodException Thrown if there is no such accessible method.
893     * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
894     * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
895     * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
896     * @see SecurityManager#checkPermission
897     * @since 3.4
898     */
899    public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
900            IllegalAccessException, InvocationTargetException {
901        return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
902    }
903
904    /**
905     * Invokes a named method whose parameter type matches the object type.
906     *
907     * <p>
908     * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
909     * </p>
910     * <p>
911     * This method supports calls to methods taking primitive parameters
912     * via passing in wrapping classes. So, for example, a {@link Boolean} object
913     * would match a {@code boolean} primitive.
914     * </p>
915     * <p>
916     * This is a convenient wrapper for
917     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
918     * </p>
919     *
920     * @param object invoke method on this object.
921     * @param methodName get method with this name.
922     * @param args use these arguments - treat null as empty array.
923     * @return The value returned by the invoked method.
924     * @throws NoSuchMethodException Thrown if there is no such accessible method.
925     * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
926     * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
927     * @throws NullPointerException Thrown if the object or method name are {@code null}.
928     * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
929     * @see SecurityManager#checkPermission
930     */
931    public static Object invokeMethod(final Object object, final String methodName, final Object... args)
932            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
933        final Object[] actuals = ArrayUtils.nullToEmpty(args);
934        return invokeMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
935    }
936
937    /**
938     * Invokes a named method whose parameter type matches the object type.
939     *
940     * <p>
941     * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
942     * </p>
943     * <p>
944     * This method supports calls to methods taking primitive parameters
945     * via passing in wrapping classes. So, for example, a {@link Boolean} object
946     * would match a {@code boolean} primitive.
947     * </p>
948     *
949     * @param object invoke method on this object.
950     * @param methodName get method with this name.
951     * @param args use these arguments - treat null as empty array.
952     * @param parameterTypes match these parameters - treat null as empty array.
953     * @return The value returned by the invoked method.
954     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
955     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
956     *                                     inaccessible.
957     * @throws IllegalArgumentException    Thrown if:
958     *                                     <ul>
959     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
960     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
961     *                                     <li>the number of actual and formal parameters differ;</li>
962     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
963     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
964     *                                     method invocation conversion.</li>
965     *                                     </ul>
966     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
967     * @throws NullPointerException        Thrown if the specified {@code object} is null.
968     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
969     * @see SecurityManager#checkPermission
970     */
971    public static Object invokeMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
972            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
973        return invokeMethod(object, false, methodName, args, parameterTypes);
974    }
975
976    /**
977     * Invokes a named {@code static} method whose parameter type matches the object type.
978     *
979     * <p>
980     * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
981     * </p>
982     * <p>
983     * This method supports calls to methods taking primitive parameters
984     * via passing in wrapping classes. So, for example, a {@link Boolean} class
985     * would match a {@code boolean} primitive.
986     * </p>
987     * <p>
988     * This is a convenient wrapper for
989     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
990     * </p>
991     *
992     * @param cls invoke static method on this class.
993     * @param methodName get method with this name.
994     * @param args use these arguments - treat {@code null} as empty array.
995     * @return The value returned by the invoked method.
996     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
997     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
998     *                                     inaccessible.
999     * @throws IllegalArgumentException    Thrown if:
1000     *                                     <ul>
1001     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1002     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1003     *                                     <li>the number of actual and formal parameters differ;</li>
1004     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
1005     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1006     *                                     method invocation conversion.</li>
1007     *                                     </ul>
1008     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
1009     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1010     * @see SecurityManager#checkPermission
1011     */
1012    public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object... args)
1013            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1014        final Object[] actuals = ArrayUtils.nullToEmpty(args);
1015        return invokeStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
1016    }
1017
1018    /**
1019     * Invokes a named {@code static} method whose parameter type matches the object type.
1020     *
1021     * <p>
1022     * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
1023     * </p>
1024     * <p>
1025     * This method supports calls to methods taking primitive parameters
1026     * via passing in wrapping classes. So, for example, a {@link Boolean} class
1027     * would match a {@code boolean} primitive.
1028     * </p>
1029     *
1030     * @param cls invoke static method on this class.
1031     * @param methodName get method with this name.
1032     * @param args use these arguments - treat {@code null} as empty array.
1033     * @param parameterTypes match these parameters - treat {@code null} as empty array.
1034     * @return The value returned by the invoked method.
1035     * @throws NoSuchMethodException       Thrown if there is no such accessible method.
1036     * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
1037     *                                     inaccessible.
1038     * @throws IllegalArgumentException    Thrown if:
1039     *                                     <ul>
1040     *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1041     *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1042     *                                     <li>the number of actual and formal parameters differ;</li>
1043     *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
1044     *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1045     *                                     method invocation conversion.</li>
1046     *                                     </ul>
1047     * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
1048     * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1049     * @see SecurityManager#checkPermission
1050     */
1051    public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
1052            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1053        final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
1054        final Method method = getMatchingAccessibleMethod(cls, methodName, paramTypes);
1055        requireNonNull(method, cls, methodName, paramTypes);
1056        return method.invoke(null, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
1057    }
1058
1059    private static Method requireNonNull(final Method method, final Class<?> cls, final String methodName, final Class<?>[] parameterTypes)
1060            throws NoSuchMethodException {
1061        if (method == null) {
1062            throw new NoSuchMethodException(String.format("No method: %s.%s(%s)", ClassUtils.getName(cls), methodName,
1063                    Streams.of(parameterTypes).map(ClassUtils::getName).collect(LangCollectors.joining(", "))));
1064        }
1065        return method;
1066    }
1067
1068    static Object[] toVarArgs(final Executable executable, final Object[] args)
1069            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1070        return executable.isVarArgs() ? toVarArgs(args, executable.getParameterTypes()) : args;
1071    }
1072
1073    /**
1074     * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method, for example an array with the declared number of
1075     * parameters, and whose last parameter is an array of the varargs type.
1076     * <p>
1077     * We follow the <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a> rules.
1078     * </p>
1079     *
1080     * @param args                 The array of arguments passed to the varags method.
1081     * @param methodParameterTypes The declared array of method parameter types.
1082     * @return An array of the variadic arguments passed to the method.
1083     * @throws NoSuchMethodException       Thrown if the constructor could not be found.
1084     * @throws IllegalAccessException      Thrown if this {@code Constructor} object is enforcing Java language access control and the underlying constructor is
1085     *                                     inaccessible.
1086     * @throws IllegalArgumentException    Thrown if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments
1087     *                                     fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter
1088     *                                     type by a method invocation conversion; if this constructor pertains to an enum type.
1089     * @throws InstantiationException      Thrown if a class that declares the underlying constructor represents an abstract class.
1090     * @throws InvocationTargetException   Thrown if an underlying constructor throws an exception.
1091     * @throws ExceptionInInitializerError Thrown if an initialization provoked by this method fails.
1092     * @see <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a>
1093     */
1094    private static Object[] toVarArgs(final Object[] args, final Class<?>[] methodParameterTypes)
1095            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1096        final int mptLength = methodParameterTypes.length;
1097        if (args.length == mptLength) {
1098            final Object lastArg = args[args.length - 1];
1099            if (lastArg == null || lastArg.getClass().equals(methodParameterTypes[mptLength - 1])) {
1100                // The args array is already in the canonical form for the method.
1101                return args;
1102            }
1103        }
1104        // Construct a new array matching the method's declared parameter types.
1105        // Copy the normal (non-varargs) parameters
1106        final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, mptLength - 1, () -> new Object[mptLength]);
1107        // Construct a new array for the variadic parameters
1108        final Class<?> varArgComponentType = methodParameterTypes[mptLength - 1].getComponentType();
1109        final Class<?> varArgComponentWrappedType = ClassUtils.primitiveToWrapper(varArgComponentType);
1110        final int varArgLength = args.length - mptLength + 1;
1111        // Copy the variadic arguments into the varargs array, converting types if needed.
1112        Object varArgsArray = Array.newInstance(varArgComponentWrappedType, varArgLength);
1113        final boolean primitiveOrWrapper = ClassUtils.isPrimitiveOrWrapper(varArgComponentWrappedType);
1114        for (int i = 0; i < varArgLength; i++) {
1115            final Object arg = args[mptLength - 1 + i];
1116            try {
1117                Array.set(varArgsArray, i, primitiveOrWrapper
1118                        ? varArgComponentWrappedType.getConstructor(ClassUtils.wrapperToPrimitive(varArgComponentWrappedType)).newInstance(arg)
1119                        : varArgComponentWrappedType.cast(arg));
1120            } catch (final InstantiationException e) {
1121                throw new IllegalArgumentException("Cannot convert vararg #" + i, e);
1122            }
1123        }
1124        if (varArgComponentType.isPrimitive()) {
1125            // unbox from wrapper type to primitive type
1126            varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
1127        }
1128        // Store the varargs array in the last position of the array to return
1129        newArgs[mptLength - 1] = varArgsArray;
1130        // Return the canonical varargs array.
1131        return newArgs;
1132    }
1133
1134    /**
1135     * {@link MethodUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1136     * {@code MethodUtils.getAccessibleMethod(method)}.
1137     *
1138     * <p>
1139     * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
1140     * </p>
1141     *
1142     * @deprecated TODO Make private in 4.0.
1143     */
1144    @Deprecated
1145    public MethodUtils() {
1146        // empty
1147    }
1148}