001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util;
018
019import java.beans.Introspector;
020import java.lang.reflect.Array;
021import java.lang.reflect.Constructor;
022import java.lang.reflect.Method;
023import java.lang.reflect.Modifier;
024import java.lang.reflect.Proxy;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.IdentityHashMap;
031import java.util.Iterator;
032import java.util.LinkedHashSet;
033import java.util.Map;
034import java.util.Set;
035
036/**
037 * Miscellaneous {@code java.lang.Class} utility methods.
038 * Mainly for internal use within the framework.
039 *
040 * @author Juergen Hoeller
041 * @author Keith Donald
042 * @author Rob Harrop
043 * @author Sam Brannen
044 * @since 1.1
045 * @see TypeUtils
046 * @see ReflectionUtils
047 */
048public abstract class ClassUtils {
049
050        /** Suffix for array class names: "[]" */
051        public static final String ARRAY_SUFFIX = "[]";
052
053        /** Prefix for internal array class names: "[" */
054        private static final String INTERNAL_ARRAY_PREFIX = "[";
055
056        /** Prefix for internal non-primitive array class names: "[L" */
057        private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L";
058
059        /** The package separator character: '.' */
060        private static final char PACKAGE_SEPARATOR = '.';
061
062        /** The path separator character: '/' */
063        private static final char PATH_SEPARATOR = '/';
064
065        /** The inner class separator character: '$' */
066        private static final char INNER_CLASS_SEPARATOR = '$';
067
068        /** The CGLIB class separator: "$$" */
069        public static final String CGLIB_CLASS_SEPARATOR = "$$";
070
071        /** The ".class" file suffix */
072        public static final String CLASS_FILE_SUFFIX = ".class";
073
074
075        /**
076         * Map with primitive wrapper type as key and corresponding primitive
077         * type as value, for example: Integer.class -> int.class.
078         */
079        private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<Class<?>, Class<?>>(8);
080
081        /**
082         * Map with primitive type as key and corresponding wrapper
083         * type as value, for example: int.class -> Integer.class.
084         */
085        private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<Class<?>, Class<?>>(8);
086
087        /**
088         * Map with primitive type name as key and corresponding primitive
089         * type as value, for example: "int" -> "int.class".
090         */
091        private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(32);
092
093        /**
094         * Map with common "java.lang" class name as key and corresponding Class as value.
095         * Primarily for efficient deserialization of remote invocations.
096         */
097        private static final Map<String, Class<?>> commonClassCache = new HashMap<String, Class<?>>(32);
098
099
100        static {
101                primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
102                primitiveWrapperTypeMap.put(Byte.class, byte.class);
103                primitiveWrapperTypeMap.put(Character.class, char.class);
104                primitiveWrapperTypeMap.put(Double.class, double.class);
105                primitiveWrapperTypeMap.put(Float.class, float.class);
106                primitiveWrapperTypeMap.put(Integer.class, int.class);
107                primitiveWrapperTypeMap.put(Long.class, long.class);
108                primitiveWrapperTypeMap.put(Short.class, short.class);
109
110                for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) {
111                        primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
112                        registerCommonClasses(entry.getKey());
113                }
114
115                Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(64);
116                primitiveTypes.addAll(primitiveWrapperTypeMap.values());
117                primitiveTypes.addAll(Arrays.asList(new Class<?>[] {
118                                boolean[].class, byte[].class, char[].class, double[].class,
119                                float[].class, int[].class, long[].class, short[].class}));
120                primitiveTypes.add(void.class);
121                for (Class<?> primitiveType : primitiveTypes) {
122                        primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
123                }
124
125                registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class,
126                                Float[].class, Integer[].class, Long[].class, Short[].class);
127                registerCommonClasses(Number.class, Number[].class, String.class, String[].class,
128                                Class.class, Class[].class, Object.class, Object[].class);
129                registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class,
130                                Error.class, StackTraceElement.class, StackTraceElement[].class);
131                registerCommonClasses(Enum.class, Iterable.class, Cloneable.class, Comparable.class);
132        }
133
134
135        /**
136         * Register the given common classes with the ClassUtils cache.
137         */
138        private static void registerCommonClasses(Class<?>... commonClasses) {
139                for (Class<?> clazz : commonClasses) {
140                        commonClassCache.put(clazz.getName(), clazz);
141                }
142        }
143
144        /**
145         * Return the default ClassLoader to use: typically the thread context
146         * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
147         * class will be used as fallback.
148         * <p>Call this method if you intend to use the thread context ClassLoader
149         * in a scenario where you clearly prefer a non-null ClassLoader reference:
150         * for example, for class path resource loading (but not necessarily for
151         * {@code Class.forName}, which accepts a {@code null} ClassLoader
152         * reference as well).
153         * @return the default ClassLoader (only {@code null} if even the system
154         * ClassLoader isn't accessible)
155         * @see Thread#getContextClassLoader()
156         * @see ClassLoader#getSystemClassLoader()
157         */
158        public static ClassLoader getDefaultClassLoader() {
159                ClassLoader cl = null;
160                try {
161                        cl = Thread.currentThread().getContextClassLoader();
162                }
163                catch (Throwable ex) {
164                        // Cannot access thread context ClassLoader - falling back...
165                }
166                if (cl == null) {
167                        // No thread context class loader -> use class loader of this class.
168                        cl = ClassUtils.class.getClassLoader();
169                        if (cl == null) {
170                                // getClassLoader() returning null indicates the bootstrap ClassLoader
171                                try {
172                                        cl = ClassLoader.getSystemClassLoader();
173                                }
174                                catch (Throwable ex) {
175                                        // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
176                                }
177                        }
178                }
179                return cl;
180        }
181
182        /**
183         * Override the thread context ClassLoader with the environment's bean ClassLoader
184         * if necessary, i.e. if the bean ClassLoader is not equivalent to the thread
185         * context ClassLoader already.
186         * @param classLoaderToUse the actual ClassLoader to use for the thread context
187         * @return the original thread context ClassLoader, or {@code null} if not overridden
188         */
189        public static ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse) {
190                Thread currentThread = Thread.currentThread();
191                ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
192                if (classLoaderToUse != null && !classLoaderToUse.equals(threadContextClassLoader)) {
193                        currentThread.setContextClassLoader(classLoaderToUse);
194                        return threadContextClassLoader;
195                }
196                else {
197                        return null;
198                }
199        }
200
201        /**
202         * Replacement for {@code Class.forName()} that also returns Class instances
203         * for primitives (e.g. "int") and array class names (e.g. "String[]").
204         * Furthermore, it is also capable of resolving inner class names in Java source
205         * style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
206         * @param name the name of the Class
207         * @param classLoader the class loader to use
208         * (may be {@code null}, which indicates the default class loader)
209         * @return a class instance for the supplied name
210         * @throws ClassNotFoundException if the class was not found
211         * @throws LinkageError if the class file could not be loaded
212         * @see Class#forName(String, boolean, ClassLoader)
213         */
214        public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
215                Assert.notNull(name, "Name must not be null");
216
217                Class<?> clazz = resolvePrimitiveClassName(name);
218                if (clazz == null) {
219                        clazz = commonClassCache.get(name);
220                }
221                if (clazz != null) {
222                        return clazz;
223                }
224
225                // "java.lang.String[]" style arrays
226                if (name.endsWith(ARRAY_SUFFIX)) {
227                        String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
228                        Class<?> elementClass = forName(elementClassName, classLoader);
229                        return Array.newInstance(elementClass, 0).getClass();
230                }
231
232                // "[Ljava.lang.String;" style arrays
233                if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
234                        String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
235                        Class<?> elementClass = forName(elementName, classLoader);
236                        return Array.newInstance(elementClass, 0).getClass();
237                }
238
239                // "[[I" or "[[Ljava.lang.String;" style arrays
240                if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
241                        String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
242                        Class<?> elementClass = forName(elementName, classLoader);
243                        return Array.newInstance(elementClass, 0).getClass();
244                }
245
246                ClassLoader clToUse = classLoader;
247                if (clToUse == null) {
248                        clToUse = getDefaultClassLoader();
249                }
250                try {
251                        return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
252                }
253                catch (ClassNotFoundException ex) {
254                        int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
255                        if (lastDotIndex != -1) {
256                                String innerClassName =
257                                                name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
258                                try {
259                                        return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
260                                }
261                                catch (ClassNotFoundException ex2) {
262                                        // Swallow - let original exception get through
263                                }
264                        }
265                        throw ex;
266                }
267        }
268
269        /**
270         * Resolve the given class name into a Class instance. Supports
271         * primitives (like "int") and array class names (like "String[]").
272         * <p>This is effectively equivalent to the {@code forName}
273         * method with the same arguments, with the only difference being
274         * the exceptions thrown in case of class loading failure.
275         * @param className the name of the Class
276         * @param classLoader the class loader to use
277         * (may be {@code null}, which indicates the default class loader)
278         * @return a class instance for the supplied name
279         * @throws IllegalArgumentException if the class name was not resolvable
280         * (that is, the class could not be found or the class file could not be loaded)
281         * @see #forName(String, ClassLoader)
282         */
283        public static Class<?> resolveClassName(String className, ClassLoader classLoader) throws IllegalArgumentException {
284                try {
285                        return forName(className, classLoader);
286                }
287                catch (ClassNotFoundException ex) {
288                        throw new IllegalArgumentException("Could not find class [" + className + "]", ex);
289                }
290                catch (LinkageError err) {
291                        throw new IllegalArgumentException("Unresolvable class definition for class [" + className + "]", err);
292                }
293        }
294
295        /**
296         * Determine whether the {@link Class} identified by the supplied name is present
297         * and can be loaded. Will return {@code false} if either the class or
298         * one of its dependencies is not present or cannot be loaded.
299         * @param className the name of the class to check
300         * @param classLoader the class loader to use
301         * (may be {@code null} which indicates the default class loader)
302         * @return whether the specified class is present
303         */
304        public static boolean isPresent(String className, ClassLoader classLoader) {
305                try {
306                        forName(className, classLoader);
307                        return true;
308                }
309                catch (Throwable ex) {
310                        // Class or one of its dependencies is not present...
311                        return false;
312                }
313        }
314
315        /**
316         * Check whether the given class is visible in the given ClassLoader.
317         * @param clazz the class to check (typically an interface)
318         * @param classLoader the ClassLoader to check against
319         * (may be {@code null} in which case this method will always return {@code true})
320         */
321        public static boolean isVisible(Class<?> clazz, ClassLoader classLoader) {
322                if (classLoader == null) {
323                        return true;
324                }
325                try {
326                        return (clazz == classLoader.loadClass(clazz.getName()));
327                        // Else: different class with same name found
328                }
329                catch (ClassNotFoundException ex) {
330                        // No corresponding class found at all
331                        return false;
332                }
333        }
334
335        /**
336         * Check whether the given class is cache-safe in the given context,
337         * i.e. whether it is loaded by the given ClassLoader or a parent of it.
338         * @param clazz the class to analyze
339         * @param classLoader the ClassLoader to potentially cache metadata in
340         * (may be {@code null} which indicates the system class loader)
341         */
342        public static boolean isCacheSafe(Class<?> clazz, ClassLoader classLoader) {
343                Assert.notNull(clazz, "Class must not be null");
344                try {
345                        ClassLoader target = clazz.getClassLoader();
346                        // Common cases
347                        if (target == classLoader || target == null) {
348                                return true;
349                        }
350                        if (classLoader == null) {
351                                return false;
352                        }
353                        // Check for match in ancestors -> positive
354                        ClassLoader current = classLoader;
355                        while (current != null) {
356                                current = current.getParent();
357                                if (current == target) {
358                                        return true;
359                                }
360                        }
361                        // Check for match in children -> negative
362                        while (target != null) {
363                                target = target.getParent();
364                                if (target == classLoader) {
365                                        return false;
366                                }
367                        }
368                }
369                catch (SecurityException ex) {
370                        // Fall through to Class reference comparison below
371                }
372
373                // Fallback for ClassLoaders without parent/child relationship:
374                // safe if same Class can be loaded from given ClassLoader
375                return (classLoader != null && isVisible(clazz, classLoader));
376        }
377
378        /**
379         * Resolve the given class name as primitive class, if appropriate,
380         * according to the JVM's naming rules for primitive classes.
381         * <p>Also supports the JVM's internal class names for primitive arrays.
382         * Does <i>not</i> support the "[]" suffix notation for primitive arrays;
383         * this is only supported by {@link #forName(String, ClassLoader)}.
384         * @param name the name of the potentially primitive class
385         * @return the primitive class, or {@code null} if the name does not denote
386         * a primitive class or primitive array class
387         */
388        public static Class<?> resolvePrimitiveClassName(String name) {
389                Class<?> result = null;
390                // Most class names will be quite long, considering that they
391                // SHOULD sit in a package, so a length check is worthwhile.
392                if (name != null && name.length() <= 8) {
393                        // Could be a primitive - likely.
394                        result = primitiveTypeNameMap.get(name);
395                }
396                return result;
397        }
398
399        /**
400         * Check if the given class represents a primitive wrapper,
401         * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
402         * @param clazz the class to check
403         * @return whether the given class is a primitive wrapper class
404         */
405        public static boolean isPrimitiveWrapper(Class<?> clazz) {
406                Assert.notNull(clazz, "Class must not be null");
407                return primitiveWrapperTypeMap.containsKey(clazz);
408        }
409
410        /**
411         * Check if the given class represents a primitive (i.e. boolean, byte,
412         * char, short, int, long, float, or double) or a primitive wrapper
413         * (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double).
414         * @param clazz the class to check
415         * @return whether the given class is a primitive or primitive wrapper class
416         */
417        public static boolean isPrimitiveOrWrapper(Class<?> clazz) {
418                Assert.notNull(clazz, "Class must not be null");
419                return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
420        }
421
422        /**
423         * Check if the given class represents an array of primitives,
424         * i.e. boolean, byte, char, short, int, long, float, or double.
425         * @param clazz the class to check
426         * @return whether the given class is a primitive array class
427         */
428        public static boolean isPrimitiveArray(Class<?> clazz) {
429                Assert.notNull(clazz, "Class must not be null");
430                return (clazz.isArray() && clazz.getComponentType().isPrimitive());
431        }
432
433        /**
434         * Check if the given class represents an array of primitive wrappers,
435         * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
436         * @param clazz the class to check
437         * @return whether the given class is a primitive wrapper array class
438         */
439        public static boolean isPrimitiveWrapperArray(Class<?> clazz) {
440                Assert.notNull(clazz, "Class must not be null");
441                return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType()));
442        }
443
444        /**
445         * Resolve the given class if it is a primitive class,
446         * returning the corresponding primitive wrapper type instead.
447         * @param clazz the class to check
448         * @return the original class, or a primitive wrapper for the original primitive type
449         */
450        public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) {
451                Assert.notNull(clazz, "Class must not be null");
452                return (clazz.isPrimitive() && clazz != void.class ? primitiveTypeToWrapperMap.get(clazz) : clazz);
453        }
454
455        /**
456         * Check if the right-hand side type may be assigned to the left-hand side
457         * type, assuming setting by reflection. Considers primitive wrapper
458         * classes as assignable to the corresponding primitive types.
459         * @param lhsType the target type
460         * @param rhsType the value type that should be assigned to the target type
461         * @return if the target type is assignable from the value type
462         * @see TypeUtils#isAssignable(java.lang.reflect.Type, java.lang.reflect.Type)
463         */
464        public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
465                Assert.notNull(lhsType, "Left-hand side type must not be null");
466                Assert.notNull(rhsType, "Right-hand side type must not be null");
467                if (lhsType.isAssignableFrom(rhsType)) {
468                        return true;
469                }
470                if (lhsType.isPrimitive()) {
471                        Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
472                        return (lhsType == resolvedPrimitive);
473                }
474                else {
475                        Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
476                        return (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper));
477                }
478        }
479
480        /**
481         * Determine if the given type is assignable from the given value,
482         * assuming setting by reflection. Considers primitive wrapper classes
483         * as assignable to the corresponding primitive types.
484         * @param type the target type
485         * @param value the value that should be assigned to the type
486         * @return if the type is assignable from the value
487         */
488        public static boolean isAssignableValue(Class<?> type, Object value) {
489                Assert.notNull(type, "Type must not be null");
490                return (value != null ? isAssignable(type, value.getClass()) : !type.isPrimitive());
491        }
492
493        /**
494         * Convert a "/"-based resource path to a "."-based fully qualified class name.
495         * @param resourcePath the resource path pointing to a class
496         * @return the corresponding fully qualified class name
497         */
498        public static String convertResourcePathToClassName(String resourcePath) {
499                Assert.notNull(resourcePath, "Resource path must not be null");
500                return resourcePath.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR);
501        }
502
503        /**
504         * Convert a "."-based fully qualified class name to a "/"-based resource path.
505         * @param className the fully qualified class name
506         * @return the corresponding resource path, pointing to the class
507         */
508        public static String convertClassNameToResourcePath(String className) {
509                Assert.notNull(className, "Class name must not be null");
510                return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
511        }
512
513        /**
514         * Return a path suitable for use with {@code ClassLoader.getResource}
515         * (also suitable for use with {@code Class.getResource} by prepending a
516         * slash ('/') to the return value). Built by taking the package of the specified
517         * class file, converting all dots ('.') to slashes ('/'), adding a trailing slash
518         * if necessary, and concatenating the specified resource name to this.
519         * <br/>As such, this function may be used to build a path suitable for
520         * loading a resource file that is in the same package as a class file,
521         * although {@link org.springframework.core.io.ClassPathResource} is usually
522         * even more convenient.
523         * @param clazz the Class whose package will be used as the base
524         * @param resourceName the resource name to append. A leading slash is optional.
525         * @return the built-up resource path
526         * @see ClassLoader#getResource
527         * @see Class#getResource
528         */
529        public static String addResourcePathToPackagePath(Class<?> clazz, String resourceName) {
530                Assert.notNull(resourceName, "Resource name must not be null");
531                if (!resourceName.startsWith("/")) {
532                        return classPackageAsResourcePath(clazz) + '/' + resourceName;
533                }
534                return classPackageAsResourcePath(clazz) + resourceName;
535        }
536
537        /**
538         * Given an input class object, return a string which consists of the
539         * class's package name as a pathname, i.e., all dots ('.') are replaced by
540         * slashes ('/'). Neither a leading nor trailing slash is added. The result
541         * could be concatenated with a slash and the name of a resource and fed
542         * directly to {@code ClassLoader.getResource()}. For it to be fed to
543         * {@code Class.getResource} instead, a leading slash would also have
544         * to be prepended to the returned value.
545         * @param clazz the input class. A {@code null} value or the default
546         * (empty) package will result in an empty string ("") being returned.
547         * @return a path which represents the package name
548         * @see ClassLoader#getResource
549         * @see Class#getResource
550         */
551        public static String classPackageAsResourcePath(Class<?> clazz) {
552                if (clazz == null) {
553                        return "";
554                }
555                String className = clazz.getName();
556                int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
557                if (packageEndIndex == -1) {
558                        return "";
559                }
560                String packageName = className.substring(0, packageEndIndex);
561                return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
562        }
563
564        /**
565         * Build a String that consists of the names of the classes/interfaces
566         * in the given array.
567         * <p>Basically like {@code AbstractCollection.toString()}, but stripping
568         * the "class "/"interface " prefix before every class name.
569         * @param classes an array of Class objects
570         * @return a String of form "[com.foo.Bar, com.foo.Baz]"
571         * @see java.util.AbstractCollection#toString()
572         */
573        public static String classNamesToString(Class<?>... classes) {
574                return classNamesToString(Arrays.asList(classes));
575        }
576
577        /**
578         * Build a String that consists of the names of the classes/interfaces
579         * in the given collection.
580         * <p>Basically like {@code AbstractCollection.toString()}, but stripping
581         * the "class "/"interface " prefix before every class name.
582         * @param classes a Collection of Class objects (may be {@code null})
583         * @return a String of form "[com.foo.Bar, com.foo.Baz]"
584         * @see java.util.AbstractCollection#toString()
585         */
586        public static String classNamesToString(Collection<Class<?>> classes) {
587                if (CollectionUtils.isEmpty(classes)) {
588                        return "[]";
589                }
590                StringBuilder sb = new StringBuilder("[");
591                for (Iterator<Class<?>> it = classes.iterator(); it.hasNext(); ) {
592                        Class<?> clazz = it.next();
593                        sb.append(clazz.getName());
594                        if (it.hasNext()) {
595                                sb.append(", ");
596                        }
597                }
598                sb.append("]");
599                return sb.toString();
600        }
601
602        /**
603         * Copy the given {@code Collection} into a {@code Class} array.
604         * <p>The {@code Collection} must contain {@code Class} elements only.
605         * @param collection the {@code Collection} to copy
606         * @return the {@code Class} array
607         * @since 3.1
608         * @see StringUtils#toStringArray
609         */
610        public static Class<?>[] toClassArray(Collection<Class<?>> collection) {
611                if (collection == null) {
612                        return null;
613                }
614                return collection.toArray(new Class<?>[collection.size()]);
615        }
616
617        /**
618         * Return all interfaces that the given instance implements as an array,
619         * including ones implemented by superclasses.
620         * @param instance the instance to analyze for interfaces
621         * @return all interfaces that the given instance implements as an array
622         */
623        public static Class<?>[] getAllInterfaces(Object instance) {
624                Assert.notNull(instance, "Instance must not be null");
625                return getAllInterfacesForClass(instance.getClass());
626        }
627
628        /**
629         * Return all interfaces that the given class implements as an array,
630         * including ones implemented by superclasses.
631         * <p>If the class itself is an interface, it gets returned as sole interface.
632         * @param clazz the class to analyze for interfaces
633         * @return all interfaces that the given object implements as an array
634         */
635        public static Class<?>[] getAllInterfacesForClass(Class<?> clazz) {
636                return getAllInterfacesForClass(clazz, null);
637        }
638
639        /**
640         * Return all interfaces that the given class implements as an array,
641         * including ones implemented by superclasses.
642         * <p>If the class itself is an interface, it gets returned as sole interface.
643         * @param clazz the class to analyze for interfaces
644         * @param classLoader the ClassLoader that the interfaces need to be visible in
645         * (may be {@code null} when accepting all declared interfaces)
646         * @return all interfaces that the given object implements as an array
647         */
648        public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, ClassLoader classLoader) {
649                return toClassArray(getAllInterfacesForClassAsSet(clazz, classLoader));
650        }
651
652        /**
653         * Return all interfaces that the given instance implements as a Set,
654         * including ones implemented by superclasses.
655         * @param instance the instance to analyze for interfaces
656         * @return all interfaces that the given instance implements as a Set
657         */
658        public static Set<Class<?>> getAllInterfacesAsSet(Object instance) {
659                Assert.notNull(instance, "Instance must not be null");
660                return getAllInterfacesForClassAsSet(instance.getClass());
661        }
662
663        /**
664         * Return all interfaces that the given class implements as a Set,
665         * including ones implemented by superclasses.
666         * <p>If the class itself is an interface, it gets returned as sole interface.
667         * @param clazz the class to analyze for interfaces
668         * @return all interfaces that the given object implements as a Set
669         */
670        public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz) {
671                return getAllInterfacesForClassAsSet(clazz, null);
672        }
673
674        /**
675         * Return all interfaces that the given class implements as a Set,
676         * including ones implemented by superclasses.
677         * <p>If the class itself is an interface, it gets returned as sole interface.
678         * @param clazz the class to analyze for interfaces
679         * @param classLoader the ClassLoader that the interfaces need to be visible in
680         * (may be {@code null} when accepting all declared interfaces)
681         * @return all interfaces that the given object implements as a Set
682         */
683        public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader) {
684                Assert.notNull(clazz, "Class must not be null");
685                if (clazz.isInterface() && isVisible(clazz, classLoader)) {
686                        return Collections.<Class<?>>singleton(clazz);
687                }
688                Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
689                Class<?> current = clazz;
690                while (current != null) {
691                        Class<?>[] ifcs = current.getInterfaces();
692                        for (Class<?> ifc : ifcs) {
693                                interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader));
694                        }
695                        current = current.getSuperclass();
696                }
697                return interfaces;
698        }
699
700        /**
701         * Create a composite interface Class for the given interfaces,
702         * implementing the given interfaces in one single Class.
703         * <p>This implementation builds a JDK proxy class for the given interfaces.
704         * @param interfaces the interfaces to merge
705         * @param classLoader the ClassLoader to create the composite Class in
706         * @return the merged interface as Class
707         * @throws IllegalArgumentException if the specified interfaces expose
708         * conflicting method signatures (or a similar constraint is violated)
709         * @see java.lang.reflect.Proxy#getProxyClass
710         */
711        public static Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader) {
712                Assert.notEmpty(interfaces, "Interface array must not be empty");
713                return Proxy.getProxyClass(classLoader, interfaces);
714        }
715
716        /**
717         * Determine the common ancestor of the given classes, if any.
718         * @param clazz1 the class to introspect
719         * @param clazz2 the other class to introspect
720         * @return the common ancestor (i.e. common superclass, one interface
721         * extending the other), or {@code null} if none found. If any of the
722         * given classes is {@code null}, the other class will be returned.
723         * @since 3.2.6
724         */
725        public static Class<?> determineCommonAncestor(Class<?> clazz1, Class<?> clazz2) {
726                if (clazz1 == null) {
727                        return clazz2;
728                }
729                if (clazz2 == null) {
730                        return clazz1;
731                }
732                if (clazz1.isAssignableFrom(clazz2)) {
733                        return clazz1;
734                }
735                if (clazz2.isAssignableFrom(clazz1)) {
736                        return clazz2;
737                }
738                Class<?> ancestor = clazz1;
739                do {
740                        ancestor = ancestor.getSuperclass();
741                        if (ancestor == null || Object.class == ancestor) {
742                                return null;
743                        }
744                }
745                while (!ancestor.isAssignableFrom(clazz2));
746                return ancestor;
747        }
748
749        /**
750         * Check whether the given object is a CGLIB proxy.
751         * @param object the object to check
752         * @see #isCglibProxyClass(Class)
753         * @see org.springframework.aop.support.AopUtils#isCglibProxy(Object)
754         */
755        public static boolean isCglibProxy(Object object) {
756                return isCglibProxyClass(object.getClass());
757        }
758
759        /**
760         * Check whether the specified class is a CGLIB-generated class.
761         * @param clazz the class to check
762         * @see #isCglibProxyClassName(String)
763         */
764        public static boolean isCglibProxyClass(Class<?> clazz) {
765                return (clazz != null && isCglibProxyClassName(clazz.getName()));
766        }
767
768        /**
769         * Check whether the specified class name is a CGLIB-generated class.
770         * @param className the class name to check
771         */
772        public static boolean isCglibProxyClassName(String className) {
773                return (className != null && className.contains(CGLIB_CLASS_SEPARATOR));
774        }
775
776        /**
777         * Return the user-defined class for the given instance: usually simply
778         * the class of the given instance, but the original class in case of a
779         * CGLIB-generated subclass.
780         * @param instance the instance to check
781         * @return the user-defined class
782         */
783        public static Class<?> getUserClass(Object instance) {
784                Assert.notNull(instance, "Instance must not be null");
785                return getUserClass(instance.getClass());
786        }
787
788        /**
789         * Return the user-defined class for the given class: usually simply the given
790         * class, but the original class in case of a CGLIB-generated subclass.
791         * @param clazz the class to check
792         * @return the user-defined class
793         */
794        public static Class<?> getUserClass(Class<?> clazz) {
795                if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
796                        Class<?> superclass = clazz.getSuperclass();
797                        if (superclass != null && Object.class != superclass) {
798                                return superclass;
799                        }
800                }
801                return clazz;
802        }
803
804        /**
805         * Return a descriptive name for the given object's type: usually simply
806         * the class name, but component type class name + "[]" for arrays,
807         * and an appended list of implemented interfaces for JDK proxies.
808         * @param value the value to introspect
809         * @return the qualified name of the class
810         */
811        public static String getDescriptiveType(Object value) {
812                if (value == null) {
813                        return null;
814                }
815                Class<?> clazz = value.getClass();
816                if (Proxy.isProxyClass(clazz)) {
817                        StringBuilder result = new StringBuilder(clazz.getName());
818                        result.append(" implementing ");
819                        Class<?>[] ifcs = clazz.getInterfaces();
820                        for (int i = 0; i < ifcs.length; i++) {
821                                result.append(ifcs[i].getName());
822                                if (i < ifcs.length - 1) {
823                                        result.append(',');
824                                }
825                        }
826                        return result.toString();
827                }
828                else if (clazz.isArray()) {
829                        return getQualifiedNameForArray(clazz);
830                }
831                else {
832                        return clazz.getName();
833                }
834        }
835
836        /**
837         * Check whether the given class matches the user-specified type name.
838         * @param clazz the class to check
839         * @param typeName the type name to match
840         */
841        public static boolean matchesTypeName(Class<?> clazz, String typeName) {
842                return (typeName != null &&
843                                (typeName.equals(clazz.getName()) || typeName.equals(clazz.getSimpleName()) ||
844                                                (clazz.isArray() && typeName.equals(getQualifiedNameForArray(clazz)))));
845        }
846
847        /**
848         * Get the class name without the qualified package name.
849         * @param className the className to get the short name for
850         * @return the class name of the class without the package name
851         * @throws IllegalArgumentException if the className is empty
852         */
853        public static String getShortName(String className) {
854                Assert.hasLength(className, "Class name must not be empty");
855                int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
856                int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
857                if (nameEndIndex == -1) {
858                        nameEndIndex = className.length();
859                }
860                String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
861                shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
862                return shortName;
863        }
864
865        /**
866         * Get the class name without the qualified package name.
867         * @param clazz the class to get the short name for
868         * @return the class name of the class without the package name
869         */
870        public static String getShortName(Class<?> clazz) {
871                return getShortName(getQualifiedName(clazz));
872        }
873
874        /**
875         * Return the short string name of a Java class in uncapitalized JavaBeans
876         * property format. Strips the outer class name in case of an inner class.
877         * @param clazz the class
878         * @return the short name rendered in a standard JavaBeans property format
879         * @see java.beans.Introspector#decapitalize(String)
880         */
881        public static String getShortNameAsProperty(Class<?> clazz) {
882                String shortName = getShortName(clazz);
883                int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR);
884                shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
885                return Introspector.decapitalize(shortName);
886        }
887
888        /**
889         * Determine the name of the class file, relative to the containing
890         * package: e.g. "String.class"
891         * @param clazz the class
892         * @return the file name of the ".class" file
893         */
894        public static String getClassFileName(Class<?> clazz) {
895                Assert.notNull(clazz, "Class must not be null");
896                String className = clazz.getName();
897                int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
898                return className.substring(lastDotIndex + 1) + CLASS_FILE_SUFFIX;
899        }
900
901        /**
902         * Determine the name of the package of the given class,
903         * e.g. "java.lang" for the {@code java.lang.String} class.
904         * @param clazz the class
905         * @return the package name, or the empty String if the class
906         * is defined in the default package
907         */
908        public static String getPackageName(Class<?> clazz) {
909                Assert.notNull(clazz, "Class must not be null");
910                return getPackageName(clazz.getName());
911        }
912
913        /**
914         * Determine the name of the package of the given fully-qualified class name,
915         * e.g. "java.lang" for the {@code java.lang.String} class name.
916         * @param fqClassName the fully-qualified class name
917         * @return the package name, or the empty String if the class
918         * is defined in the default package
919         */
920        public static String getPackageName(String fqClassName) {
921                Assert.notNull(fqClassName, "Class name must not be null");
922                int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
923                return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
924        }
925
926        /**
927         * Return the qualified name of the given class: usually simply
928         * the class name, but component type class name + "[]" for arrays.
929         * @param clazz the class
930         * @return the qualified name of the class
931         */
932        public static String getQualifiedName(Class<?> clazz) {
933                Assert.notNull(clazz, "Class must not be null");
934                if (clazz.isArray()) {
935                        return getQualifiedNameForArray(clazz);
936                }
937                else {
938                        return clazz.getName();
939                }
940        }
941
942        /**
943         * Build a nice qualified name for an array:
944         * component type class name + "[]".
945         * @param clazz the array class
946         * @return a qualified name for the array class
947         */
948        private static String getQualifiedNameForArray(Class<?> clazz) {
949                StringBuilder result = new StringBuilder();
950                while (clazz.isArray()) {
951                        clazz = clazz.getComponentType();
952                        result.append(ARRAY_SUFFIX);
953                }
954                result.insert(0, clazz.getName());
955                return result.toString();
956        }
957
958        /**
959         * Return the qualified name of the given method, consisting of
960         * fully qualified interface/class name + "." + method name.
961         * @param method the method
962         * @return the qualified name of the method
963         */
964        public static String getQualifiedMethodName(Method method) {
965                return getQualifiedMethodName(method, null);
966        }
967
968        /**
969         * Return the qualified name of the given method, consisting of
970         * fully qualified interface/class name + "." + method name.
971         * @param method the method
972         * @param clazz the clazz that the method is being invoked on
973         * (may be {@code null} to indicate the method's declaring class)
974         * @return the qualified name of the method
975         * @since 4.3.4
976         */
977        public static String getQualifiedMethodName(Method method, Class<?> clazz) {
978                Assert.notNull(method, "Method must not be null");
979                return (clazz != null ? clazz : method.getDeclaringClass()).getName() + '.' + method.getName();
980        }
981
982        /**
983         * Determine whether the given class has a public constructor with the given signature.
984         * <p>Essentially translates {@code NoSuchMethodException} to "false".
985         * @param clazz the clazz to analyze
986         * @param paramTypes the parameter types of the method
987         * @return whether the class has a corresponding constructor
988         * @see Class#getConstructor
989         */
990        public static boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes) {
991                return (getConstructorIfAvailable(clazz, paramTypes) != null);
992        }
993
994        /**
995         * Determine whether the given class has a public constructor with the given signature,
996         * and return it if available (else return {@code null}).
997         * <p>Essentially translates {@code NoSuchMethodException} to {@code null}.
998         * @param clazz the clazz to analyze
999         * @param paramTypes the parameter types of the method
1000         * @return the constructor, or {@code null} if not found
1001         * @see Class#getConstructor
1002         */
1003        public static <T> Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>... paramTypes) {
1004                Assert.notNull(clazz, "Class must not be null");
1005                try {
1006                        return clazz.getConstructor(paramTypes);
1007                }
1008                catch (NoSuchMethodException ex) {
1009                        return null;
1010                }
1011        }
1012
1013        /**
1014         * Determine whether the given class has a public method with the given signature.
1015         * <p>Essentially translates {@code NoSuchMethodException} to "false".
1016         * @param clazz the clazz to analyze
1017         * @param methodName the name of the method
1018         * @param paramTypes the parameter types of the method
1019         * @return whether the class has a corresponding method
1020         * @see Class#getMethod
1021         */
1022        public static boolean hasMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
1023                return (getMethodIfAvailable(clazz, methodName, paramTypes) != null);
1024        }
1025
1026        /**
1027         * Determine whether the given class has a public method with the given signature,
1028         * and return it if available (else throws an {@code IllegalStateException}).
1029         * <p>In case of any signature specified, only returns the method if there is a
1030         * unique candidate, i.e. a single public method with the specified name.
1031         * <p>Essentially translates {@code NoSuchMethodException} to {@code IllegalStateException}.
1032         * @param clazz the clazz to analyze
1033         * @param methodName the name of the method
1034         * @param paramTypes the parameter types of the method
1035         * (may be {@code null} to indicate any signature)
1036         * @return the method (never {@code null})
1037         * @throws IllegalStateException if the method has not been found
1038         * @see Class#getMethod
1039         */
1040        public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
1041                Assert.notNull(clazz, "Class must not be null");
1042                Assert.notNull(methodName, "Method name must not be null");
1043                if (paramTypes != null) {
1044                        try {
1045                                return clazz.getMethod(methodName, paramTypes);
1046                        }
1047                        catch (NoSuchMethodException ex) {
1048                                throw new IllegalStateException("Expected method not found: " + ex);
1049                        }
1050                }
1051                else {
1052                        Set<Method> candidates = new HashSet<Method>(1);
1053                        Method[] methods = clazz.getMethods();
1054                        for (Method method : methods) {
1055                                if (methodName.equals(method.getName())) {
1056                                        candidates.add(method);
1057                                }
1058                        }
1059                        if (candidates.size() == 1) {
1060                                return candidates.iterator().next();
1061                        }
1062                        else if (candidates.isEmpty()) {
1063                                throw new IllegalStateException("Expected method not found: " + clazz.getName() + '.' + methodName);
1064                        }
1065                        else {
1066                                throw new IllegalStateException("No unique method found: " + clazz.getName() + '.' + methodName);
1067                        }
1068                }
1069        }
1070
1071        /**
1072         * Determine whether the given class has a public method with the given signature,
1073         * and return it if available (else return {@code null}).
1074         * <p>In case of any signature specified, only returns the method if there is a
1075         * unique candidate, i.e. a single public method with the specified name.
1076         * <p>Essentially translates {@code NoSuchMethodException} to {@code null}.
1077         * @param clazz the clazz to analyze
1078         * @param methodName the name of the method
1079         * @param paramTypes the parameter types of the method
1080         * (may be {@code null} to indicate any signature)
1081         * @return the method, or {@code null} if not found
1082         * @see Class#getMethod
1083         */
1084        public static Method getMethodIfAvailable(Class<?> clazz, String methodName, Class<?>... paramTypes) {
1085                Assert.notNull(clazz, "Class must not be null");
1086                Assert.notNull(methodName, "Method name must not be null");
1087                if (paramTypes != null) {
1088                        try {
1089                                return clazz.getMethod(methodName, paramTypes);
1090                        }
1091                        catch (NoSuchMethodException ex) {
1092                                return null;
1093                        }
1094                }
1095                else {
1096                        Set<Method> candidates = new HashSet<Method>(1);
1097                        Method[] methods = clazz.getMethods();
1098                        for (Method method : methods) {
1099                                if (methodName.equals(method.getName())) {
1100                                        candidates.add(method);
1101                                }
1102                        }
1103                        if (candidates.size() == 1) {
1104                                return candidates.iterator().next();
1105                        }
1106                        return null;
1107                }
1108        }
1109
1110        /**
1111         * Return the number of methods with a given name (with any argument types),
1112         * for the given class and/or its superclasses. Includes non-public methods.
1113         * @param clazz the clazz to check
1114         * @param methodName the name of the method
1115         * @return the number of methods with the given name
1116         */
1117        public static int getMethodCountForName(Class<?> clazz, String methodName) {
1118                Assert.notNull(clazz, "Class must not be null");
1119                Assert.notNull(methodName, "Method name must not be null");
1120                int count = 0;
1121                Method[] declaredMethods = clazz.getDeclaredMethods();
1122                for (Method method : declaredMethods) {
1123                        if (methodName.equals(method.getName())) {
1124                                count++;
1125                        }
1126                }
1127                Class<?>[] ifcs = clazz.getInterfaces();
1128                for (Class<?> ifc : ifcs) {
1129                        count += getMethodCountForName(ifc, methodName);
1130                }
1131                if (clazz.getSuperclass() != null) {
1132                        count += getMethodCountForName(clazz.getSuperclass(), methodName);
1133                }
1134                return count;
1135        }
1136
1137        /**
1138         * Does the given class or one of its superclasses at least have one or more
1139         * methods with the supplied name (with any argument types)?
1140         * Includes non-public methods.
1141         * @param clazz the clazz to check
1142         * @param methodName the name of the method
1143         * @return whether there is at least one method with the given name
1144         */
1145        public static boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName) {
1146                Assert.notNull(clazz, "Class must not be null");
1147                Assert.notNull(methodName, "Method name must not be null");
1148                Method[] declaredMethods = clazz.getDeclaredMethods();
1149                for (Method method : declaredMethods) {
1150                        if (method.getName().equals(methodName)) {
1151                                return true;
1152                        }
1153                }
1154                Class<?>[] ifcs = clazz.getInterfaces();
1155                for (Class<?> ifc : ifcs) {
1156                        if (hasAtLeastOneMethodWithName(ifc, methodName)) {
1157                                return true;
1158                        }
1159                }
1160                return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
1161        }
1162
1163        /**
1164         * Given a method, which may come from an interface, and a target class used
1165         * in the current reflective invocation, find the corresponding target method
1166         * if there is one. E.g. the method may be {@code IFoo.bar()} and the
1167         * target class may be {@code DefaultFoo}. In this case, the method may be
1168         * {@code DefaultFoo.bar()}. This enables attributes on that method to be found.
1169         * <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod},
1170         * this method does <i>not</i> resolve Java 5 bridge methods automatically.
1171         * Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
1172         * if bridge method resolution is desirable (e.g. for obtaining metadata from
1173         * the original method definition).
1174         * <p><b>NOTE:</b> Since Spring 3.1.1, if Java security settings disallow reflective
1175         * access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation
1176         * will fall back to returning the originally provided method.
1177         * @param method the method to be invoked, which may come from an interface
1178         * @param targetClass the target class for the current invocation.
1179         * May be {@code null} or may not even implement the method.
1180         * @return the specific target method, or the original method if the
1181         * {@code targetClass} doesn't implement it or is {@code null}
1182         */
1183        public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
1184                if (method != null && isOverridable(method, targetClass) &&
1185                                targetClass != null && targetClass != method.getDeclaringClass()) {
1186                        try {
1187                                if (Modifier.isPublic(method.getModifiers())) {
1188                                        try {
1189                                                return targetClass.getMethod(method.getName(), method.getParameterTypes());
1190                                        }
1191                                        catch (NoSuchMethodException ex) {
1192                                                return method;
1193                                        }
1194                                }
1195                                else {
1196                                        Method specificMethod =
1197                                                        ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
1198                                        return (specificMethod != null ? specificMethod : method);
1199                                }
1200                        }
1201                        catch (SecurityException ex) {
1202                                // Security settings are disallowing reflective access; fall back to 'method' below.
1203                        }
1204                }
1205                return method;
1206        }
1207
1208        /**
1209         * Determine whether the given method is declared by the user or at least pointing to
1210         * a user-declared method.
1211         * <p>Checks {@link Method#isSynthetic()} (for implementation methods) as well as the
1212         * {@code GroovyObject} interface (for interface methods; on an implementation class,
1213         * implementations of the {@code GroovyObject} methods will be marked as synthetic anyway).
1214         * Note that, despite being synthetic, bridge methods ({@link Method#isBridge()}) are considered
1215         * as user-level methods since they are eventually pointing to a user-declared generic method.
1216         * @param method the method to check
1217         * @return {@code true} if the method can be considered as user-declared; [@code false} otherwise
1218         */
1219        public static boolean isUserLevelMethod(Method method) {
1220                Assert.notNull(method, "Method must not be null");
1221                return (method.isBridge() || (!method.isSynthetic() && !isGroovyObjectMethod(method)));
1222        }
1223
1224        private static boolean isGroovyObjectMethod(Method method) {
1225                return method.getDeclaringClass().getName().equals("groovy.lang.GroovyObject");
1226        }
1227
1228        /**
1229         * Determine whether the given method is overridable in the given target class.
1230         * @param method the method to check
1231         * @param targetClass the target class to check against
1232         */
1233        private static boolean isOverridable(Method method, Class<?> targetClass) {
1234                if (Modifier.isPrivate(method.getModifiers())) {
1235                        return false;
1236                }
1237                if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
1238                        return true;
1239                }
1240                return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));
1241        }
1242
1243        /**
1244         * Return a public static method of a class.
1245         * @param clazz the class which defines the method
1246         * @param methodName the static method name
1247         * @param args the parameter types to the method
1248         * @return the static method, or {@code null} if no static method was found
1249         * @throws IllegalArgumentException if the method name is blank or the clazz is null
1250         */
1251        public static Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args) {
1252                Assert.notNull(clazz, "Class must not be null");
1253                Assert.notNull(methodName, "Method name must not be null");
1254                try {
1255                        Method method = clazz.getMethod(methodName, args);
1256                        return Modifier.isStatic(method.getModifiers()) ? method : null;
1257                }
1258                catch (NoSuchMethodException ex) {
1259                        return null;
1260                }
1261        }
1262
1263}