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     */
017    package org.apache.commons.lang.reflect;
018    
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    import java.lang.reflect.Modifier;
022    
023    import org.apache.commons.lang.ArrayUtils;
024    import org.apache.commons.lang.ClassUtils;
025    
026    /**
027     * <p> Utility reflection methods focused on methods, originally from Commons BeanUtils.
028     * Differences from the BeanUtils version may be noted, especially where similar functionality
029     * already existed within Lang.
030     * </p>
031     *
032     * <h3>Known Limitations</h3>
033     * <h4>Accessing Public Methods In A Default Access Superclass</h4>
034     * <p>There is an issue when invoking public methods contained in a default access superclass on JREs prior to 1.4.
035     * Reflection locates these methods fine and correctly assigns them as public.
036     * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
037     *
038     * <p><code>MethodUtils</code> contains a workaround for this situation. 
039     * It will attempt to call <code>setAccessible</code> on this method.
040     * If this call succeeds, then the method can be invoked as normal.
041     * This call will only succeed when the application has sufficient security privileges. 
042     * If this call fails then the method may fail.</p>
043     *
044     * @author Apache Software Foundation
045     * @author Craig R. McClanahan
046     * @author Ralph Schaer
047     * @author Chris Audley
048     * @author Rey Fran&#231;ois
049     * @author Gregor Ra&#253;man
050     * @author Jan Sorensen
051     * @author Robert Burrell Donkin
052     * @author Matt Benson
053     * @since 2.5
054     * @version $Id: MethodUtils.java 911986 2010-02-19 21:19:05Z niallp $
055     */
056    public class MethodUtils {
057    
058        /**
059         * <p>MethodUtils instances should NOT be constructed in standard programming.
060         * Instead, the class should be used as
061         * <code>MethodUtils.getAccessibleMethod(method)</code>.</p>
062         *
063         * <p>This constructor is public to permit tools that require a JavaBean
064         * instance to operate.</p>
065         */
066        public MethodUtils() {
067            super();
068        }
069    
070        /**
071         * <p>Invoke a named method whose parameter type matches the object type.</p>
072         *
073         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
074         *
075         * <p>This method supports calls to methods taking primitive parameters 
076         * via passing in wrapping classes. So, for example, a <code>Boolean</code> object
077         * would match a <code>boolean</code> primitive.</p>
078         *
079         * <p> This is a convenient wrapper for
080         * {@link #invokeMethod(Object object, String methodName, Object[] args)}.
081         * </p>
082         *
083         * @param object invoke method on this object
084         * @param methodName get method with this name
085         * @param arg use this argument
086         * @return The value returned by the invoked method
087         *
088         * @throws NoSuchMethodException if there is no such accessible method
089         * @throws InvocationTargetException wraps an exception thrown by the method invoked
090         * @throws IllegalAccessException if the requested method is not accessible via reflection
091         */
092        public static Object invokeMethod(Object object, String methodName,
093                Object arg) throws NoSuchMethodException, IllegalAccessException,
094                InvocationTargetException {
095            return invokeMethod(object, methodName, new Object[] { arg });
096        }
097    
098        /**
099         * <p>Invoke a named method whose parameter type matches the object type.</p>
100         *
101         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
102         *
103         * <p>This method supports calls to methods taking primitive parameters 
104         * via passing in wrapping classes. So, for example, a <code>Boolean</code> object
105         * would match a <code>boolean</code> primitive.</p>
106         *
107         * <p> This is a convenient wrapper for
108         * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}.
109         * </p>
110         *
111         * @param object invoke method on this object
112         * @param methodName get method with this name
113         * @param args use these arguments - treat null as empty array
114         * @return The value returned by the invoked method
115         *
116         * @throws NoSuchMethodException if there is no such accessible method
117         * @throws InvocationTargetException wraps an exception thrown by the method invoked
118         * @throws IllegalAccessException if the requested method is not accessible via reflection
119         */
120        public static Object invokeMethod(Object object, String methodName,
121                Object[] args) throws NoSuchMethodException,
122                IllegalAccessException, InvocationTargetException {
123            if (args == null) {
124                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
125            }
126            int arguments = args.length;
127            Class[] parameterTypes = new Class[arguments];
128            for (int i = 0; i < arguments; i++) {
129                parameterTypes[i] = args[i].getClass();
130            }
131            return invokeMethod(object, methodName, args, parameterTypes);
132        }
133    
134        /**
135         * <p>Invoke a named method whose parameter type matches the object type.</p>
136         *
137         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
138         *
139         * <p>This method supports calls to methods taking primitive parameters 
140         * via passing in wrapping classes. So, for example, a <code>Boolean</code> object
141         * would match a <code>boolean</code> primitive.</p>
142         *
143         * @param object invoke method on this object
144         * @param methodName get method with this name
145         * @param args use these arguments - treat null as empty array
146         * @param parameterTypes match these parameters - treat null as empty array
147         * @return The value returned by the invoked method
148         *
149         * @throws NoSuchMethodException if there is no such accessible method
150         * @throws InvocationTargetException wraps an exception thrown by the method invoked
151         * @throws IllegalAccessException if the requested method is not accessible via reflection
152         */
153        public static Object invokeMethod(Object object, String methodName,
154                Object[] args, Class[] parameterTypes)
155                throws NoSuchMethodException, IllegalAccessException,
156                InvocationTargetException {
157            if (parameterTypes == null) {
158                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
159            }
160            if (args == null) {
161                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
162            }
163            Method method = getMatchingAccessibleMethod(object.getClass(),
164                    methodName, parameterTypes);
165            if (method == null) {
166                throw new NoSuchMethodException("No such accessible method: "
167                        + methodName + "() on object: "
168                        + object.getClass().getName());
169            }
170            return method.invoke(object, args);
171        }
172    
173        /**
174         * <p>Invoke a method whose parameter type matches exactly the object
175         * type.</p>
176         *
177         * <p> This is a convenient wrapper for
178         * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
179         * </p>
180         *
181         * @param object invoke method on this object
182         * @param methodName get method with this name
183         * @param arg use this argument
184         * @return The value returned by the invoked method
185         *
186         * @throws NoSuchMethodException if there is no such accessible method
187         * @throws InvocationTargetException wraps an exception thrown by the
188         *  method invoked
189         * @throws IllegalAccessException if the requested method is not accessible
190         *  via reflection
191         */
192        public static Object invokeExactMethod(Object object, String methodName,
193                Object arg) throws NoSuchMethodException, IllegalAccessException,
194                InvocationTargetException {
195            return invokeExactMethod(object, methodName, new Object[] { arg });
196        }
197    
198        /**
199         * <p>Invoke a method whose parameter types match exactly the object
200         * types.</p>
201         *
202         * <p> This uses reflection to invoke the method obtained from a call to
203         * <code>getAccessibleMethod()</code>.</p>
204         *
205         * @param object invoke method on this object
206         * @param methodName get method with this name
207         * @param args use these arguments - treat null as empty array
208         * @return The value returned by the invoked method
209         *
210         * @throws NoSuchMethodException if there is no such accessible method
211         * @throws InvocationTargetException wraps an exception thrown by the
212         *  method invoked
213         * @throws IllegalAccessException if the requested method is not accessible
214         *  via reflection
215         */
216        public static Object invokeExactMethod(Object object, String methodName,
217                Object[] args) throws NoSuchMethodException,
218                IllegalAccessException, InvocationTargetException {
219            if (args == null) {
220                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
221            }
222            int arguments = args.length;
223            Class[] parameterTypes = new Class[arguments];
224            for (int i = 0; i < arguments; i++) {
225                parameterTypes[i] = args[i].getClass();
226            }
227            return invokeExactMethod(object, methodName, args, parameterTypes);
228        }
229    
230        /**
231         * <p>Invoke a method whose parameter types match exactly the parameter
232         * types given.</p>
233         *
234         * <p>This uses reflection to invoke the method obtained from a call to
235         * <code>getAccessibleMethod()</code>.</p>
236         *
237         * @param object invoke method on this object
238         * @param methodName get method with this name
239         * @param args use these arguments - treat null as empty array
240         * @param parameterTypes match these parameters - treat null as empty array
241         * @return The value returned by the invoked method
242         *
243         * @throws NoSuchMethodException if there is no such accessible method
244         * @throws InvocationTargetException wraps an exception thrown by the
245         *  method invoked
246         * @throws IllegalAccessException if the requested method is not accessible
247         *  via reflection
248         */
249        public static Object invokeExactMethod(Object object, String methodName,
250                Object[] args, Class[] parameterTypes)
251                throws NoSuchMethodException, IllegalAccessException,
252                InvocationTargetException {
253            if (args == null) {
254                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
255            }
256            if (parameterTypes == null) {
257                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
258            }
259            Method method = getAccessibleMethod(object.getClass(), methodName,
260                    parameterTypes);
261            if (method == null) {
262                throw new NoSuchMethodException("No such accessible method: "
263                        + methodName + "() on object: "
264                        + object.getClass().getName());
265            }
266            return method.invoke(object, args);
267        }
268    
269        /**
270         * <p>Invoke a static method whose parameter types match exactly the parameter
271         * types given.</p>
272         *
273         * <p>This uses reflection to invoke the method obtained from a call to
274         * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
275         *
276         * @param cls invoke static method on this class
277         * @param methodName get method with this name
278         * @param args use these arguments - treat null as empty array
279         * @param parameterTypes match these parameters - treat null as empty array
280         * @return The value returned by the invoked method
281         *
282         * @throws NoSuchMethodException if there is no such accessible method
283         * @throws InvocationTargetException wraps an exception thrown by the
284         *  method invoked
285         * @throws IllegalAccessException if the requested method is not accessible
286         *  via reflection
287         */
288        public static Object invokeExactStaticMethod(Class cls, String methodName,
289                Object[] args, Class[] parameterTypes)
290                throws NoSuchMethodException, IllegalAccessException,
291                InvocationTargetException {
292            if (args == null) {
293                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
294            }
295            if (parameterTypes == null) {
296                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
297            }
298            Method method = getAccessibleMethod(cls, methodName, parameterTypes);
299            if (method == null) {
300                throw new NoSuchMethodException("No such accessible method: "
301                        + methodName + "() on class: " + cls.getName());
302            }
303            return method.invoke(null, args);
304        }
305    
306        /**
307         * <p>Invoke a named static method whose parameter type matches the object type.</p>
308         *
309         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
310         *
311         * <p>This method supports calls to methods taking primitive parameters 
312         * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
313         * would match a <code>boolean</code> primitive.</p>
314         *
315         * <p> This is a convenient wrapper for
316         * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.
317         * </p>
318         *
319         * @param cls invoke static method on this class
320         * @param methodName get method with this name
321         * @param arg use this argument
322         * @return The value returned by the invoked method
323         *
324         * @throws NoSuchMethodException if there is no such accessible method
325         * @throws InvocationTargetException wraps an exception thrown by the
326         *  method invoked
327         * @throws IllegalAccessException if the requested method is not accessible
328         *  via reflection
329         */
330        public static Object invokeStaticMethod(Class cls, String methodName,
331                Object arg) throws NoSuchMethodException, IllegalAccessException,
332                InvocationTargetException {
333            return invokeStaticMethod(cls, methodName, new Object[] { arg });
334        }
335    
336        /**
337         * <p>Invoke a named static method whose parameter type matches the object type.</p>
338         *
339         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
340         *
341         * <p>This method supports calls to methods taking primitive parameters 
342         * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
343         * would match a <code>boolean</code> primitive.</p>
344         *
345         * <p> This is a convenient wrapper for
346         * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.
347         * </p>
348         *
349         * @param cls invoke static method on this class
350         * @param methodName get method with this name
351         * @param args use these arguments - treat null as empty array
352         * @return The value returned by the invoked method
353         *
354         * @throws NoSuchMethodException if there is no such accessible method
355         * @throws InvocationTargetException wraps an exception thrown by the
356         *  method invoked
357         * @throws IllegalAccessException if the requested method is not accessible
358         *  via reflection
359         */
360        public static Object invokeStaticMethod(Class cls, String methodName,
361                Object[] args) throws NoSuchMethodException,
362                IllegalAccessException, InvocationTargetException {
363            if (args == null) {
364                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
365            }
366            int arguments = args.length;
367            Class[] parameterTypes = new Class[arguments];
368            for (int i = 0; i < arguments; i++) {
369                parameterTypes[i] = args[i].getClass();
370            }
371            return invokeStaticMethod(cls, methodName, args, parameterTypes);
372        }
373    
374        /**
375         * <p>Invoke a named static method whose parameter type matches the object type.</p>
376         *
377         * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
378         *
379         * <p>This method supports calls to methods taking primitive parameters 
380         * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
381         * would match a <code>boolean</code> primitive.</p>
382         *
383         *
384         * @param cls invoke static method on this class
385         * @param methodName get method with this name
386         * @param args use these arguments - treat null as empty array
387         * @param parameterTypes match these parameters - treat null as empty array
388         * @return The value returned by the invoked method
389         *
390         * @throws NoSuchMethodException if there is no such accessible method
391         * @throws InvocationTargetException wraps an exception thrown by the
392         *  method invoked
393         * @throws IllegalAccessException if the requested method is not accessible
394         *  via reflection
395         */
396        public static Object invokeStaticMethod(Class cls, String methodName,
397                Object[] args, Class[] parameterTypes)
398                throws NoSuchMethodException, IllegalAccessException,
399                InvocationTargetException {
400            if (parameterTypes == null) {
401                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
402            }
403            if (args == null) {
404                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
405            }
406            Method method = getMatchingAccessibleMethod(cls, methodName,
407                    parameterTypes);
408            if (method == null) {
409                throw new NoSuchMethodException("No such accessible method: "
410                        + methodName + "() on class: " + cls.getName());
411            }
412            return method.invoke(null, args);
413        }
414    
415        /**
416         * <p>Invoke a static method whose parameter type matches exactly the object
417         * type.</p>
418         *
419         * <p> This is a convenient wrapper for
420         * {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.
421         * </p>
422         *
423         * @param cls invoke static method on this class
424         * @param methodName get method with this name
425         * @param arg use this argument
426         * @return The value returned by the invoked method
427         *
428         * @throws NoSuchMethodException if there is no such accessible method
429         * @throws InvocationTargetException wraps an exception thrown by the
430         *  method invoked
431         * @throws IllegalAccessException if the requested method is not accessible
432         *  via reflection
433         */
434        public static Object invokeExactStaticMethod(Class cls, String methodName,
435                Object arg) throws NoSuchMethodException, IllegalAccessException,
436                InvocationTargetException {
437            return invokeExactStaticMethod(cls, methodName, new Object[] { arg });
438        }
439    
440        /**
441         * <p>Invoke a static method whose parameter types match exactly the object
442         * types.</p>
443         *
444         * <p> This uses reflection to invoke the method obtained from a call to
445         * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
446         *
447         * @param cls invoke static method on this class
448         * @param methodName get method with this name
449         * @param args use these arguments - treat null as empty array
450         * @return The value returned by the invoked method
451         *
452         * @throws NoSuchMethodException if there is no such accessible method
453         * @throws InvocationTargetException wraps an exception thrown by the
454         *  method invoked
455         * @throws IllegalAccessException if the requested method is not accessible
456         *  via reflection
457         */
458        public static Object invokeExactStaticMethod(Class cls, String methodName,
459                Object[] args) throws NoSuchMethodException,
460                IllegalAccessException, InvocationTargetException {
461            if (args == null) {
462                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
463            }
464            int arguments = args.length;
465            Class[] parameterTypes = new Class[arguments];
466            for (int i = 0; i < arguments; i++) {
467                parameterTypes[i] = args[i].getClass();
468            }
469            return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
470        }
471    
472        /**
473         * <p>Return an accessible method (that is, one that can be invoked via
474         * reflection) with given name and a single parameter.  If no such method
475         * can be found, return <code>null</code>.
476         * Basically, a convenience wrapper that constructs a <code>Class</code>
477         * array for you.</p>
478         *
479         * @param cls get method from this class
480         * @param methodName get method with this name
481         * @param parameterType taking this type of parameter
482         * @return The accessible method
483         */
484        public static Method getAccessibleMethod(Class cls, String methodName,
485                Class parameterType) {
486            return getAccessibleMethod(cls, methodName,
487                    new Class[] { parameterType });
488        }
489    
490        /**
491         * <p>Return an accessible method (that is, one that can be invoked via
492         * reflection) with given name and parameters.  If no such method
493         * can be found, return <code>null</code>.
494         * This is just a convenient wrapper for
495         * {@link #getAccessibleMethod(Method method)}.</p>
496         *
497         * @param cls get method from this class
498         * @param methodName get method with this name
499         * @param parameterTypes with these parameters types
500         * @return The accessible method
501         */
502        public static Method getAccessibleMethod(Class cls, String methodName,
503                Class[] parameterTypes) {
504            try {
505                return getAccessibleMethod(cls.getMethod(methodName,
506                        parameterTypes));
507            } catch (NoSuchMethodException e) {
508                return (null);
509            }
510        }
511    
512        /**
513         * <p>Return an accessible method (that is, one that can be invoked via
514         * reflection) that implements the specified Method.  If no such method
515         * can be found, return <code>null</code>.</p>
516         *
517         * @param method The method that we wish to call
518         * @return The accessible method
519         */
520        public static Method getAccessibleMethod(Method method) {
521            if (!MemberUtils.isAccessible(method)) {
522                return null;
523            }
524            // If the declaring class is public, we are done
525            Class cls = method.getDeclaringClass();
526            if (Modifier.isPublic(cls.getModifiers())) {
527                return method;
528            }
529            String methodName = method.getName();
530            Class[] parameterTypes = method.getParameterTypes();
531    
532            // Check the implemented interfaces and subinterfaces
533            method = getAccessibleMethodFromInterfaceNest(cls, methodName,
534                    parameterTypes);
535    
536            // Check the superclass chain
537            if (method == null) {
538                method = getAccessibleMethodFromSuperclass(cls, methodName,
539                        parameterTypes);
540            }
541            return method;
542        }
543    
544        /**
545         * <p>Return an accessible method (that is, one that can be invoked via
546         * reflection) by scanning through the superclasses. If no such method
547         * can be found, return <code>null</code>.</p>
548         *
549         * @param cls Class to be checked
550         * @param methodName Method name of the method we wish to call
551         * @param parameterTypes The parameter type signatures
552         * @return the accessible method or <code>null</code> if not found
553         */
554        private static Method getAccessibleMethodFromSuperclass(Class cls,
555                String methodName, Class[] parameterTypes) {
556            Class parentClass = cls.getSuperclass();
557            while (parentClass != null) {
558                if (Modifier.isPublic(parentClass.getModifiers())) {
559                    try {
560                        return parentClass.getMethod(methodName, parameterTypes);
561                    } catch (NoSuchMethodException e) {
562                        return null;
563                    }
564                }
565                parentClass = parentClass.getSuperclass();
566            }
567            return null;
568        }
569    
570        /**
571         * <p>Return an accessible method (that is, one that can be invoked via
572         * reflection) that implements the specified method, by scanning through
573         * all implemented interfaces and subinterfaces.  If no such method
574         * can be found, return <code>null</code>.</p>
575         *
576         * <p> There isn't any good reason why this method must be private.
577         * It is because there doesn't seem any reason why other classes should
578         * call this rather than the higher level methods.</p>
579         *
580         * @param cls Parent class for the interfaces to be checked
581         * @param methodName Method name of the method we wish to call
582         * @param parameterTypes The parameter type signatures
583         * @return the accessible method or <code>null</code> if not found
584         */
585        private static Method getAccessibleMethodFromInterfaceNest(Class cls,
586                String methodName, Class[] parameterTypes) {
587            Method method = null;
588    
589            // Search up the superclass chain
590            for (; cls != null; cls = cls.getSuperclass()) {
591    
592                // Check the implemented interfaces of the parent class
593                Class[] interfaces = cls.getInterfaces();
594                for (int i = 0; i < interfaces.length; i++) {
595                    // Is this interface public?
596                    if (!Modifier.isPublic(interfaces[i].getModifiers())) {
597                        continue;
598                    }
599                    // Does the method exist on this interface?
600                    try {
601                        method = interfaces[i].getDeclaredMethod(methodName,
602                                parameterTypes);
603                    } catch (NoSuchMethodException e) {
604                        /*
605                         * Swallow, if no method is found after the loop then this
606                         * method returns null.
607                         */
608                    }
609                    if (method != null) {
610                        break;
611                    }
612                    // Recursively check our parent interfaces
613                    method = getAccessibleMethodFromInterfaceNest(interfaces[i],
614                            methodName, parameterTypes);
615                    if (method != null) {
616                        break;
617                    }
618                }
619            }
620            return method;
621        }
622    
623        /**
624         * <p>Find an accessible method that matches the given name and has compatible parameters.
625         * Compatible parameters mean that every method parameter is assignable from 
626         * the given parameters.
627         * In other words, it finds a method with the given name 
628         * that will take the parameters given.<p>
629         *
630         * <p>This method is used by 
631         * {@link 
632         * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
633         *
634         * <p>This method can match primitive parameter by passing in wrapper classes.
635         * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
636         * parameter.
637         *
638         * @param cls find method in this class
639         * @param methodName find method with this name
640         * @param parameterTypes find method with most compatible parameters 
641         * @return The accessible method
642         */
643        public static Method getMatchingAccessibleMethod(Class cls,
644                String methodName, Class[] parameterTypes) {
645            try {
646                Method method = cls.getMethod(methodName, parameterTypes);
647                MemberUtils.setAccessibleWorkaround(method);
648                return method;
649            } catch (NoSuchMethodException e) { /* SWALLOW */
650            }
651            // search through all methods
652            Method bestMatch = null;
653            Method[] methods = cls.getMethods();
654            for (int i = 0, size = methods.length; i < size; i++) {
655                if (methods[i].getName().equals(methodName)) {
656                    // compare parameters
657                    if (ClassUtils.isAssignable(parameterTypes, methods[i]
658                            .getParameterTypes(), true)) {
659                        // get accessible version of method
660                        Method accessibleMethod = getAccessibleMethod(methods[i]);
661                        if (accessibleMethod != null) {
662                            if (bestMatch == null
663                                    || MemberUtils.compareParameterTypes(
664                                            accessibleMethod.getParameterTypes(),
665                                            bestMatch.getParameterTypes(),
666                                            parameterTypes) < 0) {
667                                bestMatch = accessibleMethod;
668                            }
669                        }
670                    }
671                }
672            }
673            if (bestMatch != null) {
674                MemberUtils.setAccessibleWorkaround(bestMatch);
675            }
676            return bestMatch;
677        }
678    }