package com.fuint.utils;

import java.lang.reflect.Array;
import java.util.*;

/**
 * 有关 <code>Class</code> 处理的工具类。
 *
 *
 * 这个类中的每个方法都可以“安全”地处理 <code>null</code> ，而不会抛出 <code>NullPointerException</code>。
 *
 *
 * Created by FSQ
 * CopyRight https://www.fuint.cn
 */
public class ClassUtil {
    /* ============================================================================ */
    /* 常量和singleton。                                                            */
    /* ============================================================================ */

    /** 资源文件的分隔符： <code>'/'</code>。 */
    public static final char   RESOURCE_SEPARATOR_CHAR    = '/';

    /** Java类名的分隔符： <code>'.'</code>。 */
    public static final char   PACKAGE_SEPARATOR_CHAR     = '.';

    /** Java类名的分隔符： <code>"."</code>。 */
    public static final String PACKAGE_SEPARATOR          = String.valueOf(PACKAGE_SEPARATOR_CHAR);

    /** 内联类的分隔符： <code>'$'</code>。 */
    public static final char   INNER_CLASS_SEPARATOR_CHAR = '$';

    /** 内联类的分隔符： <code>"$"</code>。 */
    public static final String INNER_CLASS_SEPARATOR      = String
                                                              .valueOf(INNER_CLASS_SEPARATOR_CHAR);

    /** 所有类的信息表，包括父类, 接口, 数组的维数等信息。 */
    private static Map         TYPE_MAP                   = Collections
                                                              .synchronizedMap(new WeakHashMap());

    /* ============================================================================ */
    /* 取得类名和package名的方法。                                                  */
    /* ============================================================================ */

    /**
     * 取得对象所属的类的直观类名。
     *
     * <p>
     * 相当于 <code>object.getClass().getName()</code> ，但不同的是，该方法用更直观的方式显示数组类型。 例如：
     * <pre>
     *  int[].class.getName() = "[I" ClassUtil.getClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;" ClassUtil.getClassName(Integer[][].class) = "java.lang.Integer[][]"
     *  </pre>
     * </p>
     *
     * <p>
     * 对于非数组的类型，该方法等效于 <code>Class.getName()</code> 方法。
     * </p>
     *
     * <p>
     * 注意，该方法所返回的数组类名只能用于显示给人看，不能用于 <code>Class.forName</code> 操作。
     * </p>
     *
     * @param object 要显示类名的对象
     *
     * @return 用于显示的直观类名，如果原类名为空或非法，则返回 <code>null</code>
     */
    public static String getClassNameForObject(Object object) {
        if (object == null) {
            return null;
        }

        return getClassName(object.getClass().getName(), true);
    }

    /**
     * 取得直观的类名。
     *
     * <p>
     * 相当于 <code>clazz.getName()</code> ，但不同的是，该方法用更直观的方式显示数组类型。 例如：
     * <pre>
     *  int[].class.getName() = "[I" ClassUtil.getClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;" ClassUtil.getClassName(Integer[][].class) = "java.lang.Integer[][]"
     *  </pre>
     * </p>
     *
     * <p>
     * 对于非数组的类型，该方法等效于 <code>Class.getName()</code> 方法。
     * </p>
     *
     * <p>
     * 注意，该方法所返回的数组类名只能用于显示给人看，不能用于 <code>Class.forName</code> 操作。
     * </p>
     *
     * @param clazz 要显示类名的类
     *
     * @return 用于显示的直观类名，如果原始类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getClassName(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getClassName(clazz.getName(), true);
    }

    /**
     * 取得直观的类名。
     *
     * <p>
     * <code>className</code> 必须是从 <code>clazz.getName()</code> 所返回的合法类名。该方法用更直观的方式显示数组类型。 例如：
     * <pre>
     *  int[].class.getName() = "[I" ClassUtil.getClassName(int[].class) = "int[]"
     *
     *  Integer[][].class.getName() = "[[Ljava.lang.Integer;" ClassUtil.getClassName(Integer[][].class) = "java.lang.Integer[][]"
     *  </pre>
     * </p>
     *
     * <p>
     * 对于非数组的类型，该方法等效于 <code>Class.getName()</code> 方法。
     * </p>
     *
     * <p>
     * 注意，该方法所返回的数组类名只能用于显示给人看，不能用于 <code>Class.forName</code> 操作。
     * </p>
     *
     * @param className 要显示的类名
     *
     * @return 用于显示的直观类名，如果原类名为 <code>null</code> ，则返回 <code>null</code> ，如果原类名是非法的，则返回原类名
     */
    public static String getClassName(String className) {
        return getClassName(className, true);
    }

    /**
     * 取得直观的类名。
     *
     * @param className 类名
     * @param processInnerClass 是否将内联类分隔符 <code>'$'</code> 转换成 <code>'.'</code>
     *
     * @return 直观的类名，或 <code>null</code>
     */
    private static String getClassName(String className, boolean processInnerClass) {
        if (StringUtil.isEmpty(className)) {
            return className;
        }

        if (processInnerClass) {
            className = className.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
        }

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

        // 取得数组的维数，如果不是数组，维数为0
        for (int i = 0; i < length; i++, dimension++) {
            if (className.charAt(i) != '[') {
                break;
            }
        }

        // 如果不是数组，则直接返回
        if (dimension == 0) {
            return className;
        }

        // 确保类名合法
        if (length <= dimension) {
            return className; // 非法类名
        }

        // 处理数组
        StringBuffer componentTypeName = new StringBuffer();

        switch (className.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 ((className.charAt(length - 1) != ';') || (length <= (dimension + 2))) {
                    return className; // 非法类名
                }

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

            default:
                return className; // 非法类名
        }

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

        return componentTypeName.toString();
    }

    /**
     * 取得指定对象所属的类的短类名，不包括package名。
     *
     * <p>
     * 此方法可以正确显示数组和内联类的名称。
     * </p>
     *
     * <p>
     * 例如：
     * <pre>
     *  ClassUtil.getShortClassNameForObject(Boolean.TRUE) = "Boolean" ClassUtil.getShortClassNameForObject(new Boolean[10]) = "Boolean[]" ClassUtil.getShortClassNameForObject(new int[1][2]) = "int[][]"
     *  </pre>
     * </p>
     *
     * @param object 要查看的对象
     *
     * @return 短类名，如果对象为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getShortClassNameForObject(Object object) {
        if (object == null) {
            return null;
        }

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

    /**
     * 取得短类名，不包括package名。
     *
     * <p>
     * 此方法可以正确显示数组和内联类的名称。
     * </p>
     *
     * <p>
     * 例如：
     * <pre>
     *  ClassUtil.getShortClassName(Boolean.class) = "Boolean" ClassUtil.getShortClassName(Boolean[].class) = "Boolean[]" ClassUtil.getShortClassName(int[][].class) = "int[][]" ClassUtil.getShortClassName(Map.Entry.class) = "Map.Entry"
     *  </pre>
     * </p>
     *
     * @param clazz 要查看的类
     *
     * @return 短类名，如果类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getShortClassName(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getShortClassName(clazz.getName());
    }

    /**
     * 取得类名，不包括package名。
     *
     * <p>
     * 此方法可以正确显示数组和内联类的名称。
     * </p>
     *
     * <p>
     * 例如：
     * <pre>
     *  ClassUtil.getShortClassName(Boolean.class.getName()) = "Boolean" ClassUtil.getShortClassName(Boolean[].class.getName()) = "Boolean[]" ClassUtil.getShortClassName(int[][].class.getName()) = "int[][]" ClassUtil.getShortClassName(Map.Entry.class.getName()) = "Map.Entry"
     *  </pre>
     * </p>
     *
     * @param className 要查看的类名
     *
     * @return 短类名，如果类名为空，则返回 <code>null</code>
     */
    public static String getShortClassName(String className) {
        if (StringUtil.isEmpty(className)) {
            return className;
        }

        // 转换成直观的类名
        className = getClassName(className, false);

        char[] chars = className.toCharArray();
        int lastDot = 0;

        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == PACKAGE_SEPARATOR_CHAR) {
                lastDot = i + 1;
            } else if (chars[i] == INNER_CLASS_SEPARATOR_CHAR) {
                chars[i] = PACKAGE_SEPARATOR_CHAR;
            }
        }

        return new String(chars, lastDot, chars.length - lastDot);
    }

    /**
     * 取得指定对象所属的类的package名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param object 要查看的对象
     *
     * @return package名，如果对象为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getPackageNameForObject(Object object) {
        if (object == null) {
            return null;
        }

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

    /**
     * 取得指定类的package名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param clazz 要查看的类
     *
     * @return package名，如果类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getPackageName(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getPackageName(clazz.getName());
    }

    /**
     * 取得指定类名的package名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param className 要查看的类名
     *
     * @return package名，如果类名为空，则返回 <code>null</code>
     */
    public static String getPackageName(String className) {
        if (StringUtil.isEmpty(className)) {
            return null;
        }

        // 转换成直观的类名
        className = getClassName(className, false);

        int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);

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

        return className.substring(0, i);
    }

    /* ============================================================================ */
    /* 取得类名和package名的resource名的方法。                                      */
    /*                                                                              */
    /* 和类名、package名不同的是，resource名符合文件名命名规范，例如：              */
    /*  java/lang/String.class                                                      */
    /*  com/alibaba/commons/lang                                                    */
    /*  etc.                                                                        */
    /* ============================================================================ */

    /**
     * 取得对象所属的类的资源名。
     *
     * <p>
     * 例如：
     * <pre>
     * ClassUtil.getClassNameForObjectAsResource("This is a string")    = "java/lang/String.class"
     * </pre>
     * </p>
     *
     * @param object 要显示类名的对象
     *
     * @return 指定对象所属类的资源名，如果对象为空，则返回<code>null</code>
     */
    public static String getClassNameForObjectAsResource(Object object) {
        if (object == null) {
            return null;
        }

        return object.getClass().getName().replace(PACKAGE_SEPARATOR_CHAR, RESOURCE_SEPARATOR_CHAR)
               + ".class";
    }

    /**
     * 取得指定类的资源名。
     *
     * <p>
     * 例如：
     * <pre>
     * ClassUtil.getClassNameAsResource(String.class)   = "java/lang/String.class"
     * </pre>
     * </p>
     *
     * @param clazz 要显示类名的类
     *
     * @return 指定类的资源名，如果指定类为空，则返回<code>null</code>
     */
    public static String getClassNameAsResource(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return clazz.getName().replace(PACKAGE_SEPARATOR_CHAR, RESOURCE_SEPARATOR_CHAR) + ".class";
    }

    /**
     * 取得指定类的资源名。
     *
     * <p>
     * 例如：
     * <pre>
     * ClassUtil.getClassNameAsResource("java.lang.String") = "java/lang/String.class"
     * </pre>
     * </p>
     *
     * @param className 要显示的类名
     *
     * @return 指定类名对应的资源名，如果指定类名为空，则返回<code>null</code>
     */
    public static String getClassNameAsResource(String className) {
        if (className == null) {
            return null;
        }

        return className.replace(PACKAGE_SEPARATOR_CHAR, RESOURCE_SEPARATOR_CHAR) + ".class";
    }

    /**
     * 取得指定对象所属的类的package名的资源名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param object 要查看的对象
     *
     * @return package名，如果对象为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getPackageNameForObjectAsResource(Object object) {
        if (object == null) {
            return null;
        }

        return getPackageNameForObject(object).replace(PACKAGE_SEPARATOR_CHAR,
            RESOURCE_SEPARATOR_CHAR);
    }

    /**
     * 取得指定类的package名的资源名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param clazz 要查看的类
     *
     * @return package名，如果类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static String getPackageNameAsResource(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getPackageName(clazz).replace(PACKAGE_SEPARATOR_CHAR, RESOURCE_SEPARATOR_CHAR);
    }

    /**
     * 取得指定类名的package名的资源名。
     *
     * <p>
     * 对于数组，此方法返回的是数组元素类型的package名。
     * </p>
     *
     * @param className 要查看的类名
     *
     * @return package名，如果类名为空，则返回 <code>null</code>
     */
    public static String getPackageNameAsResource(String className) {
        if (className == null) {
            return null;
        }

        return getPackageName(className).replace(PACKAGE_SEPARATOR_CHAR, RESOURCE_SEPARATOR_CHAR);
    }

    /* ============================================================================ */
    /* 取得类的信息，如父类, 接口, 数组的维数等。                                   */
    /* ============================================================================ */

    /**
     * 取得指定维数的 <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 componentType, int dimension) {
        if (dimension <= 0) {
            return componentType;
        }

        if (componentType == null) {
            return null;
        }

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

    /**
     * 取得数组元素的类型。
     *
     * @param type 要查找的类
     *
     * @return 如果是数组, 则返回数组元素的类型, 否则返回 <code>null</code>
     */
    public static Class getArrayComponentType(Class type) {
        if (type == null) {
            return null;
        }

        return getTypeInfo(type).getArrayComponentType();
    }

    /**
     * 取得数组的维数。
     *
     * @param clazz 要查找的类
     *
     * @return 数组的维数. 如果不是数组, 则返回 <code>0</code> ，如果数组为 <code>null</code> ，是返回 <code>-1</code>
     */
    public static int getArrayDimension(Class clazz) {
        if (clazz == null) {
            return -1;
        }

        return getTypeInfo(clazz).getArrayDimension();
    }

    /**
     * 取得指定类的所有父类。
     *
     * <p>
     * 对于一个 <code>Class</code> 实例，如果它不是接口，也不是数组，此方法依次列出从该类的父类开始直到 <code>Object</code> 的所有类。
     * </p>
     *
     * <p>
     * 例如 <code>ClassUtil.getSuperclasses(java.util.ArrayList.class)</code> 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.util.AbstractList</code>
     * </li>
     * <li>
     * <code>java.util.AbstractCollection</code>
     * </li>
     * <li>
     * <code>java.lang.Object</code>
     * </li>
     * </ol>
     * </p>
     *
     * <p>
     * 对于一个接口，此方法返回一个空列表。
     * </p>
     *
     * <p>
     * 例如<code>ClassUtil.getSuperclasses(java.util.List.class)</code>将返回一个空列表。
     * </p>
     *
     * <p>
     * 对于一个数组，此方法返回一个列表，列出所有component类型的父类的相同维数的数组类型。 例如：
     * <code>ClassUtil.getSuperclasses(java.util.ArrayList[][].class)</code> 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.util.AbstractList[][]</code>
     * </li>
     * <li>
     * <code>java.util.AbstractCollection[][]</code>
     * </li>
     * <li>
     * <code>java.lang.Object[][]</code>
     * </li>
     * <li>
     * <code>java.lang.Object[]</code>
     * </li>
     * <li>
     * <code>java.lang.Object</code>
     * </li>
     * </ol>
     *
     * 注意，原子类型及其数组，将被转换成相应的包装类来处理。 例如： <code>ClassUtil.getSuperclasses(int[][].class)</code>
     * 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.lang.Number[][]</code>
     * </li>
     * <li>
     * <code>java.lang.Object[][]</code>
     * </li>
     * <li>
     * <code>java.lang.Object[]</code>
     * </li>
     * <li>
     * <code>java.lang.Object</code>
     * </li>
     * </ol>
     * </p>
     *
     * @param clazz 要查找的类
     *
     * @return 所有父类的列表，如果指定类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static List getSuperclasses(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getTypeInfo(clazz).getSuperclasses();
    }

    /**
     * 取得指定类的所有接口。
     *
     * <p>
     * 对于一个 <code>Class</code> 实例，如果它不是接口，也不是数组，此方法依次列出从该类的父类开始直到 <code>Object</code> 的所有类。
     * </p>
     *
     * <p>
     * 例如 <code>ClassUtil.getInterfaces(java.util.ArrayList.class)</code> 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.util.List</code>
     * </li>
     * <li>
     * <code>java.util.Collection</code>
     * </li>
     * <li>
     * <code>java.util.RandomAccess</code>
     * </li>
     * <li>
     * <code>java.lang.Cloneable</code>
     * </li>
     * <li>
     * <code>java.io.Serializable</code>
     * </li>
     * </ol>
     * </p>
     *
     * <p>
     * 对于一个数组，此方法返回一个列表，列出所有component类型的接口的相同维数的数组类型。 例如：
     * <code>ClassUtil.getInterfaces(java.util.ArrayList[][].class)</code> 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.util.List[][]</code>
     * </li>
     * <li>
     * <code>java.util.Collection[][]</code>
     * </li>
     * <li>
     * <code>java.util.RandomAccess[][]</code>
     * </li>
     * <li>
     * <code>java.lang.Cloneable[][]</code>
     * </li>
     * <li>
     * <code>java.io.Serializable[][]</code>
     * </li>
     * </ol>
     * </p>
     *
     * <p>
     * 注意，原子类型及其数组，将被转换成相应的包装类来处理。 例如： <code>ClassUtil.getInterfaces(int[][].class)</code> 返回以下列表：
     *
     * <ol>
     * <li>
     * <code>java.lang.Comparable[][]</code>
     * </li>
     * <li>
     * <code>java.io.Serializable[][]</code>
     * </li>
     * </ol>
     * </p>
     *
     * @param clazz 要查找的类
     *
     * @return 所有接口的列表，如果指定类为 <code>null</code> ，则返回 <code>null</code>
     */
    public static List getInterfaces(Class clazz) {
        if (clazz == null) {
            return null;
        }

        return getTypeInfo(clazz).getInterfaces();
    }

    /**
     * 判断指定类是否为内联类。
     *
     * @param clazz 要查找的类
     *
     * @return 如果是，则返回 <code>true</code>
     */
    public static boolean isInnerClass(Class clazz) {
        if (clazz == null) {
            return false;
        }

        return StringUtil.contains(clazz.getName(), INNER_CLASS_SEPARATOR_CHAR);
    }

    /**
     * 检查一组指定类型 <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.4定义的Widening 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 (!ArrayUtil.isSameLength(fromClasses, classes)) {
            return false;
        }

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

        if (classes == null) {
            classes = ArrayUtil.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.4定义的Widening 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;
        }

        // 如果fromClass是null，只要clazz不是原子类型如int，就一定可以赋值
        if (fromClass == null) {
            return !clazz.isPrimitive();
        }

        // 如果类相同或有父子关系，当然可以赋值
        if (clazz.isAssignableFrom(fromClass)) {
            return true;
        }

        // 对于原子类型，根据JLS的规则进行扩展
        // 目标class为原子类型时，fromClass可以为原子类型和原子类型的包装类型。
        if (clazz.isPrimitive()) {
            // boolean可以接受：boolean
            if (Boolean.TYPE.equals(clazz)) {
                return Boolean.class.equals(fromClass);
            }

            // byte可以接受：byte
            if (Byte.TYPE.equals(clazz)) {
                return Byte.class.equals(fromClass);
            }

            // char可以接受：char
            if (Character.TYPE.equals(clazz)) {
                return Character.class.equals(fromClass);
            }

            // short可以接受：short, byte
            if (Short.TYPE.equals(clazz)) {
                return Short.class.equals(fromClass) || Byte.TYPE.equals(fromClass)
                       || Byte.class.equals(fromClass);
            }

            // int可以接受：int、byte、short、char
            if (Integer.TYPE.equals(clazz)) {
                return Integer.class.equals(fromClass) || Byte.TYPE.equals(fromClass)
                       || Byte.class.equals(fromClass) || Short.TYPE.equals(fromClass)
                       || Short.class.equals(fromClass) || Character.TYPE.equals(fromClass)
                       || Character.class.equals((fromClass));
            }

            // long可以接受：long、int、byte、short、char
            if (Long.TYPE.equals(clazz)) {
                return Long.class.equals(fromClass) || Integer.TYPE.equals(fromClass)
                       || Integer.class.equals(fromClass) || Byte.TYPE.equals(fromClass)
                       || Byte.class.equals(fromClass) || Short.TYPE.equals(fromClass)
                       || Short.class.equals(fromClass) || Character.TYPE.equals(fromClass)
                       || Character.class.equals((fromClass));
            }

            // float可以接受：float, long, int, byte, short, char
            if (Float.TYPE.equals(clazz)) {
                return Float.class.equals(fromClass) || Long.TYPE.equals(fromClass)
                       || Long.class.equals(fromClass) || Integer.TYPE.equals(fromClass)
                       || Integer.class.equals(fromClass) || Byte.TYPE.equals(fromClass)
                       || Byte.class.equals(fromClass) || Short.TYPE.equals(fromClass)
                       || Short.class.equals(fromClass) || Character.TYPE.equals(fromClass)
                       || Character.class.equals((fromClass));
            }

            // double可以接受：double, float, long, int, byte, short, char
            if (Double.TYPE.equals(clazz)) {
                return Double.class.equals(fromClass) || Float.TYPE.equals(fromClass)
                       || Float.class.equals(fromClass) || Long.TYPE.equals(fromClass)
                       || Long.class.equals(fromClass) || Integer.TYPE.equals(fromClass)
                       || Integer.class.equals(fromClass) || Byte.TYPE.equals(fromClass)
                       || Byte.class.equals(fromClass) || Short.TYPE.equals(fromClass)
                       || Short.class.equals(fromClass) || Character.TYPE.equals(fromClass)
                       || Character.class.equals((fromClass));
            }
        }

        return false;
    }

    /**
     * 取得指定类的 <code>TypeInfo</code>。
     *
     * @param type 指定类或接口
     *
     * @return <code>TypeInfo</code> 对象.
     */
    protected static TypeInfo getTypeInfo(Class type) {
        if (type == null) {
            throw new IllegalArgumentException("Parameter clazz should not be null");
        }

        TypeInfo classInfo;

        synchronized (TYPE_MAP) {
            classInfo = (TypeInfo) TYPE_MAP.get(type);

            if (classInfo == null) {
                classInfo = new TypeInfo(type);
                TYPE_MAP.put(type, classInfo);
            }
        }

        return classInfo;
    }

    /**
     * 代表一个类的信息, 包括父类, 接口, 数组的维数等.
     */
    protected static class TypeInfo {
        private Class type;
        private Class componentType;
        private int   dimension;
        private List  superclasses = new ArrayList(2);
        private List  interfaces   = new ArrayList(2);

        /**
         * 创建 <code>TypeInfo</code>。
         *
         * @param type 创建指定类的 <code>TypeInfo</code>
         */
        private TypeInfo(Class type) {
            this.type = type;

            // 如果是array, 设置componentType和dimension
            Class componentType = null;

            if (type.isArray()) {
                componentType = type;

                do {
                    componentType = componentType.getComponentType();
                    dimension++;
                } while (componentType.isArray());
            }

            this.componentType = componentType;

            // 取得所有superclass
            if (dimension > 0) {
                // 将primitive类型转换成对应的包装类
                componentType = getNonPrimitiveType(componentType);

                Class superComponentType = componentType.getSuperclass();

                // 如果是primitive, interface, 则设置其基类为Object.
                if ((superComponentType == null) && !Object.class.equals(componentType)) {
                    superComponentType = Object.class;
                }

                if (superComponentType != null) {
                    Class superclass = getArrayClass(superComponentType, dimension);

                    superclasses.add(superclass);
                    superclasses.addAll(getTypeInfo(superclass).superclasses);
                } else {
                    for (int i = dimension - 1; i >= 0; i--) {
                        superclasses.add(getArrayClass(Object.class, i));
                    }
                }
            } else {
                // 将primitive类型转换成对应的包装类
                type = getNonPrimitiveType(type);

                Class superclass = type.getSuperclass();

                if (superclass != null) {
                    superclasses.add(superclass);
                    superclasses.addAll(getTypeInfo(superclass).superclasses);
                }
            }

            // 取得所有interface
            if (dimension == 0) {
                Class[] typeInterfaces = type.getInterfaces();
                List set = new ArrayList();

                for (int i = 0; i < typeInterfaces.length; i++) {
                    Class typeInterface = typeInterfaces[i];

                    set.add(typeInterface);
                    set.addAll(getTypeInfo(typeInterface).interfaces);
                }

                for (Iterator i = superclasses.iterator(); i.hasNext();) {
                    Class typeInterface = (Class) i.next();

                    set.addAll(getTypeInfo(typeInterface).interfaces);
                }

                for (Iterator i = set.iterator(); i.hasNext();) {
                    Class interfaceClass = (Class) i.next();

                    if (!interfaces.contains(interfaceClass)) {
                        interfaces.add(interfaceClass);
                    }
                }
            } else {
                for (Iterator i = getTypeInfo(componentType).interfaces.iterator(); i.hasNext();) {
                    Class componentInterface = (Class) i.next();

                    interfaces.add(getArrayClass(componentInterface, dimension));
                }
            }
        }

        /**
         * 将所有的原子类型转换成对应的包装类，其它类型不变。
         *
         * @param type 要转换的类型
         *
         * @return 非原子类型
         */
        private Class getNonPrimitiveType(Class type) {
            if (type.isPrimitive()) {
                if (Integer.TYPE.equals(type)) {
                    type = Integer.class;
                } else if (Long.TYPE.equals(type)) {
                    type = Long.class;
                } else if (Short.TYPE.equals(type)) {
                    type = Short.class;
                } else if (Byte.TYPE.equals(type)) {
                    type = Byte.class;
                } else if (Float.TYPE.equals(type)) {
                    type = Float.class;
                } else if (Double.TYPE.equals(type)) {
                    type = Double.class;
                } else if (Boolean.TYPE.equals(type)) {
                    type = Boolean.class;
                } else if (Character.TYPE.equals(type)) {
                    type = Character.class;
                }
            }

            return type;
        }

        /**
         * 取得 <code>TypeInfo</code> 所代表的java类。
         *
         * @return <code>TypeInfo</code> 所代表的java类
         */
        public Class getType() {
            return type;
        }

        /**
         * 取得数组元素的类型。
         *
         * @return 如果是数组, 则返回数组元素的类型, 否则返回 <code>null</code>
         */
        public Class getArrayComponentType() {
            return componentType;
        }

        /**
         * 取得数组的维数。
         *
         * @return 数组的维数. 如果不是数组, 则返回 <code>0</code>
         */
        public int getArrayDimension() {
            return dimension;
        }

        /**
         * 取得所有的父类。
         *
         * @return 所有的父类
         */
        public List getSuperclasses() {
            return Collections.unmodifiableList(superclasses);
        }

        /**
         * 取得所有的接口。
         *
         * @return 所有的接口
         */
        public List getInterfaces() {
            return Collections.unmodifiableList(interfaces);
        }
    }

    /* ============================================================================ */
    /* 有关primitive类型的方法。                                                    */
    /* ============================================================================ */

    /**
     * 返回指定类型所对应的primitive类型。
     *
     * @param clazz 要检查的类型
     *
     * @return 如果指定类型为<code>null</code>或不是primitive类型的包装类，则返回<code>null</code>，否则返回相应的primitive类型。
     */
    public static Class getPrimitiveType(Class clazz) {
        if (clazz == null) {
            return null;
        }

        if (clazz.isPrimitive()) {
            return clazz;
        }

        if (clazz.equals(Long.class)) {
            return long.class;
        }

        if (clazz.equals(Integer.class)) {
            return int.class;
        }

        if (clazz.equals(Short.class)) {
            return short.class;
        }

        if (clazz.equals(Byte.class)) {
            return byte.class;
        }

        if (clazz.equals(Double.class)) {
            return double.class;
        }

        if (clazz.equals(Float.class)) {
            return float.class;
        }

        if (clazz.equals(Boolean.class)) {
            return boolean.class;
        }

        if (clazz.equals(Character.class)) {
            return char.class;
        }

        return null;
    }

    /**
     * 返回指定类型所对应的非primitive类型。
     *
     * @param clazz 要检查的类型
     *
     * @return 如果指定类型为<code>null</code>，则返回<code>null</code>，如果是primitive类型，则返回相应的包装类，否则返回原始的类型。
     */
    public static Class getNonPrimitiveType(Class clazz) {
        if (clazz == null) {
            return null;
        }

        if (!clazz.isPrimitive()) {
            return clazz;
        }

        if (clazz.equals(long.class)) {
            return Long.class;
        }

        if (clazz.equals(int.class)) {
            return Integer.class;
        }

        if (clazz.equals(short.class)) {
            return Short.class;
        }

        if (clazz.equals(byte.class)) {
            return Byte.class;
        }

        if (clazz.equals(double.class)) {
            return Double.class;
        }

        if (clazz.equals(float.class)) {
            return Float.class;
        }

        if (clazz.equals(boolean.class)) {
            return Boolean.class;
        }

        if (clazz.equals(char.class)) {
            return Character.class;
        }

        return null;
    }
}
