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