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.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.lang.reflect.Modifier;
023import java.lang.reflect.Type;
024import java.lang.reflect.TypeVariable;
025import java.util.Arrays;
026import java.util.ArrayList;
027import java.util.Iterator;
028import java.util.LinkedHashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032
033import org.apache.commons.lang3.ArrayUtils;
034import org.apache.commons.lang3.ClassUtils;
035import org.apache.commons.lang3.ClassUtils.Interfaces;
036import org.apache.commons.lang3.Validate;
037
038/**
039 * <p>Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
040 * Differences from the BeanUtils version may be noted, especially where similar functionality
041 * already existed within Lang.
042 * </p>
043 *
044 * <h3>Known Limitations</h3>
045 * <h4>Accessing Public Methods In A Default Access Superclass</h4>
046 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
047 * Reflection locates these methods fine and correctly assigns them as {@code public}.
048 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
049 *
050 * <p>{@link MethodUtils} contains a workaround for this situation. 
051 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
052 * If this call succeeds, then the method can be invoked as normal.
053 * This call will only succeed when the application has sufficient security privileges. 
054 * If this call fails then the method may fail.</p>
055 *
056 * @since 2.5
057 * @version $Id: MethodUtils.java 1630277 2014-10-09 04:37:38Z ggregory $
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 whose parameter type matches the object type.</p>
099     *
100     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
101     *
102     * <p>This method supports calls to methods taking primitive parameters 
103     * via passing in wrapping classes. So, for example, a {@code Boolean} object
104     * would match a {@code boolean} primitive.</p>
105     *
106     * <p>This is a convenient wrapper for
107     * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}.
108     * </p>
109     *
110     * @param object invoke method on this object
111     * @param methodName get method with this name
112     * @param args use these arguments - treat null as empty array
113     * @return The value returned by the invoked method
114     *
115     * @throws NoSuchMethodException if there is no such accessible method
116     * @throws InvocationTargetException wraps an exception thrown by the method invoked
117     * @throws IllegalAccessException if the requested method is not accessible via reflection
118     */
119    public static Object invokeMethod(final Object object, final String methodName,
120            Object... args) throws NoSuchMethodException,
121            IllegalAccessException, InvocationTargetException {
122        args = ArrayUtils.nullToEmpty(args);
123        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
124        return invokeMethod(object, methodName, args, parameterTypes);
125    }
126
127    /**
128     * <p>Invokes a named method whose parameter type matches the object type.</p>
129     *
130     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
131     *
132     * <p>This method supports calls to methods taking primitive parameters 
133     * via passing in wrapping classes. So, for example, a {@code Boolean} object
134     * would match a {@code boolean} primitive.</p>
135     *
136     * @param object invoke method on this object
137     * @param methodName get method with this name
138     * @param args use these arguments - treat null as empty array
139     * @param parameterTypes match these parameters - treat null as empty array
140     * @return The value returned by the invoked method
141     *
142     * @throws NoSuchMethodException if there is no such accessible method
143     * @throws InvocationTargetException wraps an exception thrown by the method invoked
144     * @throws IllegalAccessException if the requested method is not accessible via reflection
145     */
146    public static Object invokeMethod(final Object object, final String methodName,
147            Object[] args, Class<?>[] parameterTypes)
148            throws NoSuchMethodException, IllegalAccessException,
149            InvocationTargetException {
150        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
151        args = ArrayUtils.nullToEmpty(args);
152        final Method method = getMatchingAccessibleMethod(object.getClass(),
153                methodName, parameterTypes);
154        if (method == null) {
155            throw new NoSuchMethodException("No such accessible method: "
156                    + methodName + "() on object: "
157                    + object.getClass().getName());
158        }
159        return method.invoke(object, args);
160    }
161
162    /**
163     * <p>Invokes a method whose parameter types match exactly the object
164     * types.</p>
165     *
166     * <p>This uses reflection to invoke the method obtained from a call to
167     * {@link #getAccessibleMethod}(Class,String,Class[])}.</p>
168     *
169     * @param object invoke method on this object
170     * @param methodName get method with this name
171     * @return The value returned by the invoked method
172     *
173     * @throws NoSuchMethodException if there is no such accessible method
174     * @throws InvocationTargetException wraps an exception thrown by the
175     *  method invoked
176     * @throws IllegalAccessException if the requested method is not accessible
177     *  via reflection
178     *  
179     *  @since 3.4
180     */
181    public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
182            IllegalAccessException, InvocationTargetException {
183        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
184    }
185
186    /**
187     * <p>Invokes a method with no parameters.</p>
188     *
189     * <p>This uses reflection to invoke the method obtained from a call to
190     * {@link #getAccessibleMethod}(Class,String,Class[])}.</p>
191     *
192     * @param object invoke method on this object
193     * @param methodName get method with this name
194     * @param args use these arguments - treat null as empty array
195     * @return The value returned by the invoked method
196     *
197     * @throws NoSuchMethodException if there is no such accessible method
198     * @throws InvocationTargetException wraps an exception thrown by the
199     *  method invoked
200     * @throws IllegalAccessException if the requested method is not accessible
201     *  via reflection
202     */
203    public static Object invokeExactMethod(final Object object, final String methodName,
204            Object... args) throws NoSuchMethodException,
205            IllegalAccessException, InvocationTargetException {
206        args = ArrayUtils.nullToEmpty(args);
207        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
208        return invokeExactMethod(object, methodName, args, parameterTypes);
209    }
210
211    /**
212     * <p>Invokes a method whose parameter types match exactly the parameter
213     * types given.</p>
214     *
215     * <p>This uses reflection to invoke the method obtained from a call to
216     * {@link #getAccessibleMethod(Class,String,Class[])}.</p>
217     *
218     * @param object invoke method on this object
219     * @param methodName get method with this name
220     * @param args use these arguments - treat null as empty array
221     * @param parameterTypes match these parameters - treat {@code null} as empty array
222     * @return The value returned by the invoked method
223     *
224     * @throws NoSuchMethodException if there is no such accessible method
225     * @throws InvocationTargetException wraps an exception thrown by the
226     *  method invoked
227     * @throws IllegalAccessException if the requested method is not accessible
228     *  via reflection
229     */
230    public static Object invokeExactMethod(final Object object, final String methodName,
231            Object[] args, Class<?>[] parameterTypes)
232            throws NoSuchMethodException, IllegalAccessException,
233            InvocationTargetException {
234        args = ArrayUtils.nullToEmpty(args);
235        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
236        final Method method = getAccessibleMethod(object.getClass(), methodName,
237                parameterTypes);
238        if (method == null) {
239            throw new NoSuchMethodException("No such accessible method: "
240                    + methodName + "() on object: "
241                    + object.getClass().getName());
242        }
243        return method.invoke(object, args);
244    }
245
246    /**
247     * <p>Invokes a {@code static} method whose parameter types match exactly the parameter
248     * types given.</p>
249     *
250     * <p>This uses reflection to invoke the method obtained from a call to
251     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
252     *
253     * @param cls invoke static method on this class
254     * @param methodName get method with this name
255     * @param args use these arguments - treat {@code null} as empty array
256     * @param parameterTypes match these parameters - treat {@code null} as empty array
257     * @return The value returned by the invoked method
258     *
259     * @throws NoSuchMethodException if there is no such accessible method
260     * @throws InvocationTargetException wraps an exception thrown by the
261     *  method invoked
262     * @throws IllegalAccessException if the requested method is not accessible
263     *  via reflection
264     */
265    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
266            Object[] args, Class<?>[] parameterTypes)
267            throws NoSuchMethodException, IllegalAccessException,
268            InvocationTargetException {
269        args = ArrayUtils.nullToEmpty(args);
270        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
271        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
272        if (method == null) {
273            throw new NoSuchMethodException("No such accessible method: "
274                    + methodName + "() on class: " + cls.getName());
275        }
276        return method.invoke(null, args);
277    }
278
279    /**
280     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
281     *
282     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
283     *
284     * <p>This method supports calls to methods taking primitive parameters 
285     * via passing in wrapping classes. So, for example, a {@code Boolean} class
286     * would match a {@code boolean} primitive.</p>
287     *
288     * <p>This is a convenient wrapper for
289     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
290     * </p>
291     *
292     * @param cls invoke static method on this class
293     * @param methodName get method with this name
294     * @param args use these arguments - treat {@code null} as empty array
295     * @return The value returned by the invoked method
296     *
297     * @throws NoSuchMethodException if there is no such accessible method
298     * @throws InvocationTargetException wraps an exception thrown by the
299     *  method invoked
300     * @throws IllegalAccessException if the requested method is not accessible
301     *  via reflection
302     */
303    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
304            Object... args) throws NoSuchMethodException,
305            IllegalAccessException, InvocationTargetException {
306        args = ArrayUtils.nullToEmpty(args);
307        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
308        return invokeStaticMethod(cls, methodName, args, parameterTypes);
309    }
310
311    /**
312     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
313     *
314     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
315     *
316     * <p>This method supports calls to methods taking primitive parameters 
317     * via passing in wrapping classes. So, for example, a {@code Boolean} class
318     * would match a {@code boolean} primitive.</p>
319     *
320     *
321     * @param cls invoke static method on this class
322     * @param methodName get method with this name
323     * @param args use these arguments - treat {@code null} as empty array
324     * @param parameterTypes match these parameters - treat {@code null} as empty array
325     * @return The value returned by the invoked method
326     *
327     * @throws NoSuchMethodException if there is no such accessible method
328     * @throws InvocationTargetException wraps an exception thrown by the
329     *  method invoked
330     * @throws IllegalAccessException if the requested method is not accessible
331     *  via reflection
332     */
333    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
334            Object[] args, Class<?>[] parameterTypes)
335            throws NoSuchMethodException, IllegalAccessException,
336            InvocationTargetException {
337        args = ArrayUtils.nullToEmpty(args);
338        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
339        final Method method = getMatchingAccessibleMethod(cls, methodName,
340                parameterTypes);
341        if (method == null) {
342            throw new NoSuchMethodException("No such accessible method: "
343                    + methodName + "() on class: " + cls.getName());
344        }
345        return method.invoke(null, args);
346    }
347
348    /**
349     * <p>Invokes a {@code static} method whose parameter types match exactly the object
350     * types.</p>
351     *
352     * <p>This uses reflection to invoke the method obtained from a call to
353     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
354     *
355     * @param cls invoke static method on this class
356     * @param methodName get method with this name
357     * @param args use these arguments - treat {@code null} as empty array
358     * @return The value returned by the invoked method
359     *
360     * @throws NoSuchMethodException if there is no such accessible method
361     * @throws InvocationTargetException wraps an exception thrown by the
362     *  method invoked
363     * @throws IllegalAccessException if the requested method is not accessible
364     *  via reflection
365     */
366    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
367            Object... args) throws NoSuchMethodException,
368            IllegalAccessException, InvocationTargetException {
369        args = ArrayUtils.nullToEmpty(args);
370        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
371        return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
372    }
373
374    /**
375     * <p>Returns an accessible method (that is, one that can be invoked via
376     * reflection) with given name and parameters. If no such method
377     * can be found, return {@code null}.
378     * This is just a convenience wrapper for
379     * {@link #getAccessibleMethod(Method)}.</p>
380     *
381     * @param cls get method from this class
382     * @param methodName get method with this name
383     * @param parameterTypes with these parameters types
384     * @return The accessible method
385     */
386    public static Method getAccessibleMethod(final Class<?> cls, final String methodName,
387            final Class<?>... parameterTypes) {
388        try {
389            return getAccessibleMethod(cls.getMethod(methodName,
390                    parameterTypes));
391        } catch (final NoSuchMethodException e) {
392            return null;
393        }
394    }
395
396    /**
397     * <p>Returns an accessible method (that is, one that can be invoked via
398     * reflection) that implements the specified Method. If no such method
399     * can be found, return {@code null}.</p>
400     *
401     * @param method The method that we wish to call
402     * @return The accessible method
403     */
404    public static Method getAccessibleMethod(Method method) {
405        if (!MemberUtils.isAccessible(method)) {
406            return null;
407        }
408        // If the declaring class is public, we are done
409        final Class<?> cls = method.getDeclaringClass();
410        if (Modifier.isPublic(cls.getModifiers())) {
411            return method;
412        }
413        final String methodName = method.getName();
414        final Class<?>[] parameterTypes = method.getParameterTypes();
415
416        // Check the implemented interfaces and subinterfaces
417        method = getAccessibleMethodFromInterfaceNest(cls, methodName,
418                parameterTypes);
419
420        // Check the superclass chain
421        if (method == null) {
422            method = getAccessibleMethodFromSuperclass(cls, methodName,
423                    parameterTypes);
424        }
425        return method;
426    }
427
428    /**
429     * <p>Returns an accessible method (that is, one that can be invoked via
430     * reflection) by scanning through the superclasses. If no such method
431     * can be found, return {@code null}.</p>
432     *
433     * @param cls Class to be checked
434     * @param methodName Method name of the method we wish to call
435     * @param parameterTypes The parameter type signatures
436     * @return the accessible method or {@code null} if not found
437     */
438    private static Method getAccessibleMethodFromSuperclass(final Class<?> cls,
439            final String methodName, final Class<?>... parameterTypes) {
440        Class<?> parentClass = cls.getSuperclass();
441        while (parentClass != null) {
442            if (Modifier.isPublic(parentClass.getModifiers())) {
443                try {
444                    return parentClass.getMethod(methodName, parameterTypes);
445                } catch (final NoSuchMethodException e) {
446                    return null;
447                }
448            }
449            parentClass = parentClass.getSuperclass();
450        }
451        return null;
452    }
453
454    /**
455     * <p>Returns an accessible method (that is, one that can be invoked via
456     * reflection) that implements the specified method, by scanning through
457     * all implemented interfaces and subinterfaces. If no such method
458     * can be found, return {@code null}.</p>
459     *
460     * <p>There isn't any good reason why this method must be {@code private}.
461     * It is because there doesn't seem any reason why other classes should
462     * call this rather than the higher level methods.</p>
463     *
464     * @param cls Parent class for the interfaces to be checked
465     * @param methodName Method name of the method we wish to call
466     * @param parameterTypes The parameter type signatures
467     * @return the accessible method or {@code null} if not found
468     */
469    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
470            final String methodName, final Class<?>... parameterTypes) {
471        // Search up the superclass chain
472        for (; cls != null; cls = cls.getSuperclass()) {
473
474            // Check the implemented interfaces of the parent class
475            final Class<?>[] interfaces = cls.getInterfaces();
476            for (int i = 0; i < interfaces.length; i++) {
477                // Is this interface public?
478                if (!Modifier.isPublic(interfaces[i].getModifiers())) {
479                    continue;
480                }
481                // Does the method exist on this interface?
482                try {
483                    return interfaces[i].getDeclaredMethod(methodName,
484                            parameterTypes);
485                } catch (final NoSuchMethodException e) { // NOPMD
486                    /*
487                     * Swallow, if no method is found after the loop then this
488                     * method returns null.
489                     */
490                }
491                // Recursively check our parent interfaces
492                final Method method = getAccessibleMethodFromInterfaceNest(interfaces[i],
493                        methodName, parameterTypes);
494                if (method != null) {
495                    return method;
496                }
497            }
498        }
499        return null;
500    }
501
502    /**
503     * <p>Finds an accessible method that matches the given name and has compatible parameters.
504     * Compatible parameters mean that every method parameter is assignable from 
505     * the given parameters.
506     * In other words, it finds a method with the given name 
507     * that will take the parameters given.</p>
508     *
509     * <p>This method is used by 
510     * {@link 
511     * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
512     * </p>
513     *
514     * <p>This method can match primitive parameter by passing in wrapper classes.
515     * For example, a {@code Boolean} will match a primitive {@code boolean}
516     * parameter.
517     * </p>
518     *
519     * @param cls find method in this class
520     * @param methodName find method with this name
521     * @param parameterTypes find method with most compatible parameters 
522     * @return The accessible method
523     */
524    public static Method getMatchingAccessibleMethod(final Class<?> cls,
525            final String methodName, final Class<?>... parameterTypes) {
526        try {
527            final Method method = cls.getMethod(methodName, parameterTypes);
528            MemberUtils.setAccessibleWorkaround(method);
529            return method;
530        } catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception
531        }
532        // search through all methods
533        Method bestMatch = null;
534        final Method[] methods = cls.getMethods();
535        for (final Method method : methods) {
536            // compare name and parameters
537            if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) {
538                // get accessible version of method
539                final Method accessibleMethod = getAccessibleMethod(method);
540                if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareParameterTypes(
541                            accessibleMethod.getParameterTypes(),
542                            bestMatch.getParameterTypes(),
543                            parameterTypes) < 0)) {
544                        bestMatch = accessibleMethod;
545                 }
546            }
547        }
548        if (bestMatch != null) {
549            MemberUtils.setAccessibleWorkaround(bestMatch);
550        }
551        return bestMatch;
552    }
553
554    /**
555     * Get the hierarchy of overridden methods down to {@code result} respecting generics.
556     * @param method lowest to consider
557     * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false
558     * @return Set&lt;Method&gt; in ascending order from sub- to superclass
559     * @throws NullPointerException if the specified method is {@code null}
560     * @since 3.2
561     */
562    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
563        Validate.notNull(method);
564        final Set<Method> result = new LinkedHashSet<Method>();
565        result.add(method);
566
567        final Class<?>[] parameterTypes = method.getParameterTypes();
568
569        final Class<?> declaringClass = method.getDeclaringClass();
570
571        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
572        //skip the declaring class :P
573        hierarchy.next();
574        hierarchyTraversal: while (hierarchy.hasNext()) {
575            final Class<?> c = hierarchy.next();
576            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
577            if (m == null) {
578                continue;
579            }
580            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
581                // matches without generics
582                result.add(m);
583                continue;
584            }
585            // necessary to get arguments every time in the case that we are including interfaces
586            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
587            for (int i = 0; i < parameterTypes.length; i++) {
588                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
589                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
590                if (!TypeUtils.equals(childType, parentType)) {
591                    continue hierarchyTraversal;
592                }
593            }
594            result.add(m);
595        }
596        return result;
597    }
598
599    /**
600     * Gets all methods of the given class that are annotated with the given annotation.
601     * @param cls
602     *            the {@link Class} to query
603     * @param annotationCls
604     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
605     * @return an array of Methods (possibly empty).
606     * @throws IllegalArgumentException
607     *            if the class or annotation are {@code null}
608     * @since 3.4
609     */
610    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
611        final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls);
612        return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]);
613    }
614
615    /**
616     * Gets all methods of the given class that are annotated with the given annotation.
617     * @param cls
618     *            the {@link Class} to query
619     * @param annotationCls
620     *            the {@link Annotation} that must be present on a method to be matched
621     * @return a list of Methods (possibly empty).
622     * @throws IllegalArgumentException
623     *            if the class or annotation are {@code null}
624     * @since 3.4
625     */
626    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
627        Validate.isTrue(cls != null, "The class must not be null");
628        Validate.isTrue(annotationCls != null, "The annotation class must not be null");
629        final Method[] allMethods = cls.getMethods();
630        final List<Method> annotatedMethods = new ArrayList<Method>();
631        for (final Method method : allMethods) {
632            if (method.getAnnotation(annotationCls) != null) {
633                annotatedMethods.add(method);
634            }
635        }
636        return annotatedMethods;
637    }
638
639}