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