001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.beanutils;
019
020import java.lang.ref.Reference;
021import java.lang.ref.WeakReference;
022import java.lang.reflect.InvocationTargetException;
023import java.lang.reflect.Method;
024import java.lang.reflect.Modifier;
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.Map;
028import java.util.WeakHashMap;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032
033/**
034 * <p>Utility reflection methods focused on methods in general rather than properties in particular.</p>
035 *
036 * <strong>Known Limitations: Accessing Public Methods In A Default Access Superclass</strong>
037 * <p>There is an issue when invoking public methods contained in a default access superclass.
038 * Reflection locates these methods fine and correctly assigns them as public.
039 * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
040 *
041 * <p><code>MethodUtils</code> contains a workaround for this situation.
042 * It will attempt to call <code>setAccessible</code> on this method.
043 * If this call succeeds, then the method can be invoked as normal.
044 * This call will only succeed when the application has sufficient security privileges.
045 * If this call fails then a warning will be logged and the method may fail.</p>
046 *
047 */
048public class MethodUtils {
049
050    /**
051     * Represents the key to looking up a Method by reflection.
052     */
053    private static class MethodDescriptor {
054        private final Class<?> cls;
055        private final String methodName;
056        private final Class<?>[] paramTypes;
057        private final boolean exact;
058        private final int hashCode;
059
060        /**
061         * The sole constructor.
062         *
063         * @param cls  the class to reflect, must not be null
064         * @param methodName  the method name to obtain
065         * @param paramTypes the array of classes representing the parameter types
066         * @param exact whether the match has to be exact.
067         */
068        public MethodDescriptor(final Class<?> cls, final String methodName, Class<?>[] paramTypes, final boolean exact) {
069            if (cls == null) {
070                throw new IllegalArgumentException("Class cannot be null");
071            }
072            if (methodName == null) {
073                throw new IllegalArgumentException("Method Name cannot be null");
074            }
075            if (paramTypes == null) {
076                paramTypes = EMPTY_CLASS_PARAMETERS;
077            }
078
079            this.cls = cls;
080            this.methodName = methodName;
081            this.paramTypes = paramTypes;
082            this.exact= exact;
083
084            this.hashCode = methodName.length();
085        }
086        /**
087         * Checks for equality.
088         * @param obj object to be tested for equality
089         * @return true, if the object describes the same Method.
090         */
091        @Override
092        public boolean equals(final Object obj) {
093            if (!(obj instanceof MethodDescriptor)) {
094                return false;
095            }
096            final MethodDescriptor md = (MethodDescriptor)obj;
097
098            return exact == md.exact &&
099            methodName.equals(md.methodName) &&
100            cls.equals(md.cls) &&
101            Arrays.equals(paramTypes, md.paramTypes);
102        }
103        /**
104         * Returns the string length of method name. I.e. if the
105         * hashcodes are different, the objects are different. If the
106         * hashcodes are the same, need to use the equals method to
107         * determine equality.
108         * @return the string length of method name.
109         */
110        @Override
111        public int hashCode() {
112            return hashCode;
113        }
114    }
115
116    /**
117     * Only log warning about accessibility work around once.
118     * <p>
119     * Note that this is broken when this class is deployed via a shared
120     * classloader in a container, as the warning message will be emitted
121     * only once, not once per webapp. However making the warning appear
122     * once per webapp means having a map keyed by context classloader
123     * which introduces nasty memory-leak problems. As this warning is
124     * really optional we can ignore this problem; only one of the webapps
125     * will get the warning in its logs but that should be good enough.
126     */
127    private static boolean loggedAccessibleWarning;
128
129    /**
130     * Indicates whether methods should be cached for improved performance.
131     * <p>
132     * Note that when this class is deployed via a shared classloader in
133     * a container, this will affect all webapps. However making this
134     * configurable per webapp would mean having a map keyed by context classloader
135     * which may introduce memory-leak problems.
136     * </p>
137     */
138    private static boolean CACHE_METHODS = true;
139    /** An empty class array */
140    private static final Class<?>[] EMPTY_CLASS_PARAMETERS = new Class[0];
141
142    /** An empty object array */
143    private static final Object[] EMPTY_OBJECT_ARRAY = {};
144
145    /**
146     * Stores a cache of MethodDescriptor to Method in a WeakHashMap.
147     * <p>
148     * The keys into this map only ever exist as temporary variables within
149     * methods of this class, and are never exposed to users of this class.
150     * This means that the WeakHashMap is used only as a mechanism for
151     * limiting the size of the cache, ie a way to tell the garbage collector
152     * that the contents of the cache can be completely garbage-collected
153     * whenever it needs the memory. Whether this is a good approach to
154     * this problem is doubtful; something like the commons-collections
155     * LRUMap may be more appropriate (though of course selecting an
156     * appropriate size is an issue).
157     * </p>
158     * <p>
159     * This static variable is safe even when this code is deployed via a
160     * shared classloader because it is keyed via a MethodDescriptor object
161     * which has a Class as one of its members and that member is used in
162     * the MethodDescriptor.equals method. So two components that load the same
163     * class via different classloaders will generate non-equal MethodDescriptor
164     * objects and hence end up with different entries in the map.
165     * </p>
166     */
167    private static final Map<MethodDescriptor, Reference<Method>> cache = Collections
168            .synchronizedMap(new WeakHashMap<MethodDescriptor, Reference<Method>>());
169
170    /**
171     * Add a method to the cache.
172     *
173     * @param md The method descriptor
174     * @param method The method to cache
175     */
176    private static void cacheMethod(final MethodDescriptor md, final Method method) {
177        if (CACHE_METHODS && method != null) {
178            cache.put(md, new WeakReference<>(method));
179        }
180    }
181
182    /**
183     * Clear the method cache.
184     * @return the number of cached methods cleared
185     * @since 1.8.0
186     */
187    public static synchronized int clearCache() {
188        final int size = cache.size();
189        cache.clear();
190        return size;
191    }
192
193    /**
194     * <p>Return an accessible method (that is, one that can be invoked via
195     * reflection) that implements the specified Method.  If no such method
196     * can be found, return <code>null</code>.</p>
197     *
198     * @param clazz The class of the object
199     * @param method The method that we wish to call
200     * @return The accessible method
201     * @since 1.8.0
202     */
203    public static Method getAccessibleMethod(Class<?> clazz, Method method) {
204
205        // Make sure we have a method to check
206        // If the requested method is not public we cannot call it
207        if (method == null || !Modifier.isPublic(method.getModifiers())) {
208            return null;
209        }
210
211        boolean sameClass = true;
212        if (clazz == null) {
213            clazz = method.getDeclaringClass();
214        } else {
215            sameClass = clazz.equals(method.getDeclaringClass());
216            if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
217                throw new IllegalArgumentException(clazz.getName() +
218                        " is not assignable from " + method.getDeclaringClass().getName());
219            }
220        }
221
222        // If the class is public, we are done
223        if (Modifier.isPublic(clazz.getModifiers())) {
224            if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
225                setMethodAccessible(method); // Default access superclass workaround
226            }
227            return method;
228        }
229
230        final String methodName      = method.getName();
231        final Class<?>[] parameterTypes = method.getParameterTypes();
232
233        // Check the implemented interfaces and subinterfaces
234        method =
235                getAccessibleMethodFromInterfaceNest(clazz,
236                        methodName,
237                        parameterTypes);
238
239        // Check the superclass chain
240        if (method == null) {
241            method = getAccessibleMethodFromSuperclass(clazz,
242                        methodName,
243                        parameterTypes);
244        }
245
246        return method;
247    }
248
249    /**
250     * <p>Return an accessible method (that is, one that can be invoked via
251     * reflection) with given name and a single parameter.  If no such method
252     * can be found, return <code>null</code>.
253     * Basically, a convenience wrapper that constructs a <code>Class</code>
254     * array for you.</p>
255     *
256     * @param clazz get method from this class
257     * @param methodName get method with this name
258     * @param parameterType taking this type of parameter
259     * @return The accessible method
260     */
261    public static Method getAccessibleMethod(
262            final Class<?> clazz,
263            final String methodName,
264            final Class<?> parameterType) {
265
266        final Class<?>[] parameterTypes = {parameterType};
267        return getAccessibleMethod(clazz, methodName, parameterTypes);
268    }
269
270    /**
271     * <p>Return an accessible method (that is, one that can be invoked via
272     * reflection) with given name and parameters.  If no such method
273     * can be found, return <code>null</code>.
274     * This is just a convenient wrapper for
275     * {@link #getAccessibleMethod(Method method)}.</p>
276     *
277     * @param clazz get method from this class
278     * @param methodName get method with this name
279     * @param parameterTypes with these parameters types
280     * @return The accessible method
281     */
282    public static Method getAccessibleMethod(
283            final Class<?> clazz,
284            final String methodName,
285            final Class<?>[] parameterTypes) {
286
287        try {
288            final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
289            // Check the cache first
290            Method method = getCachedMethod(md);
291            if (method != null) {
292                return method;
293            }
294
295            method =  getAccessibleMethod
296                    (clazz, clazz.getMethod(methodName, parameterTypes));
297            cacheMethod(md, method);
298            return method;
299        } catch (final NoSuchMethodException e) {
300            return null;
301        }
302    }
303
304    /**
305     * <p>Return an accessible method (that is, one that can be invoked via
306     * reflection) that implements the specified Method.  If no such method
307     * can be found, return <code>null</code>.</p>
308     *
309     * @param method The method that we wish to call
310     * @return The accessible method
311     */
312    public static Method getAccessibleMethod(final Method method) {
313
314        // Make sure we have a method to check
315        if (method == null) {
316            return null;
317        }
318
319        return getAccessibleMethod(method.getDeclaringClass(), method);
320    }
321
322    /**
323     * <p>Return an accessible method (that is, one that can be invoked via
324     * reflection) that implements the specified method, by scanning through
325     * all implemented interfaces and subinterfaces.  If no such method
326     * can be found, return <code>null</code>.</p>
327     *
328     * <p> There isn't any good reason why this method must be private.
329     * It is because there doesn't seem any reason why other classes should
330     * call this rather than the higher level methods.</p>
331     *
332     * @param clazz Parent class for the interfaces to be checked
333     * @param methodName Method name of the method we wish to call
334     * @param parameterTypes The parameter type signatures
335     */
336    private static Method getAccessibleMethodFromInterfaceNest
337            (Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
338
339        Method method = null;
340
341        // Search up the superclass chain
342        for (; clazz != null; clazz = clazz.getSuperclass()) {
343
344            // Check the implemented interfaces of the parent class
345            final Class<?>[] interfaces = clazz.getInterfaces();
346            for (final Class<?> element : interfaces) {
347
348                // Is this interface public?
349                if (!Modifier.isPublic(element.getModifiers())) {
350                    continue;
351                }
352
353                // Does the method exist on this interface?
354                try {
355                    method = element.getDeclaredMethod(methodName,
356                            parameterTypes);
357                } catch (final NoSuchMethodException e) {
358                    /* Swallow, if no method is found after the loop then this
359                     * method returns null.
360                     */
361                }
362                if (method != null) {
363                    return method;
364                }
365
366                // Recursively check our parent interfaces
367                method =
368                        getAccessibleMethodFromInterfaceNest(element,
369                                methodName,
370                                parameterTypes);
371                if (method != null) {
372                    return method;
373                }
374
375            }
376
377        }
378
379        // We did not find anything
380        return null;
381    }
382
383    /**
384     * <p>Return an accessible method (that is, one that can be invoked via
385     * reflection) by scanning through the superclasses. If no such method
386     * can be found, return <code>null</code>.</p>
387     *
388     * @param clazz Class to be checked
389     * @param methodName Method name of the method we wish to call
390     * @param parameterTypes The parameter type signatures
391     */
392    private static Method getAccessibleMethodFromSuperclass
393            (final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
394
395        Class<?> parentClazz = clazz.getSuperclass();
396        while (parentClazz != null) {
397            if (Modifier.isPublic(parentClazz.getModifiers())) {
398                try {
399                    return parentClazz.getMethod(methodName, parameterTypes);
400                } catch (final NoSuchMethodException e) {
401                    return null;
402                }
403            }
404            parentClazz = parentClazz.getSuperclass();
405        }
406        return null;
407    }
408
409    /**
410     * Return the method from the cache, if present.
411     *
412     * @param md The method descriptor
413     * @return The cached method
414     */
415    private static Method getCachedMethod(final MethodDescriptor md) {
416        if (CACHE_METHODS) {
417            final Reference<Method> methodRef = cache.get(md);
418            if (methodRef != null) {
419                return methodRef.get();
420            }
421        }
422        return null;
423    }
424
425    /**
426     * <p>Find an accessible method that matches the given name and has compatible parameters.
427     * Compatible parameters mean that every method parameter is assignable from
428     * the given parameters.
429     * In other words, it finds a method with the given name
430     * that will take the parameters given.
431     * </p>
432     * <p>This method is slightly undeterministic since it loops
433     * through methods names and return the first matching method.</p>
434     * <p>This method is used by
435     * {@link
436     * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
437     * </p>
438     * <p>This method can match primitive parameter by passing in wrapper classes.
439     * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
440     * parameter.
441     * </p>
442     *
443     * @param clazz find method in this class
444     * @param methodName find method with this name
445     * @param parameterTypes find method with compatible parameters
446     * @return The accessible method
447     */
448    public static Method getMatchingAccessibleMethod(
449                                                final Class<?> clazz,
450                                                final String methodName,
451                                                final Class<?>[] parameterTypes) {
452        // trace logging
453        final Log log = LogFactory.getLog(MethodUtils.class);
454        if (log.isTraceEnabled()) {
455            log.trace("Matching name=" + methodName + " on " + clazz);
456        }
457        final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);
458
459        // see if we can find the method directly
460        // most of the time this works and it's much faster
461        try {
462            // Check the cache first
463            Method method = getCachedMethod(md);
464            if (method != null) {
465                return method;
466            }
467
468            method = clazz.getMethod(methodName, parameterTypes);
469            if (log.isTraceEnabled()) {
470                log.trace("Found straight match: " + method);
471                log.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
472            }
473
474            setMethodAccessible(method); // Default access superclass workaround
475
476            cacheMethod(md, method);
477            return method;
478
479        } catch (final NoSuchMethodException e) { /* SWALLOW */ }
480
481        // search through all methods
482        final int paramSize = parameterTypes.length;
483        Method bestMatch = null;
484        final Method[] methods = clazz.getMethods();
485        float bestMatchCost = Float.MAX_VALUE;
486        float myCost = Float.MAX_VALUE;
487        for (final Method method2 : methods) {
488            if (method2.getName().equals(methodName)) {
489                // log some trace information
490                if (log.isTraceEnabled()) {
491                    log.trace("Found matching name:");
492                    log.trace(method2);
493                }
494
495                // compare parameters
496                final Class<?>[] methodsParams = method2.getParameterTypes();
497                final int methodParamSize = methodsParams.length;
498                if (methodParamSize == paramSize) {
499                    boolean match = true;
500                    for (int n = 0 ; n < methodParamSize; n++) {
501                        if (log.isTraceEnabled()) {
502                            log.trace("Param=" + parameterTypes[n].getName());
503                            log.trace("Method=" + methodsParams[n].getName());
504                        }
505                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
506                            if (log.isTraceEnabled()) {
507                                log.trace(methodsParams[n] + " is not assignable from "
508                                            + parameterTypes[n]);
509                            }
510                            match = false;
511                            break;
512                        }
513                    }
514
515                    if (match) {
516                        // get accessible version of method
517                        final Method method = getAccessibleMethod(clazz, method2);
518                        if (method != null) {
519                            if (log.isTraceEnabled()) {
520                                log.trace(method + " accessible version of "
521                                            + method2);
522                            }
523                            setMethodAccessible(method); // Default access superclass workaround
524                            myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes());
525                            if (myCost < bestMatchCost) {
526                                bestMatch = method;
527                                bestMatchCost = myCost;
528                            }
529                        }
530
531                        log.trace("Couldn't find accessible method.");
532                    }
533                }
534            }
535        }
536        if (bestMatch != null) {
537            cacheMethod(md, bestMatch);
538        } else {
539            // didn't find a match
540            log.trace("No match found.");
541        }
542
543        return bestMatch;
544    }
545
546    /**
547     * Gets the number of steps required needed to turn the source class into the
548     * destination class. This represents the number of steps in the object hierarchy
549     * graph.
550     * @param srcClass The source class
551     * @param destClass The destination class
552     * @return The cost of transforming an object
553     */
554    private static float getObjectTransformationCost(Class<?> srcClass, final Class<?> destClass) {
555        float cost = 0.0f;
556        while (srcClass != null && !destClass.equals(srcClass)) {
557            if (destClass.isPrimitive()) {
558                final Class<?> destClassWrapperClazz = getPrimitiveWrapper(destClass);
559                if (destClassWrapperClazz != null && destClassWrapperClazz.equals(srcClass)) {
560                    cost += 0.25f;
561                    break;
562                }
563            }
564            if (destClass.isInterface() && isAssignmentCompatible(destClass,srcClass)) {
565                // slight penalty for interface match.
566                // we still want an exact match to override an interface match, but
567                // an interface match should override anything where we have to get a
568                // superclass.
569                cost += 0.25f;
570                break;
571            }
572            cost++;
573            srcClass = srcClass.getSuperclass();
574        }
575
576        /*
577         * If the destination class is null, we've travelled all the way up to
578         * an Object match. We'll penalize this by adding 1.5 to the cost.
579         */
580        if (srcClass == null) {
581            cost += 1.5f;
582        }
583
584        return cost;
585    }
586
587    /**
588     * Gets the class for the primitive type corresponding to the primitive wrapper class given.
589     * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>.
590     * @param wrapperType the
591     * @return the primitive type class corresponding to the given wrapper class,
592     * null if no match is found
593     */
594    public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
595        // does anyone know a better strategy?
596        if (Boolean.class.equals(wrapperType)) {
597            return boolean.class;
598        }
599        if (Float.class.equals(wrapperType)) {
600            return float.class;
601        }
602        if (Long.class.equals(wrapperType)) {
603            return long.class;
604        }
605        if (Integer.class.equals(wrapperType)) {
606            return int.class;
607        }
608        if (Short.class.equals(wrapperType)) {
609            return short.class;
610        }
611        if (Byte.class.equals(wrapperType)) {
612            return byte.class;
613        }
614        if (Double.class.equals(wrapperType)) {
615            return double.class;
616        }
617        if (Character.class.equals(wrapperType)) {
618            return char.class;
619        }
620        final Log log = LogFactory.getLog(MethodUtils.class);
621        if (log.isDebugEnabled()) {
622            log.debug("Not a known primitive wrapper class: " + wrapperType);
623        }
624        return null;
625    }
626
627    /**
628     * Gets the wrapper object class for the given primitive type class.
629     * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code>
630     * @param primitiveType the primitive type class for which a match is to be found
631     * @return the wrapper type associated with the given primitive
632     * or null if no match is found
633     */
634    public static Class<?> getPrimitiveWrapper(final Class<?> primitiveType) {
635        // does anyone know a better strategy than comparing names?
636        if (boolean.class.equals(primitiveType)) {
637            return Boolean.class;
638        }
639        if (float.class.equals(primitiveType)) {
640            return Float.class;
641        }
642        if (long.class.equals(primitiveType)) {
643            return Long.class;
644        }
645        if (int.class.equals(primitiveType)) {
646            return Integer.class;
647        }
648        if (short.class.equals(primitiveType)) {
649            return Short.class;
650        }
651        if (byte.class.equals(primitiveType)) {
652            return Byte.class;
653        }
654        if (double.class.equals(primitiveType)) {
655            return Double.class;
656        }
657        if (char.class.equals(primitiveType)) {
658            return Character.class;
659        }
660        return null;
661    }
662
663    /**
664     * Returns the sum of the object transformation cost for each class in the source
665     * argument list.
666     * @param srcArgs The source arguments
667     * @param destArgs The destination arguments
668     * @return The total transformation cost
669     */
670    private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Class<?>[] destArgs) {
671
672        float totalCost = 0.0f;
673        for (int i = 0; i < srcArgs.length; i++) {
674            Class<?> srcClass, destClass;
675            srcClass = srcArgs[i];
676            destClass = destArgs[i];
677            totalCost += getObjectTransformationCost(srcClass, destClass);
678        }
679
680        return totalCost;
681    }
682
683    /**
684     * <p>Invoke a method whose parameter type matches exactly the object
685     * type.</p>
686     *
687     * <p> This is a convenient wrapper for
688     * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
689     * </p>
690     *
691     * @param object invoke method on this object
692     * @param methodName get method with this name
693     * @param arg use this argument. May be null (this will result in calling the
694     *  parameterless method with name {@code methodName}).
695     * @return The value returned by the invoked method
696     * @throws NoSuchMethodException if there is no such accessible method
697     * @throws InvocationTargetException wraps an exception thrown by the
698     *  method invoked
699     * @throws IllegalAccessException if the requested method is not accessible
700     *  via reflection
701     */
702    public static Object invokeExactMethod(
703            final Object object,
704            final String methodName,
705            final Object arg)
706            throws
707            NoSuchMethodException,
708            IllegalAccessException,
709            InvocationTargetException {
710
711        final Object[] args = toArray(arg);
712        return invokeExactMethod(object, methodName, args);
713    }
714
715    /**
716     * <p>Invoke a method whose parameter types match exactly the object
717     * types.</p>
718     *
719     * <p> This uses reflection to invoke the method obtained from a call to
720     * <code>getAccessibleMethod()</code>.</p>
721     *
722     * @param object invoke method on this object
723     * @param methodName get method with this name
724     * @param args use these arguments - treat null as empty array (passing null will
725     *  result in calling the parameterless method with name {@code methodName}).
726     * @return The value returned by the invoked method
727     * @throws NoSuchMethodException if there is no such accessible method
728     * @throws InvocationTargetException wraps an exception thrown by the
729     *  method invoked
730     * @throws IllegalAccessException if the requested method is not accessible
731     *  via reflection
732     */
733    public static Object invokeExactMethod(
734            final Object object,
735            final String methodName,
736            Object[] args)
737            throws
738            NoSuchMethodException,
739            IllegalAccessException,
740            InvocationTargetException {
741
742        if (args == null) {
743            args = EMPTY_OBJECT_ARRAY;
744        }
745        final int arguments = args.length;
746        final Class<?>[] parameterTypes = new Class[arguments];
747        for (int i = 0; i < arguments; i++) {
748            parameterTypes[i] = args[i].getClass();
749        }
750        return invokeExactMethod(object, methodName, args, parameterTypes);
751    }
752
753    /**
754     * <p>Invoke a method whose parameter types match exactly the parameter
755     * types given.</p>
756     *
757     * <p>This uses reflection to invoke the method obtained from a call to
758     * <code>getAccessibleMethod()</code>.</p>
759     *
760     * @param object invoke method on this object
761     * @param methodName get method with this name
762     * @param args use these arguments - treat null as empty array (passing null will
763     *  result in calling the parameterless method with name {@code methodName}).
764     * @param parameterTypes match these parameters - treat null as empty array
765     * @return The value returned by the invoked method
766     * @throws NoSuchMethodException if there is no such accessible method
767     * @throws InvocationTargetException wraps an exception thrown by the
768     *  method invoked
769     * @throws IllegalAccessException if the requested method is not accessible
770     *  via reflection
771     */
772    public static Object invokeExactMethod(
773            final Object object,
774            final String methodName,
775            Object[] args,
776            Class<?>[] parameterTypes)
777            throws
778            NoSuchMethodException,
779            IllegalAccessException,
780            InvocationTargetException {
781
782        if (args == null) {
783            args = EMPTY_OBJECT_ARRAY;
784        }
785
786        if (parameterTypes == null) {
787            parameterTypes = EMPTY_CLASS_PARAMETERS;
788        }
789
790        final Method method = getAccessibleMethod(
791                object.getClass(),
792                methodName,
793                parameterTypes);
794        if (method == null) {
795            throw new NoSuchMethodException("No such accessible method: " +
796                    methodName + "() on object: " + object.getClass().getName());
797        }
798        return method.invoke(object, args);
799    }
800
801    /**
802     * <p>Invoke a static method whose parameter type matches exactly the object
803     * type.</p>
804     *
805     * <p> This is a convenient wrapper for
806     * {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.
807     * </p>
808     *
809     * @param objectClass invoke static method on this class
810     * @param methodName get method with this name
811     * @param arg use this argument. May be null (this will result in calling the
812     *  parameterless method with name {@code methodName}).
813     * @return The value returned by the invoked method
814     * @throws NoSuchMethodException if there is no such accessible method
815     * @throws InvocationTargetException wraps an exception thrown by the
816     *  method invoked
817     * @throws IllegalAccessException if the requested method is not accessible
818     *  via reflection
819     * @since 1.8.0
820     */
821    public static Object invokeExactStaticMethod(
822            final Class<?> objectClass,
823            final String methodName,
824            final Object arg)
825            throws
826            NoSuchMethodException,
827            IllegalAccessException,
828            InvocationTargetException {
829
830        final Object[] args = toArray(arg);
831        return invokeExactStaticMethod (objectClass, methodName, args);
832    }
833
834    /**
835     * <p>Invoke a static method whose parameter types match exactly the object
836     * types.</p>
837     *
838     * <p> This uses reflection to invoke the method obtained from a call to
839     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
840     *
841     * @param objectClass invoke static method on this class
842     * @param methodName get method with this name
843     * @param args use these arguments - treat null as empty array (passing null will
844     *  result in calling the parameterless method with name {@code methodName}).
845     * @return The value returned by the invoked method
846     * @throws NoSuchMethodException if there is no such accessible method
847     * @throws InvocationTargetException wraps an exception thrown by the
848     *  method invoked
849     * @throws IllegalAccessException if the requested method is not accessible
850     *  via reflection
851     * @since 1.8.0
852     */
853    public static Object invokeExactStaticMethod(
854            final Class<?> objectClass,
855            final String methodName,
856            Object[] args)
857            throws
858            NoSuchMethodException,
859            IllegalAccessException,
860            InvocationTargetException {
861
862        if (args == null) {
863            args = EMPTY_OBJECT_ARRAY;
864        }
865        final int arguments = args.length;
866        final Class<?>[] parameterTypes = new Class[arguments];
867        for (int i = 0; i < arguments; i++) {
868            parameterTypes[i] = args[i].getClass();
869        }
870        return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
871    }
872
873    /**
874     * <p>Invoke a static method whose parameter types match exactly the parameter
875     * types given.</p>
876     *
877     * <p>This uses reflection to invoke the method obtained from a call to
878     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
879     *
880     * @param objectClass invoke static method on this class
881     * @param methodName get method with this name
882     * @param args use these arguments - treat null as empty array (passing null will
883     *  result in calling the parameterless method with name {@code methodName}).
884     * @param parameterTypes match these parameters - treat null as empty array
885     * @return The value returned by the invoked method
886     * @throws NoSuchMethodException if there is no such accessible method
887     * @throws InvocationTargetException wraps an exception thrown by the
888     *  method invoked
889     * @throws IllegalAccessException if the requested method is not accessible
890     *  via reflection
891     * @since 1.8.0
892     */
893    public static Object invokeExactStaticMethod(
894            final Class<?> objectClass,
895            final String methodName,
896            Object[] args,
897            Class<?>[] parameterTypes)
898            throws
899            NoSuchMethodException,
900            IllegalAccessException,
901            InvocationTargetException {
902
903        if (args == null) {
904            args = EMPTY_OBJECT_ARRAY;
905        }
906
907        if (parameterTypes == null) {
908            parameterTypes = EMPTY_CLASS_PARAMETERS;
909        }
910
911        final Method method = getAccessibleMethod(
912                objectClass,
913                methodName,
914                parameterTypes);
915        if (method == null) {
916            throw new NoSuchMethodException("No such accessible method: " +
917                    methodName + "() on class: " + objectClass.getName());
918        }
919        return method.invoke(null, args);
920    }
921
922    /**
923     * <p>Invoke a named method whose parameter type matches the object type.</p>
924     *
925     * <p>The behavior of this method is less deterministic
926     * than <code>invokeExactMethod()</code>.
927     * It loops through all methods with names that match
928     * and then executes the first it finds with compatible parameters.</p>
929     *
930     * <p>This method supports calls to methods taking primitive parameters
931     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
932     * would match a <code>boolean</code> primitive.</p>
933     *
934     * <p> This is a convenient wrapper for
935     * {@link #invokeMethod(Object object,String methodName,Object [] args)}.
936     * </p>
937     *
938     * @param object invoke method on this object
939     * @param methodName get method with this name
940     * @param arg use this argument. May be null (this will result in calling the
941     *  parameterless method with name {@code methodName}).
942     * @return The value returned by the invoked method
943     * @throws NoSuchMethodException if there is no such accessible method
944     * @throws InvocationTargetException wraps an exception thrown by the
945     *  method invoked
946     * @throws IllegalAccessException if the requested method is not accessible
947     *  via reflection
948     */
949    public static Object invokeMethod(
950            final Object object,
951            final String methodName,
952            final Object arg)
953            throws
954            NoSuchMethodException,
955            IllegalAccessException,
956            InvocationTargetException {
957
958        final Object[] args = toArray(arg);
959        return invokeMethod(object, methodName, args);
960    }
961
962    /**
963     * <p>Invoke a named method whose parameter type matches the object type.</p>
964     *
965     * <p>The behavior of this method is less deterministic
966     * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
967     * It loops through all methods with names that match
968     * and then executes the first it finds with compatible parameters.</p>
969     *
970     * <p>This method supports calls to methods taking primitive parameters
971     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
972     * would match a <code>boolean</code> primitive.</p>
973     *
974     * <p> This is a convenient wrapper for
975     * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
976     * </p>
977     *
978     * @param object invoke method on this object
979     * @param methodName get method with this name
980     * @param args use these arguments - treat null as empty array (passing null will
981     *  result in calling the parameterless method with name {@code methodName}).
982     * @return The value returned by the invoked method
983     * @throws NoSuchMethodException if there is no such accessible method
984     * @throws InvocationTargetException wraps an exception thrown by the
985     *  method invoked
986     * @throws IllegalAccessException if the requested method is not accessible
987     *  via reflection
988     */
989    public static Object invokeMethod(
990            final Object object,
991            final String methodName,
992            Object[] args)
993            throws
994            NoSuchMethodException,
995            IllegalAccessException,
996            InvocationTargetException {
997
998        if (args == null) {
999            args = EMPTY_OBJECT_ARRAY;
1000        }
1001        final int arguments = args.length;
1002        final Class<?>[] parameterTypes = new Class[arguments];
1003        for (int i = 0; i < arguments; i++) {
1004            parameterTypes[i] = args[i].getClass();
1005        }
1006        return invokeMethod(object, methodName, args, parameterTypes);
1007    }
1008
1009    /**
1010     * <p>Invoke a named method whose parameter type matches the object type.</p>
1011     *
1012     * <p>The behavior of this method is less deterministic
1013     * than {@link
1014     * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
1015     * It loops through all methods with names that match
1016     * and then executes the first it finds with compatible parameters.</p>
1017     *
1018     * <p>This method supports calls to methods taking primitive parameters
1019     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
1020     * would match a <code>boolean</code> primitive.</p>
1021     *
1022     *
1023     * @param object invoke method on this object
1024     * @param methodName get method with this name
1025     * @param args use these arguments - treat null as empty array (passing null will
1026     *  result in calling the parameterless method with name {@code methodName}).
1027     * @param parameterTypes match these parameters - treat null as empty array
1028     * @return The value returned by the invoked method
1029     * @throws NoSuchMethodException if there is no such accessible method
1030     * @throws InvocationTargetException wraps an exception thrown by the
1031     *  method invoked
1032     * @throws IllegalAccessException if the requested method is not accessible
1033     *  via reflection
1034     */
1035    public static Object invokeMethod(
1036            final Object object,
1037            final String methodName,
1038            Object[] args,
1039            Class<?>[] parameterTypes)
1040                throws
1041                    NoSuchMethodException,
1042                    IllegalAccessException,
1043                    InvocationTargetException {
1044
1045        if (parameterTypes == null) {
1046            parameterTypes = EMPTY_CLASS_PARAMETERS;
1047        }
1048        if (args == null) {
1049            args = EMPTY_OBJECT_ARRAY;
1050        }
1051
1052        final Method method = getMatchingAccessibleMethod(
1053                object.getClass(),
1054                methodName,
1055                parameterTypes);
1056        if (method == null) {
1057            throw new NoSuchMethodException("No such accessible method: " +
1058                    methodName + "() on object: " + object.getClass().getName());
1059        }
1060        return method.invoke(object, args);
1061    }
1062
1063    /**
1064     * <p>Invoke a named static method whose parameter type matches the object type.</p>
1065     *
1066     * <p>The behavior of this method is less deterministic
1067     * than {@link #invokeExactMethod(Object, String, Object[], Class[])}.
1068     * It loops through all methods with names that match
1069     * and then executes the first it finds with compatible parameters.</p>
1070     *
1071     * <p>This method supports calls to methods taking primitive parameters
1072     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
1073     * would match a <code>boolean</code> primitive.</p>
1074     *
1075     * <p> This is a convenient wrapper for
1076     * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.
1077     * </p>
1078     *
1079     * @param objectClass invoke static method on this class
1080     * @param methodName get method with this name
1081     * @param arg use this argument. May be null (this will result in calling the
1082     *  parameterless method with name {@code methodName}).
1083     * @return The value returned by the invoked method
1084     * @throws NoSuchMethodException if there is no such accessible method
1085     * @throws InvocationTargetException wraps an exception thrown by the
1086     *  method invoked
1087     * @throws IllegalAccessException if the requested method is not accessible
1088     *  via reflection
1089     * @since 1.8.0
1090     */
1091    public static Object invokeStaticMethod(
1092            final Class<?> objectClass,
1093            final String methodName,
1094            final Object arg)
1095            throws
1096            NoSuchMethodException,
1097            IllegalAccessException,
1098            InvocationTargetException {
1099
1100        final Object[] args = toArray(arg);
1101        return invokeStaticMethod (objectClass, methodName, args);
1102    }
1103
1104    /**
1105     * <p>Invoke a named static method whose parameter type matches the object type.</p>
1106     *
1107     * <p>The behavior of this method is less deterministic
1108     * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
1109     * It loops through all methods with names that match
1110     * and then executes the first it finds with compatible parameters.</p>
1111     *
1112     * <p>This method supports calls to methods taking primitive parameters
1113     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
1114     * would match a <code>boolean</code> primitive.</p>
1115     *
1116     * <p> This is a convenient wrapper for
1117     * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.
1118     * </p>
1119     *
1120     * @param objectClass invoke static method on this class
1121     * @param methodName get method with this name
1122     * @param args use these arguments - treat null as empty array (passing null will
1123     *  result in calling the parameterless method with name {@code methodName}).
1124     * @return The value returned by the invoked method
1125     * @throws NoSuchMethodException if there is no such accessible method
1126     * @throws InvocationTargetException wraps an exception thrown by the
1127     *  method invoked
1128     * @throws IllegalAccessException if the requested method is not accessible
1129     *  via reflection
1130     * @since 1.8.0
1131     */
1132    public static Object invokeStaticMethod(
1133            final Class<?> objectClass,
1134            final String methodName,
1135            Object[] args)
1136            throws
1137            NoSuchMethodException,
1138            IllegalAccessException,
1139            InvocationTargetException {
1140
1141        if (args == null) {
1142            args = EMPTY_OBJECT_ARRAY;
1143        }
1144        final int arguments = args.length;
1145        final Class<?>[] parameterTypes = new Class[arguments];
1146        for (int i = 0; i < arguments; i++) {
1147            parameterTypes[i] = args[i].getClass();
1148        }
1149        return invokeStaticMethod (objectClass, methodName, args, parameterTypes);
1150    }
1151
1152    /**
1153     * <p>Invoke a named static method whose parameter type matches the object type.</p>
1154     *
1155     * <p>The behavior of this method is less deterministic
1156     * than {@link
1157     * #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.
1158     * It loops through all methods with names that match
1159     * and then executes the first it finds with compatible parameters.</p>
1160     *
1161     * <p>This method supports calls to methods taking primitive parameters
1162     * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
1163     * would match a <code>boolean</code> primitive.</p>
1164     *
1165     *
1166     * @param objectClass invoke static method on this class
1167     * @param methodName get method with this name
1168     * @param args use these arguments - treat null as empty array (passing null will
1169     *  result in calling the parameterless method with name {@code methodName}).
1170     * @param parameterTypes match these parameters - treat null as empty array
1171     * @return The value returned by the invoked method
1172     * @throws NoSuchMethodException if there is no such accessible method
1173     * @throws InvocationTargetException wraps an exception thrown by the
1174     *  method invoked
1175     * @throws IllegalAccessException if the requested method is not accessible
1176     *  via reflection
1177     * @since 1.8.0
1178     */
1179    public static Object invokeStaticMethod(
1180            final Class<?> objectClass,
1181            final String methodName,
1182            Object[] args,
1183            Class<?>[] parameterTypes)
1184                throws
1185                    NoSuchMethodException,
1186                    IllegalAccessException,
1187                    InvocationTargetException {
1188
1189        if (parameterTypes == null) {
1190            parameterTypes = EMPTY_CLASS_PARAMETERS;
1191        }
1192        if (args == null) {
1193            args = EMPTY_OBJECT_ARRAY;
1194        }
1195
1196        final Method method = getMatchingAccessibleMethod(
1197                objectClass,
1198                methodName,
1199                parameterTypes);
1200        if (method == null) {
1201            throw new NoSuchMethodException("No such accessible method: " +
1202                    methodName + "() on class: " + objectClass.getName());
1203        }
1204        return method.invoke(null, args);
1205    }
1206
1207    /**
1208     * <p>Determine whether a type can be used as a parameter in a method invocation.
1209     * This method handles primitive conversions correctly.</p>
1210     *
1211     * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
1212     * a <code>Long</code> to a <code>long</code>,
1213     * a <code>Float</code> to a <code>float</code>,
1214     * a <code>Integer</code> to a <code>int</code>,
1215     * and a <code>Double</code> to a <code>double</code>.
1216     * Now logic widening matches are allowed.
1217     * For example, a <code>Long</code> will not match a <code>int</code>.
1218     *
1219     * @param parameterType the type of parameter accepted by the method
1220     * @param parameterization the type of parameter being tested
1221     * @return true if the assignment is compatible.
1222     */
1223    public static final boolean isAssignmentCompatible(final Class<?> parameterType, final Class<?> parameterization) {
1224        // try plain assignment
1225        if (parameterType.isAssignableFrom(parameterization)) {
1226            return true;
1227        }
1228
1229        if (parameterType.isPrimitive()) {
1230            // this method does *not* do widening - you must specify exactly
1231            // is this the right behavior?
1232            final Class<?> parameterWrapperClazz = getPrimitiveWrapper(parameterType);
1233            if (parameterWrapperClazz != null) {
1234                return parameterWrapperClazz.equals(parameterization);
1235            }
1236        }
1237
1238        return false;
1239    }
1240
1241    /**
1242     * Set whether methods should be cached for greater performance or not,
1243     * default is <code>true</code>.
1244     *
1245     * @param cacheMethods <code>true</code> if methods should be
1246     * cached for greater performance, otherwise <code>false</code>
1247     * @since 1.8.0
1248     */
1249    public static synchronized void setCacheMethods(final boolean cacheMethods) {
1250        CACHE_METHODS = cacheMethods;
1251        if (!CACHE_METHODS) {
1252            clearCache();
1253        }
1254    }
1255
1256    /**
1257     * Try to make the method accessible
1258     * @param method The source arguments
1259     */
1260    private static void setMethodAccessible(final Method method) {
1261        try {
1262            //
1263            // XXX Default access superclass workaround
1264            //
1265            // When a public class has a default access superclass
1266            // with public methods, these methods are accessible.
1267            // Calling them from compiled code works fine.
1268            //
1269            // Unfortunately, using reflection to invoke these methods
1270            // seems to (wrongly) to prevent access even when the method
1271            // modifer is public.
1272            //
1273            // The following workaround solves the problem but will only
1274            // work from sufficiently privileges code.
1275            //
1276            // Better workarounds would be greatfully accepted.
1277            //
1278            if (!method.isAccessible()) {
1279                method.setAccessible(true);
1280            }
1281
1282        } catch (final SecurityException se) {
1283            // log but continue just in case the method.invoke works anyway
1284            final Log log = LogFactory.getLog(MethodUtils.class);
1285            if (!loggedAccessibleWarning) {
1286                boolean vulnerableJVM = false;
1287                try {
1288                    final String specVersion = System.getProperty("java.specification.version");
1289                    if (specVersion.charAt(0) == '1' &&
1290                            (specVersion.charAt(2) == '0' ||
1291                             specVersion.charAt(2) == '1' ||
1292                             specVersion.charAt(2) == '2' ||
1293                             specVersion.charAt(2) == '3')) {
1294
1295                        vulnerableJVM = true;
1296                    }
1297                } catch (final SecurityException e) {
1298                    // don't know - so display warning
1299                    vulnerableJVM = true;
1300                }
1301                if (vulnerableJVM) {
1302                    log.warn(
1303                        "Current Security Manager restricts use of workarounds for reflection bugs "
1304                        + " in pre-1.4 JVMs.");
1305                }
1306                loggedAccessibleWarning = true;
1307            }
1308            log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);
1309        }
1310    }
1311
1312    private static Object[] toArray(final Object arg) {
1313        Object[] args = null;
1314        if (arg != null) {
1315            args = new Object[] { arg };
1316        }
1317        return args;
1318    }
1319
1320    /**
1321     * Find a non primitive representation for given primitive class.
1322     *
1323     * @param clazz the class to find a representation for, not null
1324     * @return the original class if it not a primitive. Otherwise the wrapper class. Not null
1325     */
1326    public static Class<?> toNonPrimitiveClass(final Class<?> clazz) {
1327        if (!clazz.isPrimitive()) {
1328            return clazz;
1329        }
1330        final Class<?> primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
1331        // the above method returns
1332        if (primitiveClazz != null) {
1333            return primitiveClazz;
1334        }
1335        return clazz;
1336    }
1337
1338    /**
1339     * Deprecated, all methods are static.
1340     *
1341     * @deprecated Will be private in 2.0.
1342     */
1343    @Deprecated
1344    public MethodUtils() {
1345        // empty
1346    }
1347}