001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.util;
018:
019: import java.beans.Introspector;
020: import java.lang.reflect.Array;
021: import java.lang.reflect.Constructor;
022: import java.lang.reflect.Method;
023: import java.lang.reflect.Modifier;
024: import java.lang.reflect.Proxy;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.HashMap;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: /**
040: * Miscellaneous class utility methods. Mainly for internal use within the
041: * framework; consider Jakarta's Commons Lang for a more comprehensive suite
042: * of class utilities.
043: *
044: * @author Keith Donald
045: * @author Rob Harrop
046: * @author Juergen Hoeller
047: * @since 1.1
048: */
049: public abstract class ClassUtils {
050:
051: /** Suffix for array class names */
052: public static final String ARRAY_SUFFIX = "[]";
053:
054: /** The package separator character '.' */
055: private static final char PACKAGE_SEPARATOR = '.';
056:
057: /** The inner class separator character '$' */
058: private static final char INNER_CLASS_SEPARATOR = '$';
059:
060: /** The CGLIB class separator character "$$" */
061: public static final String CGLIB_CLASS_SEPARATOR = "$$";
062:
063: /** The ".class" file suffix */
064: public static final String CLASS_FILE_SUFFIX = ".class";
065:
066: private static final Log logger = LogFactory
067: .getLog(ClassUtils.class);
068:
069: /**
070: * Map with primitive wrapper type as key and corresponding primitive
071: * type as value, for example: Integer.class -> int.class.
072: */
073: private static final Map primitiveWrapperTypeMap = new HashMap(8);
074:
075: /**
076: * Map with primitive type name as key and corresponding primitive
077: * type as value, for example: "int" -> "int.class".
078: */
079: private static final Map primitiveTypeNameMap = new HashMap(8);
080:
081: static {
082: primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
083: primitiveWrapperTypeMap.put(Byte.class, byte.class);
084: primitiveWrapperTypeMap.put(Character.class, char.class);
085: primitiveWrapperTypeMap.put(Double.class, double.class);
086: primitiveWrapperTypeMap.put(Float.class, float.class);
087: primitiveWrapperTypeMap.put(Integer.class, int.class);
088: primitiveWrapperTypeMap.put(Long.class, long.class);
089: primitiveWrapperTypeMap.put(Short.class, short.class);
090:
091: for (Iterator it = primitiveWrapperTypeMap.values().iterator(); it
092: .hasNext();) {
093: Class primitiveClass = (Class) it.next();
094: primitiveTypeNameMap.put(primitiveClass.getName(),
095: primitiveClass);
096: }
097: }
098:
099: /**
100: * Return the default ClassLoader to use: typically the thread context
101: * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
102: * class will be used as fallback.
103: * <p>Call this method if you intend to use the thread context ClassLoader
104: * in a scenario where you absolutely need a non-null ClassLoader reference:
105: * for example, for class path resource loading (but not necessarily for
106: * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
107: * reference as well).
108: * @return the default ClassLoader (never <code>null</code>)
109: * @see java.lang.Thread#getContextClassLoader()
110: */
111: public static ClassLoader getDefaultClassLoader() {
112: ClassLoader cl = null;
113: try {
114: cl = Thread.currentThread().getContextClassLoader();
115: } catch (Throwable ex) {
116: logger
117: .debug(
118: "Cannot access thread context ClassLoader - falling back to system class loader",
119: ex);
120: }
121: if (cl == null) {
122: // No thread context class loader -> use class loader of this class.
123: cl = ClassUtils.class.getClassLoader();
124: }
125: return cl;
126: }
127:
128: /**
129: * Determine whether the {@link Class} identified by the supplied name is present
130: * and can be loaded. Will return <code>false</code> if either the class or
131: * one of its dependencies is not present or cannot be loaded.
132: * @param className the name of the class to check
133: * @return whether the specified class is present
134: */
135: public static boolean isPresent(String className) {
136: return isPresent(className, getDefaultClassLoader());
137: }
138:
139: /**
140: * Determine whether the {@link Class} identified by the supplied name is present
141: * and can be loaded. Will return <code>false</code> if either the class or
142: * one of its dependencies is not present or cannot be loaded.
143: * @param className the name of the class to check
144: * @param classLoader the class loader to use
145: * (may be <code>null</code>, which indicates the default class loader)
146: * @return whether the specified class is present
147: */
148: public static boolean isPresent(String className,
149: ClassLoader classLoader) {
150: try {
151: forName(className, classLoader);
152: return true;
153: } catch (Throwable ex) {
154: if (logger.isDebugEnabled()) {
155: logger
156: .debug("Class ["
157: + className
158: + "] or one of its dependencies is not present: "
159: + ex);
160: }
161: return false;
162: }
163: }
164:
165: /**
166: * Replacement for <code>Class.forName()</code> that also returns Class instances
167: * for primitives (like "int") and array class names (like "String[]").
168: * <p>Always uses the default class loader: that is, preferably the thread context
169: * class loader, or the ClassLoader that loaded the ClassUtils class as fallback.
170: * @param name the name of the Class
171: * @return Class instance for the supplied name
172: * @throws ClassNotFoundException if the class was not found
173: * @throws LinkageError if the class file could not be loaded
174: * @see Class#forName(String, boolean, ClassLoader)
175: * @see #getDefaultClassLoader()
176: */
177: public static Class forName(String name)
178: throws ClassNotFoundException, LinkageError {
179: return forName(name, getDefaultClassLoader());
180: }
181:
182: /**
183: * Replacement for <code>Class.forName()</code> that also returns Class instances
184: * for primitives (like "int") and array class names (like "String[]").
185: * @param name the name of the Class
186: * @param classLoader the class loader to use
187: * (may be <code>null</code>, which indicates the default class loader)
188: * @return Class instance for the supplied name
189: * @throws ClassNotFoundException if the class was not found
190: * @throws LinkageError if the class file could not be loaded
191: * @see Class#forName(String, boolean, ClassLoader)
192: */
193: public static Class forName(String name, ClassLoader classLoader)
194: throws ClassNotFoundException, LinkageError {
195: Assert.notNull(name, "Name must not be null");
196: Class clazz = resolvePrimitiveClassName(name);
197: if (clazz != null) {
198: return clazz;
199: }
200: if (name.endsWith(ARRAY_SUFFIX)) {
201: // special handling for array class names
202: String elementClassName = name.substring(0, name.length()
203: - ARRAY_SUFFIX.length());
204: Class elementClass = forName(elementClassName, classLoader);
205: return Array.newInstance(elementClass, 0).getClass();
206: }
207: ClassLoader classLoaderToUse = classLoader;
208: if (classLoaderToUse == null) {
209: classLoaderToUse = getDefaultClassLoader();
210: }
211: return classLoaderToUse.loadClass(name);
212: }
213:
214: /**
215: * Resolve the given class name into a Class instance. Supports
216: * primitives (like "int") and array class names (like "String[]").
217: * <p>This is effectively equivalent to the <code>forName</code>
218: * method with the same arguments, with the only difference being
219: * the exceptions thrown in case of class loading failure.
220: * @param className the name of the Class
221: * @param classLoader the class loader to use
222: * (may be <code>null</code>, which indicates the default class loader)
223: * @return Class instance for the supplied name
224: * @throws IllegalArgumentException if the class name was not resolvable
225: * (that is, the class could not be found or the class file could not be loaded)
226: * @see #forName(String, ClassLoader)
227: */
228: public static Class resolveClassName(String className,
229: ClassLoader classLoader) throws IllegalArgumentException {
230: try {
231: return forName(className, classLoader);
232: } catch (ClassNotFoundException ex) {
233: throw new IllegalArgumentException("Cannot find class ["
234: + className + "]. Root cause: " + ex);
235: } catch (LinkageError ex) {
236: throw new IllegalArgumentException(
237: "Error loading class ["
238: + className
239: + "]: problem with class file or dependent class. Root cause: "
240: + ex);
241: }
242: }
243:
244: /**
245: * Resolve the given class name as primitive class, if appropriate.
246: * @param name the name of the potentially primitive class
247: * @return the primitive class, or <code>null</code> if the name does not denote
248: * a primitive class
249: */
250: public static Class resolvePrimitiveClassName(String name) {
251: Class result = null;
252: // Most class names will be quite long, considering that they
253: // SHOULD sit in a package, so a length check is worthwhile.
254: if (name != null && name.length() <= 8) {
255: // Could be a primitive - likely.
256: result = (Class) primitiveTypeNameMap.get(name);
257: }
258: return result;
259: }
260:
261: /**
262: * Return the user-defined class for the given instance: usually simply
263: * the class of the given instance, but the original class in case of a
264: * CGLIB-generated subclass.
265: * @param instance the instance to check
266: * @return the user-defined class
267: */
268: public static Class getUserClass(Object instance) {
269: Assert.notNull(instance, "Instance must not be null");
270: return getUserClass(instance.getClass());
271: }
272:
273: /**
274: * Return the user-defined class for the given class: usually simply the given
275: * class, but the original class in case of a CGLIB-generated subclass.
276: * @param clazz the class to check
277: * @return the user-defined class
278: */
279: public static Class getUserClass(Class clazz) {
280: return (clazz != null
281: && clazz.getName().indexOf(CGLIB_CLASS_SEPARATOR) != -1 ? clazz
282: .getSuperclass()
283: : clazz);
284: }
285:
286: /**
287: * Get the class name without the qualified package name.
288: * @param className the className to get the short name for
289: * @return the class name of the class without the package name
290: * @throws IllegalArgumentException if the className is empty
291: */
292: public static String getShortName(String className) {
293: Assert.hasLength(className, "Class name must not be empty");
294: int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
295: int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
296: if (nameEndIndex == -1) {
297: nameEndIndex = className.length();
298: }
299: String shortName = className.substring(lastDotIndex + 1,
300: nameEndIndex);
301: shortName = shortName.replace(INNER_CLASS_SEPARATOR,
302: PACKAGE_SEPARATOR);
303: return shortName;
304: }
305:
306: /**
307: * Get the class name without the qualified package name.
308: * @param clazz the class to get the short name for
309: * @return the class name of the class without the package name
310: */
311: public static String getShortName(Class clazz) {
312: return getShortName(getQualifiedName(clazz));
313: }
314:
315: /**
316: * Return the short string name of a Java class in decapitalized
317: * JavaBeans property format.
318: * @param clazz the class
319: * @return the short name rendered in a standard JavaBeans property format
320: * @see java.beans.Introspector#decapitalize(String)
321: */
322: public static String getShortNameAsProperty(Class clazz) {
323: return Introspector.decapitalize(getShortName(clazz));
324: }
325:
326: /**
327: * Determine the name of the class file, relative to the containing
328: * package: e.g. "String.class"
329: * @param clazz the class
330: * @return the file name of the ".class" file
331: */
332: public static String getClassFileName(Class clazz) {
333: Assert.notNull(clazz, "Class must not be null");
334: String className = clazz.getName();
335: int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
336: return className.substring(lastDotIndex + 1)
337: + CLASS_FILE_SUFFIX;
338: }
339:
340: /**
341: * Return the qualified name of the given class: usually simply
342: * the class name, but component type class name + "[]" for arrays.
343: * @param clazz the class
344: * @return the qualified name of the class
345: */
346: public static String getQualifiedName(Class clazz) {
347: Assert.notNull(clazz, "Class must not be null");
348: if (clazz.isArray()) {
349: return getQualifiedNameForArray(clazz);
350: } else {
351: return clazz.getName();
352: }
353: }
354:
355: /**
356: * Build a nice qualified name for an array:
357: * component type class name + "[]".
358: * @param clazz the array class
359: * @return a qualified name for the array class
360: */
361: private static String getQualifiedNameForArray(Class clazz) {
362: StringBuffer buffer = new StringBuffer();
363: while (clazz.isArray()) {
364: clazz = clazz.getComponentType();
365: buffer.append(ClassUtils.ARRAY_SUFFIX);
366: }
367: buffer.insert(0, clazz.getName());
368: return buffer.toString();
369: }
370:
371: /**
372: * Return the qualified name of the given method, consisting of
373: * fully qualified interface/class name + "." + method name.
374: * @param method the method
375: * @return the qualified name of the method
376: */
377: public static String getQualifiedMethodName(Method method) {
378: Assert.notNull(method, "Method must not be null");
379: return method.getDeclaringClass().getName() + "."
380: + method.getName();
381: }
382:
383: /**
384: * Determine whether the given class has a constructor with the given signature.
385: * <p>Essentially translates <code>NoSuchMethodException</code> to "false".
386: * @param clazz the clazz to analyze
387: * @param paramTypes the parameter types of the method
388: * @return whether the class has a corresponding constructor
389: * @see java.lang.Class#getMethod
390: */
391: public static boolean hasConstructor(Class clazz, Class[] paramTypes) {
392: return (getConstructorIfAvailable(clazz, paramTypes) != null);
393: }
394:
395: /**
396: * Determine whether the given class has a constructor with the given signature,
397: * and return it if available (else return <code>null</code>).
398: * <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
399: * @param clazz the clazz to analyze
400: * @param paramTypes the parameter types of the method
401: * @return the constructor, or <code>null</code> if not found
402: * @see java.lang.Class#getConstructor
403: */
404: public static Constructor getConstructorIfAvailable(Class clazz,
405: Class[] paramTypes) {
406: Assert.notNull(clazz, "Class must not be null");
407: try {
408: return clazz.getConstructor(paramTypes);
409: } catch (NoSuchMethodException ex) {
410: return null;
411: }
412: }
413:
414: /**
415: * Determine whether the given class has a method with the given signature.
416: * <p>Essentially translates <code>NoSuchMethodException</code> to "false".
417: * @param clazz the clazz to analyze
418: * @param methodName the name of the method
419: * @param paramTypes the parameter types of the method
420: * @return whether the class has a corresponding method
421: * @see java.lang.Class#getMethod
422: */
423: public static boolean hasMethod(Class clazz, String methodName,
424: Class[] paramTypes) {
425: return (getMethodIfAvailable(clazz, methodName, paramTypes) != null);
426: }
427:
428: /**
429: * Determine whether the given class has a method with the given signature,
430: * and return it if available (else return <code>null</code>).
431: * <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
432: * @param clazz the clazz to analyze
433: * @param methodName the name of the method
434: * @param paramTypes the parameter types of the method
435: * @return the method, or <code>null</code> if not found
436: * @see java.lang.Class#getMethod
437: */
438: public static Method getMethodIfAvailable(Class clazz,
439: String methodName, Class[] paramTypes) {
440: Assert.notNull(clazz, "Class must not be null");
441: Assert.notNull(methodName, "Method name must not be null");
442: try {
443: return clazz.getMethod(methodName, paramTypes);
444: } catch (NoSuchMethodException ex) {
445: return null;
446: }
447: }
448:
449: /**
450: * Return the number of methods with a given name (with any argument types),
451: * for the given class and/or its superclasses. Includes non-public methods.
452: * @param clazz the clazz to check
453: * @param methodName the name of the method
454: * @return the number of methods with the given name
455: */
456: public static int getMethodCountForName(Class clazz,
457: String methodName) {
458: Assert.notNull(clazz, "Class must not be null");
459: Assert.notNull(methodName, "Method name must not be null");
460: int count = 0;
461: for (int i = 0; i < clazz.getDeclaredMethods().length; i++) {
462: Method method = clazz.getDeclaredMethods()[i];
463: if (methodName.equals(method.getName())) {
464: count++;
465: }
466: }
467: Class[] ifcs = clazz.getInterfaces();
468: for (int i = 0; i < ifcs.length; i++) {
469: count += getMethodCountForName(ifcs[i], methodName);
470: }
471: if (clazz.getSuperclass() != null) {
472: count += getMethodCountForName(clazz.getSuperclass(),
473: methodName);
474: }
475: return count;
476: }
477:
478: /**
479: * Does the given class and/or its superclasses at least have one or more
480: * methods (with any argument types)? Includes non-public methods.
481: * @param clazz the clazz to check
482: * @param methodName the name of the method
483: * @return whether there is at least one method with the given name
484: */
485: public static boolean hasAtLeastOneMethodWithName(Class clazz,
486: String methodName) {
487: Assert.notNull(clazz, "Class must not be null");
488: Assert.notNull(methodName, "Method name must not be null");
489: for (int i = 0; i < clazz.getDeclaredMethods().length; i++) {
490: Method method = clazz.getDeclaredMethods()[i];
491: if (method.getName().equals(methodName)) {
492: return true;
493: }
494: }
495: Class[] ifcs = clazz.getInterfaces();
496: for (int i = 0; i < ifcs.length; i++) {
497: if (hasAtLeastOneMethodWithName(ifcs[i], methodName)) {
498: return true;
499: }
500: }
501: return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(
502: clazz.getSuperclass(), methodName));
503: }
504:
505: /**
506: * Given a method, which may come from an interface, and a target class used
507: * in the current reflective invocation, find the corresponding target method
508: * if there is one. E.g. the method may be <code>IFoo.bar()</code> and the
509: * target class may be <code>DefaultFoo</code>. In this case, the method may be
510: * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
511: * @param method the method to be invoked, which may come from an interface
512: * @param targetClass the target class for the current invocation.
513: * May be <code>null</code> or may not even implement the method.
514: * @return the specific target method, or the original method if the
515: * <code>targetClass</code> doesn't implement it or is <code>null</code>
516: */
517: public static Method getMostSpecificMethod(Method method,
518: Class targetClass) {
519: if (method != null && targetClass != null) {
520: try {
521: method = targetClass.getMethod(method.getName(), method
522: .getParameterTypes());
523: } catch (NoSuchMethodException ex) {
524: // Perhaps the target class doesn't implement this method:
525: // that's fine, just use the original method.
526: }
527: }
528: return method;
529: }
530:
531: /**
532: * Return a static method of a class.
533: * @param methodName the static method name
534: * @param clazz the class which defines the method
535: * @param args the parameter types to the method
536: * @return the static method, or <code>null</code> if no static method was found
537: * @throws IllegalArgumentException if the method name is blank or the clazz is null
538: */
539: public static Method getStaticMethod(Class clazz,
540: String methodName, Class[] args) {
541: Assert.notNull(clazz, "Class must not be null");
542: Assert.notNull(methodName, "Method name must not be null");
543: try {
544: Method method = clazz.getDeclaredMethod(methodName, args);
545: if ((method.getModifiers() & Modifier.STATIC) != 0) {
546: return method;
547: }
548: } catch (NoSuchMethodException ex) {
549: }
550: return null;
551: }
552:
553: /**
554: * Check if the given class represents a primitive wrapper,
555: * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
556: * @param clazz the class to check
557: * @return whether the given class is a primitive wrapper class
558: */
559: public static boolean isPrimitiveWrapper(Class clazz) {
560: Assert.notNull(clazz, "Class must not be null");
561: return primitiveWrapperTypeMap.containsKey(clazz);
562: }
563:
564: /**
565: * Check if the given class represents a primitive (i.e. boolean, byte,
566: * char, short, int, long, float, or double) or a primitive wrapper
567: * (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double).
568: * @param clazz the class to check
569: * @return whether the given class is a primitive or primitive wrapper class
570: */
571: public static boolean isPrimitiveOrWrapper(Class clazz) {
572: Assert.notNull(clazz, "Class must not be null");
573: return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
574: }
575:
576: /**
577: * Check if the given class represents an array of primitives,
578: * i.e. boolean, byte, char, short, int, long, float, or double.
579: * @param clazz the class to check
580: * @return whether the given class is a primitive array class
581: */
582: public static boolean isPrimitiveArray(Class clazz) {
583: Assert.notNull(clazz, "Class must not be null");
584: return (clazz.isArray() && clazz.getComponentType()
585: .isPrimitive());
586: }
587:
588: /**
589: * Check if the given class represents an array of primitive wrappers,
590: * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
591: * @param clazz the class to check
592: * @return whether the given class is a primitive wrapper array class
593: */
594: public static boolean isPrimitiveWrapperArray(Class clazz) {
595: Assert.notNull(clazz, "Class must not be null");
596: return (clazz.isArray() && isPrimitiveWrapper(clazz
597: .getComponentType()));
598: }
599:
600: /**
601: * Determine if the given target type is assignable from the given value
602: * type, assuming setting by reflection. Considers primitive wrapper
603: * classes as assignable to the corresponding primitive types.
604: * @param targetType the target type
605: * @param valueType the value type that should be assigned to the target type
606: * @return if the target type is assignable from the value type
607: */
608: public static boolean isAssignable(Class targetType, Class valueType) {
609: Assert.notNull(targetType, "Target type must not be null");
610: Assert.notNull(valueType, "Value type must not be null");
611: return (targetType.isAssignableFrom(valueType) || targetType
612: .equals(primitiveWrapperTypeMap.get(valueType)));
613: }
614:
615: /**
616: * Determine if the given type is assignable from the given value,
617: * assuming setting by reflection. Considers primitive wrapper classes
618: * as assignable to the corresponding primitive types.
619: * @param type the target type
620: * @param value the value that should be assigned to the type
621: * @return if the type is assignable from the value
622: */
623: public static boolean isAssignableValue(Class type, Object value) {
624: Assert.notNull(type, "Type must not be null");
625: return (value != null ? isAssignable(type, value.getClass())
626: : !type.isPrimitive());
627: }
628:
629: /**
630: * Return a path suitable for use with <code>ClassLoader.getResource</code>
631: * (also suitable for use with <code>Class.getResource</code> by prepending a
632: * slash ('/') to the return value. Built by taking the package of the specified
633: * class file, converting all dots ('.') to slashes ('/'), adding a trailing slash
634: * if necesssary, and concatenating the specified resource name to this.
635: * <br/>As such, this function may be used to build a path suitable for
636: * loading a resource file that is in the same package as a class file,
637: * although {@link org.springframework.core.io.ClassPathResource} is usually
638: * even more convenient.
639: * @param clazz the Class whose package will be used as the base
640: * @param resourceName the resource name to append. A leading slash is optional.
641: * @return the built-up resource path
642: * @see java.lang.ClassLoader#getResource
643: * @see java.lang.Class#getResource
644: */
645: public static String addResourcePathToPackagePath(Class clazz,
646: String resourceName) {
647: Assert.notNull(resourceName, "Resource name must not be null");
648: if (!resourceName.startsWith("/")) {
649: return classPackageAsResourcePath(clazz) + "/"
650: + resourceName;
651: }
652: return classPackageAsResourcePath(clazz) + resourceName;
653: }
654:
655: /**
656: * Given an input class object, return a string which consists of the
657: * class's package name as a pathname, i.e., all dots ('.') are replaced by
658: * slashes ('/'). Neither a leading nor trailing slash is added. The result
659: * could be concatenated with a slash and the name of a resource, and fed
660: * directly to <code>ClassLoader.getResource()</code>. For it to be fed to
661: * <code>Class.getResource</code> instead, a leading slash would also have
662: * to be prepended to the returned value.
663: * @param clazz the input class. A <code>null</code> value or the default
664: * (empty) package will result in an empty string ("") being returned.
665: * @return a path which represents the package name
666: * @see ClassLoader#getResource
667: * @see Class#getResource
668: */
669: public static String classPackageAsResourcePath(Class clazz) {
670: if (clazz == null) {
671: return "";
672: }
673: String className = clazz.getName();
674: int packageEndIndex = className.lastIndexOf('.');
675: if (packageEndIndex == -1) {
676: return "";
677: }
678: String packageName = className.substring(0, packageEndIndex);
679: return packageName.replace('.', '/');
680: }
681:
682: /**
683: * Build a String that consists of the names of the classes/interfaces
684: * in the given array.
685: * <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
686: * the "class "/"interface " prefix before every class name.
687: * @param classes a Collection of Class objects (may be <code>null</code>)
688: * @return a String of form "[com.foo.Bar, com.foo.Baz]"
689: * @see java.util.AbstractCollection#toString()
690: */
691: public static String classNamesToString(Class[] classes) {
692: return classNamesToString(Arrays.asList(classes));
693: }
694:
695: /**
696: * Build a String that consists of the names of the classes/interfaces
697: * in the given collection.
698: * <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
699: * the "class "/"interface " prefix before every class name.
700: * @param classes a Collection of Class objects (may be <code>null</code>)
701: * @return a String of form "[com.foo.Bar, com.foo.Baz]"
702: * @see java.util.AbstractCollection#toString()
703: */
704: public static String classNamesToString(Collection classes) {
705: if (CollectionUtils.isEmpty(classes)) {
706: return "[]";
707: }
708: StringBuffer sb = new StringBuffer("[");
709: for (Iterator it = classes.iterator(); it.hasNext();) {
710: Class clazz = (Class) it.next();
711: sb.append(clazz.getName());
712: if (it.hasNext()) {
713: sb.append(", ");
714: }
715: }
716: sb.append("]");
717: return sb.toString();
718: }
719:
720: /**
721: * Return all interfaces that the given instance implements as array,
722: * including ones implemented by superclasses.
723: * @param instance the instance to analyse for interfaces
724: * @return all interfaces that the given instance implements as array
725: */
726: public static Class[] getAllInterfaces(Object instance) {
727: Assert.notNull(instance, "Instance must not be null");
728: return getAllInterfacesForClass(instance.getClass());
729: }
730:
731: /**
732: * Return all interfaces that the given class implements as array,
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 analyse for interfaces
736: * @return all interfaces that the given object implements as array
737: */
738: public static Class[] getAllInterfacesForClass(Class clazz) {
739: Assert.notNull(clazz, "Class must not be null");
740: if (clazz.isInterface()) {
741: return new Class[] { clazz };
742: }
743: List interfaces = new ArrayList();
744: while (clazz != null) {
745: for (int i = 0; i < clazz.getInterfaces().length; i++) {
746: Class ifc = clazz.getInterfaces()[i];
747: if (!interfaces.contains(ifc)) {
748: interfaces.add(ifc);
749: }
750: }
751: clazz = clazz.getSuperclass();
752: }
753: return (Class[]) interfaces
754: .toArray(new Class[interfaces.size()]);
755: }
756:
757: /**
758: * Return all interfaces that the given instance implements as Set,
759: * including ones implemented by superclasses.
760: * @param instance the instance to analyse for interfaces
761: * @return all interfaces that the given instance implements as Set
762: */
763: public static Set getAllInterfacesAsSet(Object instance) {
764: Assert.notNull(instance, "Instance must not be null");
765: return getAllInterfacesForClassAsSet(instance.getClass());
766: }
767:
768: /**
769: * Return all interfaces that the given class implements as Set,
770: * including ones implemented by superclasses.
771: * <p>If the class itself is an interface, it gets returned as sole interface.
772: * @param clazz the class to analyse for interfaces
773: * @return all interfaces that the given object implements as Set
774: */
775: public static Set getAllInterfacesForClassAsSet(Class clazz) {
776: Assert.notNull(clazz, "Class must not be null");
777: if (clazz.isInterface()) {
778: return Collections.singleton(clazz);
779: }
780: Set interfaces = new HashSet();
781: while (clazz != null) {
782: for (int i = 0; i < clazz.getInterfaces().length; i++) {
783: Class ifc = clazz.getInterfaces()[i];
784: interfaces.add(ifc);
785: }
786: clazz = clazz.getSuperclass();
787: }
788: return interfaces;
789: }
790:
791: /**
792: * Create a composite interface Class for the given interfaces,
793: * implementing the given interfaces in one single Class.
794: * <p>This implementation builds a JDK proxy class for the given interfaces.
795: * @param interfaces the interfaces to merge
796: * @param classLoader the ClassLoader to create the composite Class in
797: * @return the merged interface as Class
798: * @see java.lang.reflect.Proxy#getProxyClass
799: */
800: public static Class createCompositeInterface(Class[] interfaces,
801: ClassLoader classLoader) {
802: Assert.notEmpty(interfaces, "Interfaces must not be empty");
803: Assert.notNull(classLoader, "ClassLoader must not be null");
804: return Proxy.getProxyClass(classLoader, interfaces);
805: }
806:
807: }
|