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