com.appleframework.core.utils.ClassUtility.java Source code

Java tutorial

Introduction

Here is the source code for com.appleframework.core.utils.ClassUtility.java

Source

/*
 * Copyright (c) 2002-2012 Alibaba Group Holding Limited.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.appleframework.core.utils;

import static com.appleframework.core.utils.ArrayUtility.*;
import static com.appleframework.core.utils.Assert.*;
import static com.appleframework.core.utils.BasicConstant.*;
import static com.appleframework.core.utils.CollectionUtility.*;
import static com.appleframework.core.utils.StringUtility.*;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Map;
import java.util.Set;

import org.springframework.aop.support.AopUtils;

/**
 *  <code>Class</code> ?
 * <p>
 * ???? <code>null</code> ?
 * <code>NullPointerException</code>
 * </p>
 *
 * @author Michael Zhou
 * @version $Id: ClassUtil.java 509 2004-02-16 05:42:07Z baobao $
 */
public class ClassUtility {

    // ==========================================================================
    // ????package??
    // ==========================================================================

    /**
     * ????
     * <p>
     * <code>object.getClass().getName()</code>???? 
     * </p>
     * <p/>
     * <pre>
     *  int[].class.getName() = "[I"
     *  ClassUtil.getFriendlyClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;"
     *  ClassUtil.getFriendlyClassName(Integer[][].class) = "java.lang.Integer[][]"
     * </pre>
     * <p>
     * ? <code>Class.getName()</code> 
     * </p>
     * <p>
     * ????? <code>Class.forName</code> ?
     * </p>
     *
     * @param object ???
     * @return ???<code>null</code>
     */
    public static String getFriendlyClassNameForObject(Object object) {
        if (object == null) {
            return null;
        }

        String javaClassName = object.getClass().getName();

        return toFriendlyClassName(javaClassName, true, javaClassName);
    }

    /**
     * ????
     * <p>
     * <code>clazz.getName()</code>???? 
     * </p>
     * <p/>
     * <pre>
     *  int[].class.getName() = "[I"
     *  ClassUtil.getFriendlyClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;"
     *  ClassUtil.getFriendlyClassName(Integer[][].class) = "java.lang.Integer[][]"
     * </pre>
     * <p>
     * ? <code>Class.getName()</code> 
     * </p>
     * <p>
     * ????? <code>Class.forName</code> ?
     * </p>
     *
     * @param object ???
     * @return ???<code>null</code>
     */
    public static String getFriendlyClassName(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }

        String javaClassName = clazz.getName();

        return toFriendlyClassName(javaClassName, true, javaClassName);
    }

    /**
     * ????
     * <p>
     * <code>className</code>  <code>clazz.getName()</code>
     * ????? 
     * </p>
     * <p/>
     * <pre>
     *  int[].class.getName() = "[I"
     *  ClassUtil.getFriendlyClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;"
     *  ClassUtil.getFriendlyClassName(Integer[][].class) = "java.lang.Integer[][]"
     * </pre>
     * <p>
     * ? <code>Class.getName()</code> 
     * </p>
     * <p>
     * ????? <code>Class.forName</code> ?
     * </p>
     *
     * @param javaClassName ????
     * @return ????? <code>null</code> ?????
     */
    public static String getFriendlyClassName(String javaClassName) {
        return toFriendlyClassName(javaClassName, true, javaClassName);
    }

    /**
     * Java???????
     *
     * @param javaClassName     Java??
     * @param processInnerClass ?? <code>'$'</code> ?? <code>'.'</code>
     * @return ?????<code>null</code>
     */
    private static String toFriendlyClassName(String javaClassName, boolean processInnerClass,
            String defaultIfInvalid) {
        String name = StringUtility.trimToNull(javaClassName);

        if (name == null) {
            return defaultIfInvalid;
        }

        if (processInnerClass) {
            name = name.replace('$', '.');
        }

        int length = name.length();
        int dimension = 0;

        // ??0
        for (int i = 0; i < length; i++, dimension++) {
            if (name.charAt(i) != '[') {
                break;
            }
        }

        // ?
        if (dimension == 0) {
            return name;
        }

        // ????
        if (length <= dimension) {
            return defaultIfInvalid; // ???
        }

        // ?
        StringBuilder componentTypeName = new StringBuilder();

        switch (name.charAt(dimension)) {
        case 'Z':
            componentTypeName.append("boolean");
            break;

        case 'B':
            componentTypeName.append("byte");
            break;

        case 'C':
            componentTypeName.append("char");
            break;

        case 'D':
            componentTypeName.append("double");
            break;

        case 'F':
            componentTypeName.append("float");
            break;

        case 'I':
            componentTypeName.append("int");
            break;

        case 'J':
            componentTypeName.append("long");
            break;

        case 'S':
            componentTypeName.append("short");
            break;

        case 'L':
            if (name.charAt(length - 1) != ';' || length <= dimension + 2) {
                return defaultIfInvalid; // ???
            }

            componentTypeName.append(name.substring(dimension + 1, length - 1));
            break;

        default:
            return defaultIfInvalid; // ???
        }

        for (int i = 0; i < dimension; i++) {
            componentTypeName.append("[]");
        }

        return componentTypeName.toString();
    }

    /**
     * ?????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassNameForObject(Boolean.TRUE) = "Boolean"
     *  ClassUtil.getSimpleClassNameForObject(new Boolean[10]) = "Boolean[]"
     *  ClassUtil.getSimpleClassNameForObject(new int[1][2]) = "int[][]"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param object ?
     * @return ??? <code>null</code>  <code>null</code>
     */
    public static String getSimpleClassNameForObject(Object object) {
        if (object == null) {
            return null;
        }

        return getSimpleClassName(object.getClass().getName());
    }

    /**
     * ?????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassNameForObject(Boolean.TRUE) = "Boolean"
     *  ClassUtil.getSimpleClassNameForObject(new Boolean[10]) = "Boolean[]"
     *  ClassUtil.getSimpleClassNameForObject(new int[1][2]) = "int[][]"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param object ?
     * @return ??? <code>null</code>  <code>null</code>
     */
    public static String getSimpleClassNameForObject(Object object, boolean processInnerClass) {
        if (object == null) {
            return null;
        }

        return getSimpleClassName(object.getClass().getName(), processInnerClass);
    }

    /**
     * ?????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassName(Boolean.class) = "Boolean"
     *  ClassUtil.getSimpleClassName(Boolean[].class) = "Boolean[]"
     *  ClassUtil.getSimpleClassName(int[][].class) = "int[][]"
     *  ClassUtil.getSimpleClassName(Map.Entry.class) = "Map.Entry"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param clazz ?
     * @return ??? <code>null</code>  <code>null</code>
     */
    public static String getSimpleClassName(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }

        return getSimpleClassName(clazz.getName());
    }

    /**
     * ?????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassName(Boolean.class) = "Boolean"
     *  ClassUtil.getSimpleClassName(Boolean[].class) = "Boolean[]"
     *  ClassUtil.getSimpleClassName(int[][].class) = "int[][]"
     *  ClassUtil.getSimpleClassName(Map.Entry.class) = "Map.Entry"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param clazz ?
     * @return ??? <code>null</code>  <code>null</code>
     */
    public static String getSimpleClassName(Class<?> clazz, boolean proccessInnerClass) {
        if (clazz == null) {
            return null;
        }

        return getSimpleClassName(clazz.getName(), proccessInnerClass);
    }

    /**
     * ????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassName(Boolean.class.getName()) = "Boolean"
     *  ClassUtil.getSimpleClassName(Boolean[].class.getName()) = "Boolean[]"
     *  ClassUtil.getSimpleClassName(int[][].class.getName()) = "int[][]"
     *  ClassUtil.getSimpleClassName(Map.Entry.class.getName()) = "Map.Entry"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param javaClassName ???
     * @return ????? <code>null</code>
     */
    public static String getSimpleClassName(String javaClassName) {
        return getSimpleClassName(javaClassName, true);
    }

    /**
     * ????package??
     * <p>
     * ???? 
     * <p/>
     * <pre>
     *  ClassUtil.getSimpleClassName(Boolean.class.getName()) = "Boolean"
     *  ClassUtil.getSimpleClassName(Boolean[].class.getName()) = "Boolean[]"
     *  ClassUtil.getSimpleClassName(int[][].class.getName()) = "int[][]"
     *  ClassUtil.getSimpleClassName(Map.Entry.class.getName()) = "Map.Entry"
     * </pre>
     * <p>
     * <code>Class.getSimpleName()</code>?inner??
     * </p>
     *
     * @param javaClassName ???
     * @return ????? <code>null</code>
     */
    public static String getSimpleClassName(String javaClassName, boolean proccesInnerClass) {
        String friendlyClassName = toFriendlyClassName(javaClassName, false, null);

        if (friendlyClassName == null) {
            return javaClassName;
        }

        if (proccesInnerClass) {
            char[] chars = friendlyClassName.toCharArray();
            int beginIndex = 0;

            for (int i = chars.length - 1; i >= 0; i--) {
                if (chars[i] == '.') {
                    beginIndex = i + 1;
                    break;
                } else if (chars[i] == '$') {
                    chars[i] = '.';
                }
            }

            return new String(chars, beginIndex, chars.length - beginIndex);
        } else {
            return friendlyClassName.substring(friendlyClassName.lastIndexOf(".") + 1);
        }
    }

    /** ??method?? */
    public static String getSimpleMethodSignature(Method method) {
        return getSimpleMethodSignature(method, false, false, false, false);
    }

    /** ??method?? */
    public static String getSimpleMethodSignature(Method method, boolean withClassName) {
        return getSimpleMethodSignature(method, false, false, withClassName, false);
    }

    /** ??method?? */
    public static String getSimpleMethodSignature(Method method, boolean withModifiers, boolean withReturnType,
            boolean withClassName, boolean withExceptionType) {
        if (method == null) {
            return null;
        }

        StringBuilder buf = new StringBuilder();

        if (withModifiers) {
            buf.append(Modifier.toString(method.getModifiers())).append(' ');
        }

        if (withReturnType) {
            buf.append(getSimpleClassName(method.getReturnType())).append(' ');
        }

        if (withClassName) {
            buf.append(getSimpleClassName(method.getDeclaringClass())).append('.');
        }

        buf.append(method.getName()).append('(');

        Class<?>[] paramTypes = method.getParameterTypes();

        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> paramType = paramTypes[i];

            buf.append(getSimpleClassName(paramType));

            if (i < paramTypes.length - 1) {
                buf.append(", ");
            }
        }

        buf.append(')');

        if (withExceptionType) {
            Class<?>[] exceptionTypes = method.getExceptionTypes();

            if (!isEmptyArray(exceptionTypes)) {
                buf.append(" throws ");

                for (int i = 0; i < exceptionTypes.length; i++) {
                    Class<?> exceptionType = exceptionTypes[i];

                    buf.append(getSimpleClassName(exceptionType));

                    if (i < exceptionTypes.length - 1) {
                        buf.append(", ");
                    }
                }
            }
        }

        return buf.toString();
    }

    /**
     * ?package??
     * <p>
     * package??
     * </p>
     *
     * @param object ?
     * @return package?? <code>null</code> <code>""</code>
     */
    public static String getPackageNameForObject(Object object) {
        if (object == null) {
            return EMPTY_STRING;
        }

        return getPackageName(object.getClass().getName());
    }

    /**
     * ?package??
     * <p>
     * package??
     * </p>
     *
     * @param clazz ?
     * @return package?? <code>null</code> <code>""</code>
     */
    public static String getPackageName(Class<?> clazz) {
        if (clazz == null) {
            return EMPTY_STRING;
        }

        return getPackageName(clazz.getName());
    }

    /**
     * ???package??
     * <p>
     * package??
     * </p>
     *
     * @param javaClassName ???
     * @return package???? <code>null</code>
     */
    public static String getPackageName(String javaClassName) {
        String friendlyClassName = toFriendlyClassName(javaClassName, false, null);

        if (friendlyClassName == null) {
            return EMPTY_STRING;
        }

        int i = friendlyClassName.lastIndexOf('.');

        if (i == -1) {
            return EMPTY_STRING;
        }

        return friendlyClassName.substring(0, i);
    }

    // ==========================================================================
    // ???package??resource??
    //
    // ???package????resource???????
    // java/lang/String.class
    // com/alibaba/commons/lang
    // etc.
    // ==========================================================================

    /**
     * ????
     * <p>
     * 
     * </p>
     * <p/>
     * <pre>
     * ClassUtil.getResourceNameForObjectClass(&quot;This is a string&quot;) = &quot;java/lang/String.class&quot;
     * </pre>
     *
     * @param object ???
     * @return ???<code>null</code>
     */
    public static String getResourceNameForObjectClass(Object object) {
        if (object == null) {
            return null;
        }

        return object.getClass().getName().replace('.', '/') + ".class";
    }

    /**
     * ????
     * <p>
     * 
     * </p>
     * <p/>
     * <pre>
     * ClassUtil.getResourceNameForClass(String.class) = &quot;java/lang/String.class&quot;
     * </pre>
     *
     * @param clazz ???
     * @return ???<code>null</code>
     */
    public static String getResourceNameForClass(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }

        return clazz.getName().replace('.', '/') + ".class";
    }

    /**
     * ????
     * <p>
     * 
     * </p>
     * <p/>
     * <pre>
     * ClassUtil.getResourceNameForClass(&quot;java.lang.String&quot;) = &quot;java/lang/String.class&quot;
     * </pre>
     *
     * @param className ???
     * @return ???????<code>null</code>
     */
    public static String getResourceNameForClass(String className) {
        if (className == null) {
            return null;
        }

        return className.replace('.', '/') + ".class";
    }

    /**
     * ?package?????
     * <p>
     * package??
     * </p>
     *
     * @param object ?
     * @return package?? <code>null</code>  <code>null</code>
     */
    public static String getResourceNameForObjectPackage(Object object) {
        if (object == null) {
            return null;
        }

        return getPackageNameForObject(object).replace('.', '/');
    }

    /**
     * ?package?????
     * <p>
     * package??
     * </p>
     *
     * @param clazz ?
     * @return package?? <code>null</code>  <code>null</code>
     */
    public static String getResourceNameForPackage(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }

        return getPackageName(clazz).replace('.', '/');
    }

    /**
     * ???package?????
     * <p>
     * package??
     * </p>
     *
     * @param className ???
     * @return package???? <code>null</code>
     */
    public static String getResourceNameForPackage(String className) {
        if (className == null) {
            return null;
        }

        return getPackageName(className).replace('.', '/');
    }

    // ==========================================================================
    // ?
    // ==========================================================================

    /**
     * ?.
     *
     * @param componentType 
     * @return  <code>null</code>  <code>null</code>
     */
    public static Class<?> getArrayClass(Class<?> componentType) {
        return getArrayClass(componentType, 1);
    }

    /**
     * ? <code>Array</code>.
     *
     * @param componentType 
     * @param dimension     ? <code>0</code>  <code>0</code>
     * @return 0, , ? <code>null</code> 
     *         <code>null</code>
     */
    public static Class<?> getArrayClass(Class<?> componentClass, int dimension) {
        if (componentClass == null) {
            return null;
        }

        switch (dimension) {
        case 1:
            return Array.newInstance(componentClass, 0).getClass();

        case 0:
            return componentClass;

        default:
            assertTrue(dimension > 0, "wrong dimension: %d", dimension);

            return Array.newInstance(componentClass, new int[dimension]).getClass();
        }
    }

    // ==========================================================================
    // ??wrapper
    // ==========================================================================

    /**
     * ?primitive
     * <p>
     * 
     * <p/>
     * <pre>
     * ClassUtil.getPrimitiveType(&quot;int&quot;) = int.class;
     * ClassUtil.getPrimitiveType(&quot;long&quot;) = long.class;
     * </pre>
     * <p/>
     * </p>
     */
    public static Class<?> getPrimitiveType(String name) {
        PrimitiveInfo<?> info = PRIMITIVES.get(name);

        if (info != null) {
            return info.type;
        }

        return null;
    }

    /**
     * ?primitive
     * <p>
     * 
     * <p/>
     * <pre>
     * ClassUtil.getPrimitiveType(Integer.class) = int.class;
     * ClassUtil.getPrimitiveType(Long.class) = long.class;
     * </pre>
     * <p/>
     * </p>
     */
    public static Class<?> getPrimitiveType(Class<?> type) {
        return getPrimitiveType(type.getName());
    }

    /**
     * ?primitivewrapper?primitive
     * <p>
     * 
     * <p/>
     * <pre>
     * ClassUtil.getPrimitiveWrapperType(int.class) = Integer.class;
     * ClassUtil.getPrimitiveWrapperType(int[].class) = int[].class;
     * ClassUtil.getPrimitiveWrapperType(int[][].class) = int[][].class;
     * ClassUtil.getPrimitiveWrapperType(String[][].class) = String[][].class;
     * </pre>
     * <p/>
     * </p>
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> getWrapperTypeIfPrimitive(Class<T> type) {
        if (type.isPrimitive()) {
            return ((PrimitiveInfo<T>) PRIMITIVES.get(type.getName())).wrapperType;
        }

        return type;
    }

    /**
     * ?primitive?primitive<code>null</code>
     * <p>
     * 
     * <p/>
     * <pre>
     * ClassUtil.getPrimitiveDefaultValue(int.class) = 0;
     * ClassUtil.getPrimitiveDefaultValue(boolean.class) = false;
     * ClassUtil.getPrimitiveDefaultValue(char.class) = '\0';
     * </pre>
     * <p/>
     * </p>
     */
    @SuppressWarnings("unchecked")
    public static <T> T getPrimitiveDefaultValue(Class<T> type) {
        PrimitiveInfo<T> info = (PrimitiveInfo<T>) PRIMITIVES.get(type.getName());

        if (info != null) {
            return info.defaultValue;
        }

        return null;
    }

    private static final Map<String, PrimitiveInfo<?>> PRIMITIVES = createHashMap();

    static {
        addPrimitive(boolean.class, "Z", Boolean.class, "booleanValue", false);
        addPrimitive(short.class, "S", Short.class, "shortValue", (short) 0);
        addPrimitive(int.class, "I", Integer.class, "intValue", 0);
        addPrimitive(long.class, "J", Long.class, "longValue", 0L);
        addPrimitive(float.class, "F", Float.class, "floatValue", 0F);
        addPrimitive(double.class, "D", Double.class, "doubleValue", 0D);
        addPrimitive(char.class, "C", Character.class, "charValue", '\0');
        addPrimitive(byte.class, "B", Byte.class, "byteValue", (byte) 0);
        addPrimitive(void.class, "V", Void.class, null, null);
    }

    private static <T> void addPrimitive(Class<T> type, String typeCode, Class<T> wrapperType, String unwrapMethod,
            T defaultValue) {
        PrimitiveInfo<T> info = new PrimitiveInfo<T>(type, typeCode, wrapperType, unwrapMethod, defaultValue);

        PRIMITIVES.put(type.getName(), info);
        PRIMITIVES.put(wrapperType.getName(), info);
    }

    /** primitive? */
    @SuppressWarnings("unused")
    private static class PrimitiveInfo<T> {
        final Class<T> type;
        final String typeCode;
        final Class<T> wrapperType;
        final String unwrapMethod;
        final T defaultValue;

        public PrimitiveInfo(Class<T> type, String typeCode, Class<T> wrapperType, String unwrapMethod,
                T defaultValue) {
            this.type = type;
            this.typeCode = typeCode;
            this.wrapperType = wrapperType;
            this.unwrapMethod = unwrapMethod;
            this.defaultValue = defaultValue;
        }
    }

    // ==========================================================================
    // ?
    // ==========================================================================

    /**
     *  <code>fromClasses</code> ??? <code>classes</code>
     * <p>
     * ??? <code>object1, object2, ...</code> ????
     * <code>class1, class2,
     * ...</code> 
     * </p>
     * <p>
     *  <code>fromClasses</code> ? <code>fromClass</code> 
     * <code>classes</code> ? <code>clazz</code> 
     * <ol>
     * <li> <code>clazz</code>  <code>null</code>  <code>false</code>
     * </li>
     * <li>? <code>fromClass</code>  <code>null</code> 
     * <code>clazz</code> ?? <code>true</code>  <code>null</code>
     * ?</li>
     * <li> <code>Class.isAssignableFrom</code> ? <code>clazz</code>
     * ?? <code>fromClass</code> ??? <code>true</code></li>
     * <li> <code>clazz</code> ?? <a
     * href="http://java.sun.com/docs/books/jls/">The Java Language
     * Specification</a> sections 5.1.1, 5.1.2, 5.1.4Widening Primitive
     * Conversion? <code>fromClass</code> ???? 
     * <code>clazz</code>  <code>long</code> ?? <code>byte</code>?
     * <code>short</code>?<code>int</code>?<code>long</code>?<code>char</code>
     * ? <code>java.lang.Byte</code>?<code>java.lang.Short</code>?
     * <code>java.lang.Integer</code>? <code>java.lang.Long</code> 
     * <code>java.lang.Character</code> ? <code>true</code></li>
     * <li>?? <code>false</code></li>
     * </ol>
     * </p>
     *
     * @param classes      <code>null</code>  <code>false</code>
     * @param fromClasses ? <code>null</code> ????
     * @return ? <code>true</code>
     */
    public static boolean isAssignable(Class<?>[] classes, Class<?>[] fromClasses) {
        if (!isArraySameLength(fromClasses, classes)) {
            return false;
        }

        if (fromClasses == null) {
            fromClasses = EMPTY_CLASS_ARRAY;
        }

        if (classes == null) {
            classes = EMPTY_CLASS_ARRAY;
        }

        for (int i = 0; i < fromClasses.length; i++) {
            if (isAssignable(classes[i], fromClasses[i]) == false) {
                return false;
            }
        }

        return true;
    }

    /**
     *  <code>fromClass</code> ???? <code>clazz</code>
     * <p>
     * ??? <code>object1, object2, ...</code> ????
     * <code>class1, class2,
     * ...</code> 
     * </p>
     * <p>
     * 
     * <ol>
     * <li> <code>clazz</code>  <code>null</code>  <code>false</code>
     * </li>
     * <li>? <code>fromClass</code>  <code>null</code> 
     * <code>clazz</code> ?? <code>true</code>  <code>null</code>
     * ?</li>
     * <li> <code>Class.isAssignableFrom</code> ? <code>clazz</code>
     * ?? <code>fromClass</code> ??? <code>true</code></li>
     * <li> <code>clazz</code> ?? <a
     * href="http://java.sun.com/docs/books/jls/">The Java Language
     * Specification</a> sections 5.1.1, 5.1.2, 5.1.4Widening Primitive
     * Conversion? <code>fromClass</code> ???? 
     * <code>clazz</code>  <code>long</code> ?? <code>byte</code>?
     * <code>short</code>?<code>int</code>?<code>long</code>?<code>char</code>
     * ? <code>java.lang.Byte</code>?<code>java.lang.Short</code>?
     * <code>java.lang.Integer</code>? <code>java.lang.Long</code> 
     * <code>java.lang.Character</code> ? <code>true</code></li>
     * <li>?? <code>false</code></li>
     * </ol>
     * </p>
     *
     * @param clazz      <code>null</code>  <code>false</code>
     * @param fromClass ? <code>null</code> ????
     * @return ? <code>null</code>
     */
    public static boolean isAssignable(Class<?> clazz, Class<?> fromClass) {
        if (clazz == null) {
            return false;
        }

        // fromClassnull??clazz??int?
        if (fromClass == null) {
            return !clazz.isPrimitive();
        }

        // ???
        if (clazz.isAssignableFrom(fromClass)) {
            return true;
        }

        // ??JLS
        // class?fromClass???
        if (clazz.isPrimitive()) {
            return assignmentTable.get(clazz).contains(fromClass);
        }

        return false;
    }

    private final static Map<Class<?>, Set<Class<?>>> assignmentTable = createHashMap();

    static {
        // boolean??boolean
        assignmentTable.put(boolean.class, assignableSet(boolean.class));

        // byte??byte
        assignmentTable.put(byte.class, assignableSet(byte.class));

        // char??char
        assignmentTable.put(char.class, assignableSet(char.class));

        // short??short, byte
        assignmentTable.put(short.class, assignableSet(short.class, byte.class));

        // int??int?byte?short?char
        assignmentTable.put(int.class, assignableSet(int.class, byte.class, short.class, char.class));

        // long??long?int?byte?short?char
        assignmentTable.put(long.class, assignableSet(long.class, int.class, byte.class, short.class, char.class));

        // float??float, long, int, byte, short, char
        assignmentTable.put(float.class,
                assignableSet(float.class, long.class, int.class, byte.class, short.class, char.class));

        // double??double, float, long, int, byte, short, char
        assignmentTable.put(double.class, assignableSet(double.class, float.class, long.class, int.class,
                byte.class, short.class, char.class));

        assertTrue(assignmentTable.size() == 8);
    }

    private static Set<Class<?>> assignableSet(Class<?>... types) {
        Set<Class<?>> assignableSet = createHashSet();

        for (Class<?> type : types) {
            assignableSet.add(getPrimitiveType(type));
            assignableSet.add(getWrapperTypeIfPrimitive(type));
        }

        return assignableSet;
    }

    // ==========================================================================
    // ?class?
    // ==========================================================================

    /** class loaderclass? */
    public static String locateClass(Class<?> clazz) {
        return locateClass(clazz.getName(), clazz.getClassLoader());
    }

    /** class loaderclass? */
    public static String locateClass(String className) {
        return locateClass(className, null);
    }

    /** class loaderclass? */
    public static String locateClass(String className, ClassLoader loader) {
        className = assertNotNull(trimToNull(className), "className");
        if (loader == null) {
            loader = Thread.currentThread().getContextClassLoader();
        }
        String classFile = className.replace('.', '/') + ".class";
        URL locationURL = loader.getResource(classFile);
        String location = null;
        if (locationURL != null) {
            location = locationURL.toExternalForm();

            if (location.endsWith(classFile)) {
                location = location.substring(0, location.length() - classFile.length());
            }
            location = location.replaceAll("^(jar|zip):|!/$", EMPTY_STRING);
        }
        return location;
    }

    public static Class<?> getGenericTypeFromBean(Object object) {
        Class<?> clazz = object.getClass();

        if (AopUtils.isAopProxy(object)) {
            clazz = AopUtils.getTargetClass(object);
        }
        return getGenericType(clazz);
    }

    public static Class<?> getGenericType(Class<?> clazz) {
        return getGenericType(clazz, 0);
    }

    /**
     * Returns the generic type with the given index from the given
     * {@link Class}. Scan all base classes until finding a generic type.
     * 
     * @param clazz
     *            Class where seeking the generic type
     * @param index
     * @return the generic type
     */
    public static Class<?> getGenericType(Class<?> clazz, int index) {
        Type genericSuperclass = clazz.getGenericSuperclass();
        if (genericSuperclass == null) {
            return null;
        }
        Class<?> effectiveClass = clazz;
        while (!(genericSuperclass instanceof ParameterizedType)) {
            effectiveClass = effectiveClass.getSuperclass();
            genericSuperclass = effectiveClass.getGenericSuperclass();

            if (effectiveClass.equals(Object.class)) {
                return null;
            }
        }
        return (Class<?>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[index];
    }
}