Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » util » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 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.util.ArrayList;
   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.List;
   34   import java.util.Map;
   35   import java.util.Set;
   36   
   37   /**
   38    * Miscellaneous class utility methods. Mainly for internal use within the
   39    * framework; consider Jakarta's Commons Lang for a more comprehensive suite
   40    * of class utilities.
   41    *
   42    * @author Keith Donald
   43    * @author Rob Harrop
   44    * @author Juergen Hoeller
   45    * @since 1.1
   46    * @see TypeUtils
   47    * @see ReflectionUtils
   48    */
   49   public abstract class ClassUtils {
   50   
   51   	/** Suffix for array class names: "[]" */
   52   	public static final String ARRAY_SUFFIX = "[]";
   53   
   54   	/** Prefix for internal array class names: "[L" */
   55   	private static final String INTERNAL_ARRAY_PREFIX = "[L";
   56   
   57   	/** The package separator character '.' */
   58   	private static final char PACKAGE_SEPARATOR = '.';
   59   
   60   	/** The inner class separator character '$' */
   61   	private static final char INNER_CLASS_SEPARATOR = '$';
   62   
   63   	/** The CGLIB class separator character "$$" */
   64   	public static final String CGLIB_CLASS_SEPARATOR = "$$";
   65   
   66   	/** The ".class" file suffix */
   67   	public static final String CLASS_FILE_SUFFIX = ".class";
   68   
   69   
   70   	/**
   71   	 * Map with primitive wrapper type as key and corresponding primitive
   72   	 * type as value, for example: Integer.class -> int.class.
   73   	 */
   74   	private static final Map primitiveWrapperTypeMap = new HashMap(8);
   75   
   76   	/**
   77   	 * Map with primitive type name as key and corresponding primitive
   78   	 * type as value, for example: "int" -> "int.class".
   79   	 */
   80   	private static final Map primitiveTypeNameMap = new HashMap(16);
   81   
   82   
   83   	static {
   84   		primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
   85   		primitiveWrapperTypeMap.put(Byte.class, byte.class);
   86   		primitiveWrapperTypeMap.put(Character.class, char.class);
   87   		primitiveWrapperTypeMap.put(Double.class, double.class);
   88   		primitiveWrapperTypeMap.put(Float.class, float.class);
   89   		primitiveWrapperTypeMap.put(Integer.class, int.class);
   90   		primitiveWrapperTypeMap.put(Long.class, long.class);
   91   		primitiveWrapperTypeMap.put(Short.class, short.class);
   92   
   93   		Set primitiveTypeNames = new HashSet(16);
   94   		primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
   95   		primitiveTypeNames.addAll(Arrays.asList(new Class[] {
   96   				boolean[].class, byte[].class, char[].class, double[].class,
   97   				float[].class, int[].class, long[].class, short[].class}));
   98   		for (Iterator it = primitiveTypeNames.iterator(); it.hasNext();) {
   99   			Class primitiveClass = (Class) it.next();
  100   			primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
  101   		}
  102   	}
  103   
  104   
  105   	/**
  106   	 * Return the default ClassLoader to use: typically the thread context
  107   	 * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
  108   	 * class will be used as fallback.
  109   	 * <p>Call this method if you intend to use the thread context ClassLoader
  110   	 * in a scenario where you absolutely need a non-null ClassLoader reference:
  111   	 * for example, for class path resource loading (but not necessarily for
  112   	 * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
  113   	 * reference as well).
  114   	 * @return the default ClassLoader (never <code>null</code>)
  115   	 * @see java.lang.Thread#getContextClassLoader()
  116   	 */
  117   	public static ClassLoader getDefaultClassLoader() {
  118   		ClassLoader cl = null;
  119   		try {
  120   			cl = Thread.currentThread().getContextClassLoader();
  121   		}
  122   		catch (Throwable ex) {
  123   			// Cannot access thread context ClassLoader - falling back to system class loader...
  124   		}
  125   		if (cl == null) {
  126   			// No thread context class loader -> use class loader of this class.
  127   			cl = ClassUtils.class.getClassLoader();
  128   		}
  129   		return cl;
  130   	}
  131   
  132   	/**
  133   	 * Override the thread context ClassLoader with the environment's bean ClassLoader
  134   	 * if necessary, i.e. if the bean ClassLoader is not equivalent to the thread
  135   	 * context ClassLoader already.
  136   	 * @param classLoaderToUse the actual ClassLoader to use for the thread context
  137   	 * @return the original thread context ClassLoader, or <code>null</code> if not overridden
  138   	 */
  139   	public static ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse) {
  140   		Thread currentThread = Thread.currentThread();
  141   		ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
  142   		if (classLoaderToUse != null && !classLoaderToUse.equals(threadContextClassLoader)) {
  143   			currentThread.setContextClassLoader(classLoaderToUse);
  144   			return threadContextClassLoader;
  145   		}
  146   		else {
  147   			return null;
  148   		}
  149   	}
  150   
  151   	/**
  152   	 * Replacement for <code>Class.forName()</code> that also returns Class instances
  153   	 * for primitives (like "int") and array class names (like "String[]").
  154   	 * <p>Always uses the default class loader: that is, preferably the thread context
  155   	 * class loader, or the ClassLoader that loaded the ClassUtils class as fallback.
  156   	 * @param name the name of the Class
  157   	 * @return Class instance for the supplied name
  158   	 * @throws ClassNotFoundException if the class was not found
  159   	 * @throws LinkageError if the class file could not be loaded
  160   	 * @see Class#forName(String, boolean, ClassLoader)
  161   	 * @see #getDefaultClassLoader()
  162   	 */
  163   	public static Class forName(String name) throws ClassNotFoundException, LinkageError {
  164   		return forName(name, getDefaultClassLoader());
  165   	}
  166   
  167   	/**
  168   	 * Replacement for <code>Class.forName()</code> that also returns Class instances
  169   	 * for primitives (like "int") and array class names (like "String[]").
  170   	 * @param name the name of the Class
  171   	 * @param classLoader the class loader to use
  172   	 * (may be <code>null</code>, which indicates the default class loader)
  173   	 * @return Class instance for the supplied name
  174   	 * @throws ClassNotFoundException if the class was not found
  175   	 * @throws LinkageError if the class file could not be loaded
  176   	 * @see Class#forName(String, boolean, ClassLoader)
  177   	 */
  178   	public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
  179   		Assert.notNull(name, "Name must not be null");
  180   
  181   		Class clazz = resolvePrimitiveClassName(name);
  182   		if (clazz != null) {
  183   			return clazz;
  184   		}
  185   
  186   		// "java.lang.String[]" style arrays
  187   		if (name.endsWith(ARRAY_SUFFIX)) {
  188   			String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
  189   			Class elementClass = forName(elementClassName, classLoader);
  190   			return Array.newInstance(elementClass, 0).getClass();
  191   		}
  192   
  193   		// "[Ljava.lang.String;" style arrays
  194   		int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
  195   		if (internalArrayMarker != -1 && name.endsWith(";")) {
  196   			String elementClassName = null;
  197   			if (internalArrayMarker == 0) {
  198   				elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
  199   			}
  200   			else if (name.startsWith("[")) {
  201   				elementClassName = name.substring(1);
  202   			}
  203   			Class elementClass = forName(elementClassName, classLoader);
  204   			return Array.newInstance(elementClass, 0).getClass();
  205   		}
  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, ClassLoader classLoader) throws IllegalArgumentException {
  229   		try {
  230   			return forName(className, classLoader);
  231   		}
  232   		catch (ClassNotFoundException ex) {
  233   			IllegalArgumentException iae = new IllegalArgumentException("Cannot find class [" + className + "]");
  234   			iae.initCause(ex);
  235   			throw iae;
  236   		}
  237   		catch (LinkageError ex) {
  238   			IllegalArgumentException iae = new IllegalArgumentException(
  239   					"Error loading class [" + className + "]: problem with class file or dependent class.");
  240   			iae.initCause(ex);
  241   			throw iae;
  242   		}
  243   	}
  244   
  245   	/**
  246   	 * Resolve the given class name as primitive class, if appropriate,
  247   	 * according to the JVM's naming rules for primitive classes.
  248   	 * <p>Also supports the JVM's internal class names for primitive arrays.
  249   	 * Does <i>not</i> support the "[]" suffix notation for primitive arrays;
  250   	 * this is only supported by {@link #forName}.
  251   	 * @param name the name of the potentially primitive class
  252   	 * @return the primitive class, or <code>null</code> if the name does not denote
  253   	 * a primitive class or primitive array class
  254   	 */
  255   	public static Class resolvePrimitiveClassName(String name) {
  256   		Class result = null;
  257   		// Most class names will be quite long, considering that they
  258   		// SHOULD sit in a package, so a length check is worthwhile.
  259   		if (name != null && name.length() <= 8) {
  260   			// Could be a primitive - likely.
  261   			result = (Class) primitiveTypeNameMap.get(name);
  262   		}
  263   		return result;
  264   	}
  265   
  266   	/**
  267   	 * Determine whether the {@link Class} identified by the supplied name is present
  268   	 * and can be loaded. Will return <code>false</code> if either the class or
  269   	 * one of its dependencies is not present or cannot be loaded.
  270   	 * @param className the name of the class to check
  271   	 * @return whether the specified class is present
  272   	 * @deprecated as of Spring 2.5, in favor of {@link #isPresent(String, ClassLoader)}
  273   	 */
  274   	public static boolean isPresent(String className) {
  275   		return isPresent(className, getDefaultClassLoader());
  276   	}
  277   
  278   	/**
  279   	 * Determine whether the {@link Class} identified by the supplied name is present
  280   	 * and can be loaded. Will return <code>false</code> if either the class or
  281   	 * one of its dependencies is not present or cannot be loaded.
  282   	 * @param className the name of the class to check
  283   	 * @param classLoader the class loader to use
  284   	 * (may be <code>null</code>, which indicates the default class loader)
  285   	 * @return whether the specified class is present
  286   	 */
  287   	public static boolean isPresent(String className, ClassLoader classLoader) {
  288   		try {
  289   			forName(className, classLoader);
  290   			return true;
  291   		}
  292   		catch (Throwable ex) {
  293   			// Class or one of its dependencies is not present...
  294   			return false;
  295   		}
  296   	}
  297   
  298   	/**
  299   	 * Return the user-defined class for the given instance: usually simply
  300   	 * the class of the given instance, but the original class in case of a
  301   	 * CGLIB-generated subclass.
  302   	 * @param instance the instance to check
  303   	 * @return the user-defined class
  304   	 */
  305   	public static Class getUserClass(Object instance) {
  306   		Assert.notNull(instance, "Instance must not be null");
  307   		return getUserClass(instance.getClass());
  308   	}
  309   
  310   	/**
  311   	 * Return the user-defined class for the given class: usually simply the given
  312   	 * class, but the original class in case of a CGLIB-generated subclass.
  313   	 * @param clazz the class to check
  314   	 * @return the user-defined class
  315   	 */
  316   	public static Class getUserClass(Class clazz) {
  317   		return (clazz != null && clazz.getName().indexOf(CGLIB_CLASS_SEPARATOR) != -1 ?
  318   				clazz.getSuperclass() : clazz);
  319   	}
  320   
  321   	/**
  322   	 * Check whether the given class is cache-safe in the given context,
  323   	 * i.e. whether it is loaded by the given ClassLoader or a parent of it.
  324   	 * @param clazz the class to analyze
  325   	 * @param classLoader the ClassLoader to potentially cache metadata in
  326   	 */
  327   	public static boolean isCacheSafe(Class clazz, ClassLoader classLoader) {
  328   		Assert.notNull(clazz, "Class must not be null");
  329   		ClassLoader target = clazz.getClassLoader();
  330   		if (target == null) {
  331   			return false;
  332   		}
  333   		ClassLoader cur = classLoader;
  334   		if (cur == target) {
  335   			return true;
  336   		}
  337   		while (cur != null) {
  338   			cur = cur.getParent();
  339   			if (cur == target) {
  340   				return true;
  341   			}
  342   		}
  343   		return false;
  344   	}
  345   
  346   
  347   	/**
  348   	 * Get the class name without the qualified package name.
  349   	 * @param className the className to get the short name for
  350   	 * @return the class name of the class without the package name
  351   	 * @throws IllegalArgumentException if the className is empty
  352   	 */
  353   	public static String getShortName(String className) {
  354   		Assert.hasLength(className, "Class name must not be empty");
  355   		int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
  356   		int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
  357   		if (nameEndIndex == -1) {
  358   			nameEndIndex = className.length();
  359   		}
  360   		String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
  361   		shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
  362   		return shortName;
  363   	}
  364   
  365   	/**
  366   	 * Get the class name without the qualified package name.
  367   	 * @param clazz the class to get the short name for
  368   	 * @return the class name of the class without the package name
  369   	 */
  370   	public static String getShortName(Class clazz) {
  371   		return getShortName(getQualifiedName(clazz));
  372   	}
  373   
  374   	/**
  375   	 * Return the short string name of a Java class in decapitalized JavaBeans
  376   	 * property format. Strips the outer class name in case of an inner class.
  377   	 * @param clazz the class
  378   	 * @return the short name rendered in a standard JavaBeans property format
  379   	 * @see java.beans.Introspector#decapitalize(String)
  380   	 */
  381   	public static String getShortNameAsProperty(Class clazz) {
  382   		String shortName = ClassUtils.getShortName(clazz);
  383   		int dotIndex = shortName.lastIndexOf('.');
  384   		shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
  385   		return Introspector.decapitalize(shortName);
  386   	}
  387   
  388   	/**
  389   	 * Determine the name of the class file, relative to the containing
  390   	 * package: e.g. "String.class"
  391   	 * @param clazz the class
  392   	 * @return the file name of the ".class" file
  393   	 */
  394   	public static String getClassFileName(Class clazz) {
  395   		Assert.notNull(clazz, "Class must not be null");
  396   		String className = clazz.getName();
  397   		int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
  398   		return className.substring(lastDotIndex + 1) + CLASS_FILE_SUFFIX;
  399   	}
  400   
  401   	/**
  402   	 * Determine the name of the package of the given class:
  403   	 * e.g. "java.lang" for the <code>java.lang.String</code> class.
  404   	 * @param clazz the class
  405   	 * @return the package name, or the empty String if the class
  406   	 * is defined in the default package
  407   	 */
  408   	public static String getPackageName(Class clazz) {
  409   		Assert.notNull(clazz, "Class must not be null");
  410   		String className = clazz.getName();
  411   		int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
  412   		return (lastDotIndex != -1 ? className.substring(0, lastDotIndex) : "");
  413   	}
  414   
  415   	/**
  416   	 * Return the qualified name of the given class: usually simply
  417   	 * the class name, but component type class name + "[]" for arrays.
  418   	 * @param clazz the class
  419   	 * @return the qualified name of the class
  420   	 */
  421   	public static String getQualifiedName(Class clazz) {
  422   		Assert.notNull(clazz, "Class must not be null");
  423   		if (clazz.isArray()) {
  424   			return getQualifiedNameForArray(clazz);
  425   		}
  426   		else {
  427   			return clazz.getName();
  428   		}
  429   	}
  430   
  431   	/**
  432   	 * Build a nice qualified name for an array:
  433   	 * component type class name + "[]".
  434   	 * @param clazz the array class
  435   	 * @return a qualified name for the array class
  436   	 */
  437   	private static String getQualifiedNameForArray(Class clazz) {
  438   		StringBuffer buffer = new StringBuffer();
  439   		while (clazz.isArray()) {
  440   			clazz = clazz.getComponentType();
  441   			buffer.append(ClassUtils.ARRAY_SUFFIX);
  442   		}
  443   		buffer.insert(0, clazz.getName());
  444   		return buffer.toString();
  445   	}
  446   
  447   	/**
  448   	 * Return the qualified name of the given method, consisting of
  449   	 * fully qualified interface/class name + "." + method name.
  450   	 * @param method the method
  451   	 * @return the qualified name of the method
  452   	 */
  453   	public static String getQualifiedMethodName(Method method) {
  454   		Assert.notNull(method, "Method must not be null");
  455   		return method.getDeclaringClass().getName() + "." + method.getName();
  456   	}
  457   
  458   	/**
  459   	 * Return a descriptive name for the given object's type: usually simply
  460   	 * the class name, but component type class name + "[]" for arrays,
  461   	 * and an appended list of implemented interfaces for JDK proxies.
  462   	 * @param value the value to introspect
  463   	 * @return the qualified name of the class
  464   	 */
  465   	public static String getDescriptiveType(Object value) {
  466   		if (value == null) {
  467   			return null;
  468   		}
  469   		Class clazz = value.getClass();
  470   		if (Proxy.isProxyClass(clazz)) {
  471   			StringBuffer buf = new StringBuffer(clazz.getName());
  472   			buf.append(" implementing ");
  473   			Class[] ifcs = clazz.getInterfaces();
  474   			for (int i = 0; i < ifcs.length; i++) {
  475   				buf.append(ifcs[i].getName());
  476   				if (i < ifcs.length - 1) {
  477   					buf.append(',');
  478   				}
  479   			}
  480   			return buf.toString();
  481   		}
  482   		else if (clazz.isArray()) {
  483   			return getQualifiedNameForArray(clazz);
  484   		}
  485   		else {
  486   			return clazz.getName();
  487   		}
  488   	}
  489   
  490   
  491   	/**
  492   	 * Determine whether the given class has a constructor with the given signature.
  493   	 * <p>Essentially translates <code>NoSuchMethodException</code> to "false".
  494   	 * @param clazz	the clazz to analyze
  495   	 * @param paramTypes the parameter types of the method
  496   	 * @return whether the class has a corresponding constructor
  497   	 * @see java.lang.Class#getMethod
  498   	 */
  499   	public static boolean hasConstructor(Class clazz, Class[] paramTypes) {
  500   		return (getConstructorIfAvailable(clazz, paramTypes) != null);
  501   	}
  502   
  503   	/**
  504   	 * Determine whether the given class has a constructor with the given signature,
  505   	 * and return it if available (else return <code>null</code>).
  506   	 * <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
  507   	 * @param clazz	the clazz to analyze
  508   	 * @param paramTypes the parameter types of the method
  509   	 * @return the constructor, or <code>null</code> if not found
  510   	 * @see java.lang.Class#getConstructor
  511   	 */
  512   	public static Constructor getConstructorIfAvailable(Class clazz, Class[] paramTypes) {
  513   		Assert.notNull(clazz, "Class must not be null");
  514   		try {
  515   			return clazz.getConstructor(paramTypes);
  516   		}
  517   		catch (NoSuchMethodException ex) {
  518   			return null;
  519   		}
  520   	}
  521   
  522   	/**
  523   	 * Determine whether the given class has a method with the given signature.
  524   	 * <p>Essentially translates <code>NoSuchMethodException</code> to "false".
  525   	 * @param clazz	the clazz to analyze
  526   	 * @param methodName the name of the method
  527   	 * @param paramTypes the parameter types of the method
  528   	 * @return whether the class has a corresponding method
  529   	 * @see java.lang.Class#getMethod
  530   	 */
  531   	public static boolean hasMethod(Class clazz, String methodName, Class[] paramTypes) {
  532   		return (getMethodIfAvailable(clazz, methodName, paramTypes) != null);
  533   	}
  534   
  535   	/**
  536   	 * Determine whether the given class has a method with the given signature,
  537   	 * and return it if available (else return <code>null</code>).
  538   	 * <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
  539   	 * @param clazz	the clazz to analyze
  540   	 * @param methodName the name of the method
  541   	 * @param paramTypes the parameter types of the method
  542   	 * @return the method, or <code>null</code> if not found
  543   	 * @see java.lang.Class#getMethod
  544   	 */
  545   	public static Method getMethodIfAvailable(Class clazz, String methodName, Class[] paramTypes) {
  546   		Assert.notNull(clazz, "Class must not be null");
  547   		Assert.notNull(methodName, "Method name must not be null");
  548   		try {
  549   			return clazz.getMethod(methodName, paramTypes);
  550   		}
  551   		catch (NoSuchMethodException ex) {
  552   			return null;
  553   		}
  554   	}
  555   
  556   	/**
  557   	 * Return the number of methods with a given name (with any argument types),
  558   	 * for the given class and/or its superclasses. Includes non-public methods.
  559   	 * @param clazz	the clazz to check
  560   	 * @param methodName the name of the method
  561   	 * @return the number of methods with the given name
  562   	 */
  563   	public static int getMethodCountForName(Class clazz, String methodName) {
  564   		Assert.notNull(clazz, "Class must not be null");
  565   		Assert.notNull(methodName, "Method name must not be null");
  566   		int count = 0;
  567   		Method[] declaredMethods = clazz.getDeclaredMethods();
  568   		for (int i = 0; i < declaredMethods.length; i++) {
  569   			Method method = declaredMethods[i];
  570   			if (methodName.equals(method.getName())) {
  571   				count++;
  572   			}
  573   		}
  574   		Class[] ifcs = clazz.getInterfaces();
  575   		for (int i = 0; i < ifcs.length; i++) {
  576   			count += getMethodCountForName(ifcs[i], methodName);
  577   		}
  578   		if (clazz.getSuperclass() != null) {
  579   			count += getMethodCountForName(clazz.getSuperclass(), methodName);
  580   		}
  581   		return count;
  582   	}
  583   
  584   	/**
  585   	 * Does the given class and/or its superclasses at least have one or more
  586   	 * methods (with any argument types)? Includes non-public methods.
  587   	 * @param clazz	the clazz to check
  588   	 * @param methodName the name of the method
  589   	 * @return whether there is at least one method with the given name
  590   	 */
  591   	public static boolean hasAtLeastOneMethodWithName(Class clazz, String methodName) {
  592   		Assert.notNull(clazz, "Class must not be null");
  593   		Assert.notNull(methodName, "Method name must not be null");
  594   		Method[] declaredMethods = clazz.getDeclaredMethods();
  595   		for (int i = 0; i < declaredMethods.length; i++) {
  596   			Method method = declaredMethods[i];
  597   			if (method.getName().equals(methodName)) {
  598   				return true;
  599   			}
  600   		}
  601   		Class[] ifcs = clazz.getInterfaces();
  602   		for (int i = 0; i < ifcs.length; i++) {
  603   			if (hasAtLeastOneMethodWithName(ifcs[i], methodName)) {
  604   				return true;
  605   			}
  606   		}
  607   		return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
  608   	}
  609   
  610   	/**
  611   	 * Given a method, which may come from an interface, and a target class used
  612   	 * in the current reflective invocation, find the corresponding target method
  613   	 * if there is one. E.g. the method may be <code>IFoo.bar()</code> and the
  614   	 * target class may be <code>DefaultFoo</code>. In this case, the method may be
  615   	 * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
  616   	 * <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod},
  617   	 * this method does <i>not</i> resolve Java 5 bridge methods automatically.
  618   	 * Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
  619   	 * if bridge method resolution is desirable (e.g. for obtaining metadata from
  620   	 * the original method definition).
  621   	 * @param method the method to be invoked, which may come from an interface
  622   	 * @param targetClass the target class for the current invocation.
  623   	 * May be <code>null</code> or may not even implement the method.
  624   	 * @return the specific target method, or the original method if the
  625   	 * <code>targetClass</code> doesn't implement it or is <code>null</code>
  626   	 * @see org.springframework.aop.support.AopUtils#getMostSpecificMethod
  627   	 */
  628   	public static Method getMostSpecificMethod(Method method, Class targetClass) {
  629   		if (method != null && targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
  630   			try {
  631   				method = targetClass.getMethod(method.getName(), method.getParameterTypes());
  632   			}
  633   			catch (NoSuchMethodException ex) {
  634   				// Perhaps the target class doesn't implement this method:
  635   				// that's fine, just use the original method.
  636   			}
  637   		}
  638   		return method;
  639   	}
  640   
  641   	/**
  642   	 * Return a static method of a class.
  643   	 * @param methodName the static method name
  644   	 * @param clazz	the class which defines the method
  645   	 * @param args the parameter types to the method
  646   	 * @return the static method, or <code>null</code> if no static method was found
  647   	 * @throws IllegalArgumentException if the method name is blank or the clazz is null
  648   	 */
  649   	public static Method getStaticMethod(Class clazz, String methodName, Class[] args) {
  650   		Assert.notNull(clazz, "Class must not be null");
  651   		Assert.notNull(methodName, "Method name must not be null");
  652   		try {
  653   			Method method = clazz.getDeclaredMethod(methodName, args);
  654   			if ((method.getModifiers() & Modifier.STATIC) != 0) {
  655   				return method;
  656   			}
  657   		}
  658   		catch (NoSuchMethodException ex) {
  659   		}
  660   		return null;
  661   	}
  662   
  663   
  664   	/**
  665   	 * Check if the given class represents a primitive wrapper,
  666   	 * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
  667   	 * @param clazz the class to check
  668   	 * @return whether the given class is a primitive wrapper class
  669   	 */
  670   	public static boolean isPrimitiveWrapper(Class clazz) {
  671   		Assert.notNull(clazz, "Class must not be null");
  672   		return primitiveWrapperTypeMap.containsKey(clazz);
  673   	}
  674   
  675   	/**
  676   	 * Check if the given class represents a primitive (i.e. boolean, byte,
  677   	 * char, short, int, long, float, or double) or a primitive wrapper
  678   	 * (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double).
  679   	 * @param clazz the class to check
  680   	 * @return whether the given class is a primitive or primitive wrapper class
  681   	 */
  682   	public static boolean isPrimitiveOrWrapper(Class clazz) {
  683   		Assert.notNull(clazz, "Class must not be null");
  684   		return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
  685   	}
  686   
  687   	/**
  688   	 * Check if the given class represents an array of primitives,
  689   	 * i.e. boolean, byte, char, short, int, long, float, or double.
  690   	 * @param clazz the class to check
  691   	 * @return whether the given class is a primitive array class
  692   	 */
  693   	public static boolean isPrimitiveArray(Class clazz) {
  694   		Assert.notNull(clazz, "Class must not be null");
  695   		return (clazz.isArray() && clazz.getComponentType().isPrimitive());
  696   	}
  697   
  698   	/**
  699   	 * Check if the given class represents an array of primitive wrappers,
  700   	 * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
  701   	 * @param clazz the class to check
  702   	 * @return whether the given class is a primitive wrapper array class
  703   	 */
  704   	public static boolean isPrimitiveWrapperArray(Class clazz) {
  705   		Assert.notNull(clazz, "Class must not be null");
  706   		return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType()));
  707   	}
  708   
  709   	/**
  710   	 * Check if the right-hand side type may be assigned to the left-hand side
  711   	 * type, assuming setting by reflection. Considers primitive wrapper
  712   	 * classes as assignable to the corresponding primitive types.
  713   	 * @param lhsType the target type
  714   	 * @param rhsType	the value type that should be assigned to the target type
  715   	 * @return if the target type is assignable from the value type
  716   	 * @see TypeUtils#isAssignable
  717   	 */
  718   	public static boolean isAssignable(Class lhsType, Class rhsType) {
  719   		Assert.notNull(lhsType, "Left-hand side type must not be null");
  720   		Assert.notNull(rhsType, "Right-hand side type must not be null");
  721   		return (lhsType.isAssignableFrom(rhsType) ||
  722   				lhsType.equals(primitiveWrapperTypeMap.get(rhsType)));
  723   	}
  724   
  725   	/**
  726   	 * Determine if the given type is assignable from the given value,
  727   	 * assuming setting by reflection. Considers primitive wrapper classes
  728   	 * as assignable to the corresponding primitive types.
  729   	 * @param type	the target type
  730   	 * @param value the value that should be assigned to the type
  731   	 * @return if the type is assignable from the value
  732   	 */
  733   	public static boolean isAssignableValue(Class type, Object value) {
  734   		Assert.notNull(type, "Type must not be null");
  735   		return (value != null ? isAssignable(type, value.getClass()) : !type.isPrimitive());
  736   	}
  737   
  738   
  739   	/**
  740   	 * Convert a "/"-based resource path to a "."-based fully qualified class name.
  741   	 * @param resourcePath the resource path pointing to a class
  742   	 * @return the corresponding fully qualified class name
  743   	 */
  744   	public static String convertResourcePathToClassName(String resourcePath) {
  745   		return resourcePath.replace('/', '.');
  746   	}
  747   
  748   	/**
  749   	 * Convert a "."-based fully qualified class name to a "/"-based resource path.
  750   	 * @param className the fully qualified class name
  751   	 * @return the corresponding resource path, pointing to the class
  752   	 */
  753   	public static String convertClassNameToResourcePath(String className) {
  754   		return className.replace('.', '/');
  755   	}
  756   
  757   	/**
  758   	 * Return a path suitable for use with <code>ClassLoader.getResource</code>
  759   	 * (also suitable for use with <code>Class.getResource</code> by prepending a
  760   	 * slash ('/') to the return value. Built by taking the package of the specified
  761   	 * class file, converting all dots ('.') to slashes ('/'), adding a trailing slash
  762   	 * if necesssary, and concatenating the specified resource name to this.
  763   	 * <br/>As such, this function may be used to build a path suitable for
  764   	 * loading a resource file that is in the same package as a class file,
  765   	 * although {@link org.springframework.core.io.ClassPathResource} is usually
  766   	 * even more convenient.
  767   	 * @param clazz	the Class whose package will be used as the base
  768   	 * @param resourceName the resource name to append. A leading slash is optional.
  769   	 * @return the built-up resource path
  770   	 * @see java.lang.ClassLoader#getResource
  771   	 * @see java.lang.Class#getResource
  772   	 */
  773   	public static String addResourcePathToPackagePath(Class clazz, String resourceName) {
  774   		Assert.notNull(resourceName, "Resource name must not be null");
  775   		if (!resourceName.startsWith("/")) {
  776   			return classPackageAsResourcePath(clazz) + "/" + resourceName;
  777   		}
  778   		return classPackageAsResourcePath(clazz) + resourceName;
  779   	}
  780   
  781   	/**
  782   	 * Given an input class object, return a string which consists of the
  783   	 * class's package name as a pathname, i.e., all dots ('.') are replaced by
  784   	 * slashes ('/'). Neither a leading nor trailing slash is added. The result
  785   	 * could be concatenated with a slash and the name of a resource, and fed
  786   	 * directly to <code>ClassLoader.getResource()</code>. For it to be fed to
  787   	 * <code>Class.getResource</code> instead, a leading slash would also have
  788   	 * to be prepended to the returned value.
  789   	 * @param clazz the input class. A <code>null</code> value or the default
  790   	 * (empty) package will result in an empty string ("") being returned.
  791   	 * @return a path which represents the package name
  792   	 * @see ClassLoader#getResource
  793   	 * @see Class#getResource
  794   	 */
  795   	public static String classPackageAsResourcePath(Class clazz) {
  796   		if (clazz == null) {
  797   			return "";
  798   		}
  799   		String className = clazz.getName();
  800   		int packageEndIndex = className.lastIndexOf('.');
  801   		if (packageEndIndex == -1) {
  802   			return "";
  803   		}
  804   		String packageName = className.substring(0, packageEndIndex);
  805   		return packageName.replace('.', '/');
  806   	}
  807   
  808   	/**
  809   	 * Build a String that consists of the names of the classes/interfaces
  810   	 * in the given array.
  811   	 * <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
  812   	 * the "class "/"interface " prefix before every class name.
  813   	 * @param classes a Collection of Class objects (may be <code>null</code>)
  814   	 * @return a String of form "[com.foo.Bar, com.foo.Baz]"
  815   	 * @see java.util.AbstractCollection#toString()
  816   	 */
  817   	public static String classNamesToString(Class[] classes) {
  818   		return classNamesToString(Arrays.asList(classes));
  819   	}
  820   
  821   	/**
  822   	 * Build a String that consists of the names of the classes/interfaces
  823   	 * in the given collection.
  824   	 * <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
  825   	 * the "class "/"interface " prefix before every class name.
  826   	 * @param classes a Collection of Class objects (may be <code>null</code>)
  827   	 * @return a String of form "[com.foo.Bar, com.foo.Baz]"
  828   	 * @see java.util.AbstractCollection#toString()
  829   	 */
  830   	public static String classNamesToString(Collection classes) {
  831   		if (CollectionUtils.isEmpty(classes)) {
  832   			return "[]";
  833   		}
  834   		StringBuffer sb = new StringBuffer("[");
  835   		for (Iterator it = classes.iterator(); it.hasNext(); ) {
  836   			Class clazz = (Class) it.next();
  837   			sb.append(clazz.getName());
  838   			if (it.hasNext()) {
  839   				sb.append(", ");
  840   			}
  841   		}
  842   		sb.append("]");
  843   		return sb.toString();
  844   	}
  845   
  846   
  847   	/**
  848   	 * Return all interfaces that the given instance implements as array,
  849   	 * including ones implemented by superclasses.
  850   	 * @param instance the instance to analyse for interfaces
  851   	 * @return all interfaces that the given instance implements as array
  852   	 */
  853   	public static Class[] getAllInterfaces(Object instance) {
  854   		Assert.notNull(instance, "Instance must not be null");
  855   		return getAllInterfacesForClass(instance.getClass());
  856   	}
  857   
  858   	/**
  859   	 * Return all interfaces that the given class implements as array,
  860   	 * including ones implemented by superclasses.
  861   	 * <p>If the class itself is an interface, it gets returned as sole interface.
  862   	 * @param clazz the class to analyse for interfaces
  863   	 * @return all interfaces that the given object implements as array
  864   	 */
  865   	public static Class[] getAllInterfacesForClass(Class clazz) {
  866   		return getAllInterfacesForClass(clazz, null);
  867   	}
  868   
  869   	/**
  870   	 * Return all interfaces that the given class implements as array,
  871   	 * including ones implemented by superclasses.
  872   	 * <p>If the class itself is an interface, it gets returned as sole interface.
  873   	 * @param clazz the class to analyse for interfaces
  874   	 * @param classLoader the ClassLoader that the interfaces need to be visible in
  875   	 * (may be <code>null</code> when accepting all declared interfaces)
  876   	 * @return all interfaces that the given object implements as array
  877   	 */
  878   	public static Class[] getAllInterfacesForClass(Class clazz, ClassLoader classLoader) {
  879   		Assert.notNull(clazz, "Class must not be null");
  880   		if (clazz.isInterface()) {
  881   			return new Class[] {clazz};
  882   		}
  883   		List interfaces = new ArrayList();
  884   		while (clazz != null) {
  885   			for (int i = 0; i < clazz.getInterfaces().length; i++) {
  886   				Class ifc = clazz.getInterfaces()[i];
  887   				if (!interfaces.contains(ifc) &&
  888   						(classLoader == null || isVisible(ifc, classLoader))) {
  889   					interfaces.add(ifc);
  890   				}
  891   			}
  892   			clazz = clazz.getSuperclass();
  893   		}
  894   		return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
  895   	}
  896   
  897   	/**
  898   	 * Return all interfaces that the given instance implements as Set,
  899   	 * including ones implemented by superclasses.
  900   	 * @param instance the instance to analyse for interfaces
  901   	 * @return all interfaces that the given instance implements as Set
  902   	 */
  903   	public static Set getAllInterfacesAsSet(Object instance) {
  904   		Assert.notNull(instance, "Instance must not be null");
  905   		return getAllInterfacesForClassAsSet(instance.getClass());
  906   	}
  907   
  908   	/**
  909   	 * Return all interfaces that the given class implements as Set,
  910   	 * including ones implemented by superclasses.
  911   	 * <p>If the class itself is an interface, it gets returned as sole interface.
  912   	 * @param clazz the class to analyse for interfaces
  913   	 * @return all interfaces that the given object implements as Set
  914   	 */
  915   	public static Set getAllInterfacesForClassAsSet(Class clazz) {
  916   		return getAllInterfacesForClassAsSet(clazz, null);
  917   	}
  918   
  919   	/**
  920   	 * Return all interfaces that the given class implements as Set,
  921   	 * including ones implemented by superclasses.
  922   	 * <p>If the class itself is an interface, it gets returned as sole interface.
  923   	 * @param clazz the class to analyse for interfaces
  924   	 * @param classLoader the ClassLoader that the interfaces need to be visible in
  925   	 * (may be <code>null</code> when accepting all declared interfaces)
  926   	 * @return all interfaces that the given object implements as Set
  927   	 */
  928   	public static Set getAllInterfacesForClassAsSet(Class clazz, ClassLoader classLoader) {
  929   		Assert.notNull(clazz, "Class must not be null");
  930   		if (clazz.isInterface()) {
  931   			return Collections.singleton(clazz);
  932   		}
  933   		Set interfaces = new LinkedHashSet();
  934   		while (clazz != null) {
  935   			for (int i = 0; i < clazz.getInterfaces().length; i++) {
  936   				Class ifc = clazz.getInterfaces()[i];
  937   				if (classLoader == null || isVisible(ifc, classLoader)) {
  938   					interfaces.add(ifc);
  939   				}
  940   			}
  941   			clazz = clazz.getSuperclass();
  942   		}
  943   		return interfaces;
  944   	}
  945   
  946   	/**
  947   	 * Create a composite interface Class for the given interfaces,
  948   	 * implementing the given interfaces in one single Class.
  949   	 * <p>This implementation builds a JDK proxy class for the given interfaces.
  950   	 * @param interfaces the interfaces to merge
  951   	 * @param classLoader the ClassLoader to create the composite Class in
  952   	 * @return the merged interface as Class
  953   	 * @see java.lang.reflect.Proxy#getProxyClass
  954   	 */
  955   	public static Class createCompositeInterface(Class[] interfaces, ClassLoader classLoader) {
  956   		Assert.notEmpty(interfaces, "Interfaces must not be empty");
  957   		Assert.notNull(classLoader, "ClassLoader must not be null");
  958   		return Proxy.getProxyClass(classLoader, interfaces);
  959   	}
  960   
  961   	/**
  962   	 * Check whether the given class is visible in the given ClassLoader.
  963   	 * @param clazz the class to check (typically an interface)
  964   	 * @param classLoader the ClassLoader to check against (may be <code>null</code>,
  965   	 * in which case this method will always return <code>true</code>)
  966   	 */
  967   	public static boolean isVisible(Class clazz, ClassLoader classLoader) {
  968   		if (classLoader == null) {
  969   			return true;
  970   		}
  971   		try {
  972   			Class actualClass = classLoader.loadClass(clazz.getName());
  973   			return (clazz == actualClass);
  974   			// Else: different interface class found...
  975   		}
  976   		catch (ClassNotFoundException ex) {
  977   			// No interface class found...
  978   			return false;
  979   		}
  980   	}
  981   
  982   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » util » [javadoc | source]