    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    }