001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.reflect;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Array;
021import java.lang.reflect.InvocationTargetException;
022import java.lang.reflect.Method;
023import java.lang.reflect.Modifier;
024import java.lang.reflect.Type;
025import java.lang.reflect.TypeVariable;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Iterator;
029import java.util.LinkedHashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033
034import org.apache.commons.lang3.ArrayUtils;
035import org.apache.commons.lang3.ClassUtils;
036import org.apache.commons.lang3.ClassUtils.Interfaces;
037import org.apache.commons.lang3.Validate;
038
039/**
040 * <p>Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
041 * Differences from the BeanUtils version may be noted, especially where similar functionality
042 * already existed within Lang.
043 * </p>
044 *
045 * <h3>Known Limitations</h3>
046 * <h4>Accessing Public Methods In A Default Access Superclass</h4>
047 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
048 * Reflection locates these methods fine and correctly assigns them as {@code public}.
049 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
050 *
051 * <p>{@link MethodUtils} contains a workaround for this situation. 
052 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
053 * If this call succeeds, then the method can be invoked as normal.
054 * This call will only succeed when the application has sufficient security privileges. 
055 * If this call fails then the method may fail.</p>
056 *
057 * @since 2.5
058 */
059public class MethodUtils {
060
061    /**
062     * <p>{@link MethodUtils} instances should NOT be constructed in standard programming.
063     * Instead, the class should be used as
064     * {@code MethodUtils.getAccessibleMethod(method)}.</p>
065     *
066     * <p>This constructor is {@code public} to permit tools that require a JavaBean
067     * instance to operate.</p>
068     */
069    public MethodUtils() {
070        super();
071    }
072
073    /**
074     * <p>Invokes a named method without parameters.</p>
075     *
076     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
077     *
078     * <p>This is a convenient wrapper for
079     * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}.
080     * </p>
081     *
082     * @param object invoke method on this object
083     * @param methodName get method with this name
084     * @return The value returned by the invoked method
085     *
086     * @throws NoSuchMethodException if there is no such accessible method
087     * @throws InvocationTargetException wraps an exception thrown by the method invoked
088     * @throws IllegalAccessException if the requested method is not accessible via reflection
089     *  
090     *  @since 3.4
091     */
092    public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
093            IllegalAccessException, InvocationTargetException {
094        return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
095    }
096    
097    /**
098     * <p>Invokes a named method without parameters.</p>
099     *
100     * <p>This is a convenient wrapper for
101     * {@link #invokeMethod(Object object,boolean forceAccess,String methodName, Object[] args, Class[] parameterTypes)}.
102     * </p>
103     *
104     * @param object invoke method on this object
105     * @param forceAccess force access to invoke method even if it's not accessible
106     * @param methodName get method with this name
107     * @return The value returned by the invoked method
108     *
109     * @throws NoSuchMethodException if there is no such accessible method
110     * @throws InvocationTargetException wraps an exception thrown by the method invoked
111     * @throws IllegalAccessException if the requested method is not accessible via reflection
112     *
113     * @since 3.5
114     */
115    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
116            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
117        return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
118    }
119
120    /**
121     * <p>Invokes a named method whose parameter type matches the object type.</p>
122     *
123     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
124     *
125     * <p>This method supports calls to methods taking primitive parameters 
126     * via passing in wrapping classes. So, for example, a {@code Boolean} object
127     * would match a {@code boolean} primitive.</p>
128     *
129     * <p>This is a convenient wrapper for
130     * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}.
131     * </p>
132     *
133     * @param object invoke method on this object
134     * @param methodName get method with this name
135     * @param args use these arguments - treat null as empty array
136     * @return The value returned by the invoked method
137     *
138     * @throws NoSuchMethodException if there is no such accessible method
139     * @throws InvocationTargetException wraps an exception thrown by the method invoked
140     * @throws IllegalAccessException if the requested method is not accessible via reflection
141     */
142    public static Object invokeMethod(final Object object, final String methodName,
143            Object... args) throws NoSuchMethodException,
144            IllegalAccessException, InvocationTargetException {
145        args = ArrayUtils.nullToEmpty(args);
146        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
147        return invokeMethod(object, methodName, args, parameterTypes);
148    }
149    
150    /**
151     * <p>Invokes a named method whose parameter type matches the object type.</p>
152     *
153     * <p>This method supports calls to methods taking primitive parameters 
154     * via passing in wrapping classes. So, for example, a {@code Boolean} object
155     * would match a {@code boolean} primitive.</p>
156     *
157     * <p>This is a convenient wrapper for
158     * {@link #invokeMethod(Object object,boolean forceAccess,String methodName, Object[] args, Class[] parameterTypes)}.
159     * </p>
160     *
161     * @param object invoke method on this object
162     * @param forceAccess force access to invoke method even if it's not accessible
163     * @param methodName get method with this name
164     * @param args use these arguments - treat null as empty array
165     * @return The value returned by the invoked method
166     *
167     * @throws NoSuchMethodException if there is no such accessible method
168     * @throws InvocationTargetException wraps an exception thrown by the method invoked
169     * @throws IllegalAccessException if the requested method is not accessible via reflection
170     *
171     * @since 3.5
172     */
173    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
174            Object... args) throws NoSuchMethodException,
175            IllegalAccessException, InvocationTargetException {
176        args = ArrayUtils.nullToEmpty(args);
177        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
178        return invokeMethod(object, forceAccess, methodName, args, parameterTypes);
179    }
180
181    /**
182     * <p>Invokes a named method whose parameter type matches the object type.</p>
183     *
184     * <p>This method supports calls to methods taking primitive parameters 
185     * via passing in wrapping classes. So, for example, a {@code Boolean} object
186     * would match a {@code boolean} primitive.</p>
187     *
188     * @param object invoke method on this object
189     * @param forceAccess force access to invoke method even if it's not accessible
190     * @param methodName get method with this name
191     * @param args use these arguments - treat null as empty array
192     * @param parameterTypes match these parameters - treat null as empty array
193     * @return The value returned by the invoked method
194     *
195     * @throws NoSuchMethodException if there is no such accessible method
196     * @throws InvocationTargetException wraps an exception thrown by the method invoked
197     * @throws IllegalAccessException if the requested method is not accessible via reflection
198     * @since 3.5
199     */
200    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
201            Object[] args, Class<?>[] parameterTypes)
202            throws NoSuchMethodException, IllegalAccessException,
203            InvocationTargetException {
204        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
205        args = ArrayUtils.nullToEmpty(args);
206
207        final String messagePrefix;
208        Method method = null;
209        boolean isOriginallyAccessible = false;
210        Object result = null;
211
212        try {
213            if (forceAccess) {
214                messagePrefix = "No such method: ";
215                method = getMatchingMethod(object.getClass(),
216                        methodName, parameterTypes);
217                if (method != null) {
218                    isOriginallyAccessible = method.isAccessible();
219                    if (!isOriginallyAccessible) {
220                        method.setAccessible(true);
221                    }
222                }
223            } else {
224                messagePrefix = "No such accessible method: ";
225                method = getMatchingAccessibleMethod(object.getClass(),
226                        methodName, parameterTypes);
227            }
228
229            if (method == null) {
230                throw new NoSuchMethodException(messagePrefix
231                        + methodName + "() on object: "
232                        + object.getClass().getName());
233            }
234            args = toVarArgs(method, args);
235
236            result = method.invoke(object, args);
237        }
238        finally {
239            if (method != null && forceAccess && method.isAccessible() != isOriginallyAccessible) {
240                method.setAccessible(isOriginallyAccessible);
241            }
242        }
243
244        return result;
245    }
246
247    /**
248     * <p>Invokes a named method whose parameter type matches the object type.</p>
249     *
250     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
251     *
252     * <p>This method supports calls to methods taking primitive parameters 
253     * via passing in wrapping classes. So, for example, a {@code Boolean} object
254     * would match a {@code boolean} primitive.</p>
255     *
256     * @param object invoke method on this object
257     * @param methodName get method with this name
258     * @param args use these arguments - treat null as empty array
259     * @param parameterTypes match these parameters - treat null as empty array
260     * @return The value returned by the invoked method
261     *
262     * @throws NoSuchMethodException if there is no such accessible method
263     * @throws InvocationTargetException wraps an exception thrown by the method invoked
264     * @throws IllegalAccessException if the requested method is not accessible via reflection
265     */
266    public static Object invokeMethod(final Object object, final String methodName, 
267            Object[] args, Class<?>[] parameterTypes)
268            throws NoSuchMethodException, IllegalAccessException,
269            InvocationTargetException {
270        return invokeMethod(object, false, methodName, args, parameterTypes);
271    }
272
273    /**
274     * <p>Invokes a method whose parameter types match exactly the object
275     * types.</p>
276     *
277     * <p>This uses reflection to invoke the method obtained from a call to
278     * {@link #getAccessibleMethod}(Class,String,Class[])}.</p>
279     *
280     * @param object invoke method on this object
281     * @param methodName get method with this name
282     * @return The value returned by the invoked method
283     *
284     * @throws NoSuchMethodException if there is no such accessible method
285     * @throws InvocationTargetException wraps an exception thrown by the
286     *  method invoked
287     * @throws IllegalAccessException if the requested method is not accessible
288     *  via reflection
289     *
290     * @since 3.4
291     */
292    public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
293            IllegalAccessException, InvocationTargetException {
294        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
295    }
296
297    /**
298     * <p>Invokes a method with no parameters.</p>
299     *
300     * <p>This uses reflection to invoke the method obtained from a call to
301     * {@link #getAccessibleMethod}(Class,String,Class[])}.</p>
302     *
303     * @param object invoke method on this object
304     * @param methodName get method with this name
305     * @param args use these arguments - treat null as empty array
306     * @return The value returned by the invoked method
307     *
308     * @throws NoSuchMethodException if there is no such accessible method
309     * @throws InvocationTargetException wraps an exception thrown by the
310     *  method invoked
311     * @throws IllegalAccessException if the requested method is not accessible
312     *  via reflection
313     */
314    public static Object invokeExactMethod(final Object object, final String methodName,
315            Object... args) throws NoSuchMethodException,
316            IllegalAccessException, InvocationTargetException {
317        args = ArrayUtils.nullToEmpty(args);
318        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
319        return invokeExactMethod(object, methodName, args, parameterTypes);
320    }
321
322    /**
323     * <p>Invokes a method whose parameter types match exactly the parameter
324     * types given.</p>
325     *
326     * <p>This uses reflection to invoke the method obtained from a call to
327     * {@link #getAccessibleMethod(Class,String,Class[])}.</p>
328     *
329     * @param object invoke method on this object
330     * @param methodName get method with this name
331     * @param args use these arguments - treat null as empty array
332     * @param parameterTypes match these parameters - treat {@code null} as empty array
333     * @return The value returned by the invoked method
334     *
335     * @throws NoSuchMethodException if there is no such accessible method
336     * @throws InvocationTargetException wraps an exception thrown by the
337     *  method invoked
338     * @throws IllegalAccessException if the requested method is not accessible
339     *  via reflection
340     */
341    public static Object invokeExactMethod(final Object object, final String methodName,
342            Object[] args, Class<?>[] parameterTypes)
343            throws NoSuchMethodException, IllegalAccessException,
344            InvocationTargetException {
345        args = ArrayUtils.nullToEmpty(args);
346        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
347        final Method method = getAccessibleMethod(object.getClass(), methodName,
348                parameterTypes);
349        if (method == null) {
350            throw new NoSuchMethodException("No such accessible method: "
351                    + methodName + "() on object: "
352                    + object.getClass().getName());
353        }
354        return method.invoke(object, args);
355    }
356
357    /**
358     * <p>Invokes a {@code static} method whose parameter types match exactly the parameter
359     * types given.</p>
360     *
361     * <p>This uses reflection to invoke the method obtained from a call to
362     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
363     *
364     * @param cls invoke static method on this class
365     * @param methodName get method with this name
366     * @param args use these arguments - treat {@code null} as empty array
367     * @param parameterTypes match these parameters - treat {@code null} as empty array
368     * @return The value returned by the invoked method
369     *
370     * @throws NoSuchMethodException if there is no such accessible method
371     * @throws InvocationTargetException wraps an exception thrown by the
372     *  method invoked
373     * @throws IllegalAccessException if the requested method is not accessible
374     *  via reflection
375     */
376    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
377            Object[] args, Class<?>[] parameterTypes)
378            throws NoSuchMethodException, IllegalAccessException,
379            InvocationTargetException {
380        args = ArrayUtils.nullToEmpty(args);
381        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
382        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
383        if (method == null) {
384            throw new NoSuchMethodException("No such accessible method: "
385                    + methodName + "() on class: " + cls.getName());
386        }
387        return method.invoke(null, args);
388    }
389
390    /**
391     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
392     *
393     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
394     *
395     * <p>This method supports calls to methods taking primitive parameters 
396     * via passing in wrapping classes. So, for example, a {@code Boolean} class
397     * would match a {@code boolean} primitive.</p>
398     *
399     * <p>This is a convenient wrapper for
400     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
401     * </p>
402     *
403     * @param cls invoke static method on this class
404     * @param methodName get method with this name
405     * @param args use these arguments - treat {@code null} as empty array
406     * @return The value returned by the invoked method
407     *
408     * @throws NoSuchMethodException if there is no such accessible method
409     * @throws InvocationTargetException wraps an exception thrown by the
410     *  method invoked
411     * @throws IllegalAccessException if the requested method is not accessible
412     *  via reflection
413     */
414    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
415            Object... args) throws NoSuchMethodException,
416            IllegalAccessException, InvocationTargetException {
417        args = ArrayUtils.nullToEmpty(args);
418        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
419        return invokeStaticMethod(cls, methodName, args, parameterTypes);
420    }
421
422    /**
423     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
424     *
425     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
426     *
427     * <p>This method supports calls to methods taking primitive parameters 
428     * via passing in wrapping classes. So, for example, a {@code Boolean} class
429     * would match a {@code boolean} primitive.</p>
430     *
431     *
432     * @param cls invoke static method on this class
433     * @param methodName get method with this name
434     * @param args use these arguments - treat {@code null} as empty array
435     * @param parameterTypes match these parameters - treat {@code null} as empty array
436     * @return The value returned by the invoked method
437     *
438     * @throws NoSuchMethodException if there is no such accessible method
439     * @throws InvocationTargetException wraps an exception thrown by the
440     *  method invoked
441     * @throws IllegalAccessException if the requested method is not accessible
442     *  via reflection
443     */
444    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
445            Object[] args, Class<?>[] parameterTypes)
446            throws NoSuchMethodException, IllegalAccessException,
447            InvocationTargetException {
448        args = ArrayUtils.nullToEmpty(args);
449        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
450        final Method method = getMatchingAccessibleMethod(cls, methodName,
451                parameterTypes);
452        if (method == null) {
453            throw new NoSuchMethodException("No such accessible method: "
454                    + methodName + "() on class: " + cls.getName());
455        }
456        args = toVarArgs(method, args);
457        return method.invoke(null, args);
458    }
459
460    private static Object[] toVarArgs(Method method, Object[] args) {
461        if (method.isVarArgs()) {
462            Class<?>[] methodParameterTypes = method.getParameterTypes();
463            args = getVarArgs(args, methodParameterTypes);
464        }
465        return args;
466    }
467
468    /**
469     * <p>Given an arguments array passed to a varargs method, return an array of arguments in the canonical form,
470     * i.e. an array with the declared number of parameters, and whose last parameter is an array of the varargs type.
471     * </p>
472     *
473     * @param args the array of arguments passed to the varags method
474     * @param methodParameterTypes the declared array of method parameter types
475     * @return an array of the variadic arguments passed to the method
476     * @since 3.5
477     */
478    static Object[] getVarArgs(Object[] args, Class<?>[] methodParameterTypes) {
479        if (args.length == methodParameterTypes.length
480                && args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1])) {
481            // The args array is already in the canonical form for the method.
482            return args;
483        }
484
485        // Construct a new array matching the method's declared parameter types.
486        Object[] newArgs = new Object[methodParameterTypes.length];
487
488        // Copy the normal (non-varargs) parameters
489        System.arraycopy(args, 0, newArgs, 0, methodParameterTypes.length - 1);
490
491        // Construct a new array for the variadic parameters
492        Class<?> varArgComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
493        int varArgLength = args.length - methodParameterTypes.length + 1;
494
495        Object varArgsArray = Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength);
496        // Copy the variadic arguments into the varargs array.
497        System.arraycopy(args, methodParameterTypes.length - 1, varArgsArray, 0, varArgLength);
498
499        if(varArgComponentType.isPrimitive()) {
500            // unbox from wrapper type to primitive type
501            varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
502        }
503
504        // Store the varargs array in the last position of the array to return
505        newArgs[methodParameterTypes.length - 1] = varArgsArray;
506
507        // Return the canonical varargs array.
508        return newArgs;
509    }
510
511    /**
512     * <p>Invokes a {@code static} method whose parameter types match exactly the object
513     * types.</p>
514     *
515     * <p>This uses reflection to invoke the method obtained from a call to
516     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
517     *
518     * @param cls invoke static method on this class
519     * @param methodName get method with this name
520     * @param args use these arguments - treat {@code null} as empty array
521     * @return The value returned by the invoked method
522     *
523     * @throws NoSuchMethodException if there is no such accessible method
524     * @throws InvocationTargetException wraps an exception thrown by the
525     *  method invoked
526     * @throws IllegalAccessException if the requested method is not accessible
527     *  via reflection
528     */
529    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
530            Object... args) throws NoSuchMethodException,
531            IllegalAccessException, InvocationTargetException {
532        args = ArrayUtils.nullToEmpty(args);
533        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
534        return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
535    }
536
537    /**
538     * <p>Returns an accessible method (that is, one that can be invoked via
539     * reflection) with given name and parameters. If no such method
540     * can be found, return {@code null}.
541     * This is just a convenience wrapper for
542     * {@link #getAccessibleMethod(Method)}.</p>
543     *
544     * @param cls get method from this class
545     * @param methodName get method with this name
546     * @param parameterTypes with these parameters types
547     * @return The accessible method
548     */
549    public static Method getAccessibleMethod(final Class<?> cls, final String methodName,
550            final Class<?>... parameterTypes) {
551        try {
552            return getAccessibleMethod(cls.getMethod(methodName,
553                    parameterTypes));
554        } catch (final NoSuchMethodException e) {
555            return null;
556        }
557    }
558
559    /**
560     * <p>Returns an accessible method (that is, one that can be invoked via
561     * reflection) that implements the specified Method. If no such method
562     * can be found, return {@code null}.</p>
563     *
564     * @param method The method that we wish to call
565     * @return The accessible method
566     */
567    public static Method getAccessibleMethod(Method method) {
568        if (!MemberUtils.isAccessible(method)) {
569            return null;
570        }
571        // If the declaring class is public, we are done
572        final Class<?> cls = method.getDeclaringClass();
573        if (Modifier.isPublic(cls.getModifiers())) {
574            return method;
575        }
576        final String methodName = method.getName();
577        final Class<?>[] parameterTypes = method.getParameterTypes();
578
579        // Check the implemented interfaces and subinterfaces
580        method = getAccessibleMethodFromInterfaceNest(cls, methodName,
581                parameterTypes);
582
583        // Check the superclass chain
584        if (method == null) {
585            method = getAccessibleMethodFromSuperclass(cls, methodName,
586                    parameterTypes);
587        }
588        return method;
589    }
590
591    /**
592     * <p>Returns an accessible method (that is, one that can be invoked via
593     * reflection) by scanning through the superclasses. If no such method
594     * can be found, return {@code null}.</p>
595     *
596     * @param cls Class to be checked
597     * @param methodName Method name of the method we wish to call
598     * @param parameterTypes The parameter type signatures
599     * @return the accessible method or {@code null} if not found
600     */
601    private static Method getAccessibleMethodFromSuperclass(final Class<?> cls,
602            final String methodName, final Class<?>... parameterTypes) {
603        Class<?> parentClass = cls.getSuperclass();
604        while (parentClass != null) {
605            if (Modifier.isPublic(parentClass.getModifiers())) {
606                try {
607                    return parentClass.getMethod(methodName, parameterTypes);
608                } catch (final NoSuchMethodException e) {
609                    return null;
610                }
611            }
612            parentClass = parentClass.getSuperclass();
613        }
614        return null;
615    }
616
617    /**
618     * <p>Returns an accessible method (that is, one that can be invoked via
619     * reflection) that implements the specified method, by scanning through
620     * all implemented interfaces and subinterfaces. If no such method
621     * can be found, return {@code null}.</p>
622     *
623     * <p>There isn't any good reason why this method must be {@code private}.
624     * It is because there doesn't seem any reason why other classes should
625     * call this rather than the higher level methods.</p>
626     *
627     * @param cls Parent class for the interfaces to be checked
628     * @param methodName Method name of the method we wish to call
629     * @param parameterTypes The parameter type signatures
630     * @return the accessible method or {@code null} if not found
631     */
632    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
633            final String methodName, final Class<?>... parameterTypes) {
634        // Search up the superclass chain
635        for (; cls != null; cls = cls.getSuperclass()) {
636
637            // Check the implemented interfaces of the parent class
638            final Class<?>[] interfaces = cls.getInterfaces();
639            for (int i = 0; i < interfaces.length; i++) {
640                // Is this interface public?
641                if (!Modifier.isPublic(interfaces[i].getModifiers())) {
642                    continue;
643                }
644                // Does the method exist on this interface?
645                try {
646                    return interfaces[i].getDeclaredMethod(methodName,
647                            parameterTypes);
648                } catch (final NoSuchMethodException e) { // NOPMD
649                    /*
650                     * Swallow, if no method is found after the loop then this
651                     * method returns null.
652                     */
653                }
654                // Recursively check our parent interfaces
655                final Method method = getAccessibleMethodFromInterfaceNest(interfaces[i],
656                        methodName, parameterTypes);
657                if (method != null) {
658                    return method;
659                }
660            }
661        }
662        return null;
663    }
664
665    /**
666     * <p>Finds an accessible method that matches the given name and has compatible parameters.
667     * Compatible parameters mean that every method parameter is assignable from 
668     * the given parameters.
669     * In other words, it finds a method with the given name 
670     * that will take the parameters given.</p>
671     *
672     * <p>This method is used by 
673     * {@link 
674     * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
675     * </p>
676     *
677     * <p>This method can match primitive parameter by passing in wrapper classes.
678     * For example, a {@code Boolean} will match a primitive {@code boolean}
679     * parameter.
680     * </p>
681     *
682     * @param cls find method in this class
683     * @param methodName find method with this name
684     * @param parameterTypes find method with most compatible parameters 
685     * @return The accessible method
686     */
687    public static Method getMatchingAccessibleMethod(final Class<?> cls,
688            final String methodName, final Class<?>... parameterTypes) {
689        try {
690            final Method method = cls.getMethod(methodName, parameterTypes);
691            MemberUtils.setAccessibleWorkaround(method);
692            return method;
693        } catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception
694        }
695        // search through all methods
696        Method bestMatch = null;
697        final Method[] methods = cls.getMethods();
698        for (final Method method : methods) {
699            // compare name and parameters
700            if (method.getName().equals(methodName) &&
701                    MemberUtils.isMatchingMethod(method, parameterTypes)) {
702                // get accessible version of method
703                final Method accessibleMethod = getAccessibleMethod(method);
704                if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(
705                            accessibleMethod,
706                            bestMatch,
707                            parameterTypes) < 0)) {
708                    bestMatch = accessibleMethod;
709                }
710            }
711        }
712        if (bestMatch != null) {
713            MemberUtils.setAccessibleWorkaround(bestMatch);
714        }
715        return bestMatch;
716    }
717    
718    /**
719     * <p>Retrieves a method whether or not it's accessible. If no such method
720     * can be found, return {@code null}.</p>
721     * @param cls The class that will be subjected to the method search
722     * @param methodName The method that we wish to call
723     * @param parameterTypes Argument class types
724     * @return The method
725     * 
726     * @since 3.5
727     */
728    public static Method getMatchingMethod(final Class<?> cls, final String methodName,
729            final Class<?>... parameterTypes) {
730        Validate.notNull(cls, "Null class not allowed.");
731        Validate.notEmpty(methodName, "Null or blank methodName not allowed.");
732
733        // Address methods in superclasses
734        Method[] methodArray = cls.getDeclaredMethods();
735        List<Class<?>> superclassList = ClassUtils.getAllSuperclasses(cls);
736        for (Class<?> klass : superclassList) {
737            methodArray = ArrayUtils.addAll(methodArray, klass.getDeclaredMethods());
738        }
739
740        Method inexactMatch = null;
741        for (Method method : methodArray) {
742            if (methodName.equals(method.getName()) &&
743                    ArrayUtils.isEquals(parameterTypes, method.getParameterTypes())) {
744                return method;
745            } else if (methodName.equals(method.getName()) &&
746                    ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) {
747                if (inexactMatch == null) {
748                    inexactMatch = method;
749                } else if (distance(parameterTypes, method.getParameterTypes())
750                        < distance(parameterTypes, inexactMatch.getParameterTypes())) {
751                    inexactMatch = method;
752                }
753            }
754
755        }
756        return inexactMatch;
757    }
758    
759    /**
760     * <p>Returns the aggregate number of inheritance hops between assignable argument class types.  Returns -1
761     * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.</p>
762     * @param classArray
763     * @param toClassArray
764     * @return the aggregate number of inheritance hops between assignable argument class types.
765     */
766    private static int distance(Class<?>[] classArray, Class<?>[] toClassArray) {
767        int answer = 0;
768
769        if (!ClassUtils.isAssignable(classArray, toClassArray, true)) {
770            return -1;
771        }
772        for (int offset = 0; offset < classArray.length; offset++) {
773            // Note InheritanceUtils.distance() uses different scoring system.
774            if (classArray[offset].equals(toClassArray[offset])) {
775                continue;
776            } else if (ClassUtils.isAssignable(classArray[offset], toClassArray[offset], true)
777                    && !ClassUtils.isAssignable(classArray[offset], toClassArray[offset], false)) {
778                answer++;
779            } else {
780                answer = answer + 2;
781            }
782        }
783
784        return answer;
785    }
786
787    /**
788     * Get the hierarchy of overridden methods down to {@code result} respecting generics.
789     * @param method lowest to consider
790     * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false
791     * @return Set&lt;Method&gt; in ascending order from sub- to superclass
792     * @throws NullPointerException if the specified method is {@code null}
793     * @since 3.2
794     */
795    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
796        Validate.notNull(method);
797        final Set<Method> result = new LinkedHashSet<Method>();
798        result.add(method);
799
800        final Class<?>[] parameterTypes = method.getParameterTypes();
801
802        final Class<?> declaringClass = method.getDeclaringClass();
803
804        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
805        //skip the declaring class :P
806        hierarchy.next();
807        hierarchyTraversal: while (hierarchy.hasNext()) {
808            final Class<?> c = hierarchy.next();
809            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
810            if (m == null) {
811                continue;
812            }
813            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
814                // matches without generics
815                result.add(m);
816                continue;
817            }
818            // necessary to get arguments every time in the case that we are including interfaces
819            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
820            for (int i = 0; i < parameterTypes.length; i++) {
821                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
822                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
823                if (!TypeUtils.equals(childType, parentType)) {
824                    continue hierarchyTraversal;
825                }
826            }
827            result.add(m);
828        }
829        return result;
830    }
831
832    /**
833     * Gets all methods of the given class that are annotated with the given annotation.
834     * @param cls
835     *            the {@link Class} to query
836     * @param annotationCls
837     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
838     * @return an array of Methods (possibly empty).
839     * @throws IllegalArgumentException
840     *            if the class or annotation are {@code null}
841     * @since 3.4
842     */
843    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
844        final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls);
845        return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]);
846    }
847
848    /**
849     * Gets all methods of the given class that are annotated with the given annotation.
850     * @param cls
851     *            the {@link Class} to query
852     * @param annotationCls
853     *            the {@link Annotation} that must be present on a method to be matched
854     * @return a list of Methods (possibly empty).
855     * @throws IllegalArgumentException
856     *            if the class or annotation are {@code null}
857     * @since 3.4
858     */
859    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
860        Validate.isTrue(cls != null, "The class must not be null");
861        Validate.isTrue(annotationCls != null, "The annotation class must not be null");
862        final Method[] allMethods = cls.getMethods();
863        final List<Method> annotatedMethods = new ArrayList<Method>();
864        for (final Method method : allMethods) {
865            if (method.getAnnotation(annotationCls) != null) {
866                annotatedMethods.add(method);
867            }
868        }
869        return annotatedMethods;
870    }
871
872}