zcommon/src/main/java/org/zkoss/lang/Classes.java

Summary

Maintainability
F
2 wks
Test Coverage
/* Classes.java


    Purpose: Utilities to handle Class
    Description:
    History:
     2001/4/19, Tom M. Yeh: Created.


Copyright (C) 2001 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
    This program is distributed under LGPL Version 2.1 in the hope that
    it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.lang;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.math.BigDecimals;
import org.zkoss.math.BigIntegers;
import org.zkoss.mesg.MCommon;
import org.zkoss.mesg.Messages;
import org.zkoss.util.Cache;
import org.zkoss.util.FastReadCache;
import org.zkoss.util.IllegalSyntaxException;

/**
 * Utilities to handle java.lang.Class
 *
 * @author tomyeh
 */
public class Classes {
    private static final Logger log = LoggerFactory.getLogger(Classes.class);

    private static final Object NOT_FOUND = new Object();

    private static String _contextClassLoaderName = null;

    /**
     * Instantiates a new instance of the specified class with
     * the specified arguments and argument types.
     *
     * <p>Note only public constructors are searched.
     *
     * @param cls the class of the instance to create
     * @param argTypes the argument types of the constructor to invoke
     * @param args the arguments to initialize the instance
     * @return the new instance
     *
     * @exception NoSuchMethodException if a matching method is not found
     * @exception InstantiationException if the class that declares the
     * underlying constructor represents an abstract class
     * @exception InvocationTargetException if the underlying constructor
     * throws an exception
     * @see #newInstance(String, Class[], Object[])
     */
    public static final
    Object newInstance(Class<?> cls, Class<?>[] argTypes, Object[] args)
    throws NoSuchMethodException, InstantiationException,
    InvocationTargetException, IllegalAccessException {
         return cls.getConstructor(argTypes).newInstance(args);
    }
    /**
     * Instantiates a new instance of the specified class with the
     * specified argument.
     *
     * <p>It searches all constructors for the first one that matches
     * the specified arguments.
     * @since 3.0.1
     */
    public static final
    Object newInstance(Class<?> cls, Object[] args)
    throws NoSuchMethodException, InstantiationException,
    InvocationTargetException, IllegalAccessException {
        if (args == null || args.length == 0)
            return cls.newInstance();

        final Constructor[] cxs = cls.getConstructors();
        Constructor cx = match(cxs, args, false);
        if (cx == null)
            cx = match(cxs, args, true);
        if (cx != null)
            return cx.newInstance(args);

        throw new NoSuchMethodException(cls.getName()+": no constructor for "+Objects.toString(args));
    }
    /**
     * @param loosely whether to match Long with Integer and so on.
     */
    private static
    Constructor match(Constructor[] cxs, Object[] args, boolean loosely) {
        for (int j = 0; j < cxs.length; ++j)
            if (matched(cxs[j].getParameterTypes(), args, loosely))
                return cxs[j];
        return null;
    }
    /**
     * @param loosely whether to match Long with Integer and so on.
     */
    private static
    Method match(Class<?> cls, String name, Object[] args, boolean loosely) {
        final Method[] ms = cls.getMethods();
        for (int j = 0; j < ms.length; ++j) {
            if (!ms[j].getName().equals(name)
            || !matched(ms[j].getParameterTypes(), args, loosely))
                continue; //not found; next

            if (Modifier.isPublic(ms[j].getDeclaringClass().getModifiers()))
                return ms[j]; //found
            try {
                return getMethodInPublic(
                    cls, ms[j].getName(), ms[j].getParameterTypes());
            } catch (NoSuchMethodException ex) { //not found; next
            }
        }
        return null;
    }
    private static boolean matched(Class[] types, Object[] args, boolean loosely) {
        if (types.length == args.length) {
            final Object[] argcvt = loosely ? new Object[args.length]: args;
            boolean cvted = false;
            for (int k = args.length;;) {
                if (--k < 0) {
                    if (cvted)
                        System.arraycopy(argcvt, 0, args, 0, args.length);
                        //copy converted only if all matched
                    return true;
                }

                final Object arg = argcvt[k] = args[k];
                final Class type = types[k];
                if (arg == null)
                    if (type.isPrimitive()) break; //mismatch
                    else continue; //matched

                if (type.isInstance(arg) || (type.isPrimitive()
                && Primitives.toWrapper(type).isInstance(arg)))
                    continue; //matched

                if (loosely) {
                    argcvt[k] = looselyCast(type, arg);
                    if (argcvt[k] != null) {
                        cvted = true;
                        continue; //matched
                    }
                }
                break; //mismatch
            }
        }
        return false;
    }
    private static Object looselyCast(Class type, Object arg) {
        if (type == Integer.class || type == int.class) {
            if (arg instanceof Number)
                return ((Number) arg).intValue();
        } else if (type == Long.class || type == long.class) {
            if (arg instanceof Number)
                return ((Number) arg).longValue();
        } else if (type == Double.class || type == double.class) {
            if (arg instanceof Number)
                return ((Number) arg).doubleValue();
        } else if (type == Short.class || type == short.class) {
            if (arg instanceof Number)
                return ((Number) arg).shortValue();
        } else if (type == Float.class || type == float.class) {
            if (arg instanceof Number)
                return ((Number) arg).floatValue();
        } else if (type == Byte.class || type == byte.class) {
            if (arg instanceof Number)
                return ((Number) arg).byteValue();
        }
        return null; //not castable
    }

    /**
     * Instantiates a new instance of the specified class name
     * with the specified arguments.
     *
     * <p>It uses Class.forName to get the class.
     *
     * @param clsName the class name of the instance to create
     * @param argTypes the argument types of the constructor to invoke
     * @param args the arguments to initialize the instance
     * @return the new instance
     *
     * @exception NoSuchMethodException if a matching method is not found
     * @exception InstantiationException if the class that declares the
     * underlying constructor represents an abstract class
     * @exception InvocationTargetException if the underlying constructor
     * throws an exception
     * @exception ClassNotFoundException if the specified class name is not a class
     * @see #newInstance(Class, Class[], Object[])
     */
    public static final Object
    newInstance(String clsName, Class<?>[] argTypes, Object[] args)
    throws NoSuchMethodException, InstantiationException,
    InvocationTargetException, ClassNotFoundException, IllegalAccessException {
         return newInstance(Class.forName(clsName), argTypes, args);
    }
    /**
     * Creates and initializes a new instance of the specified class name
     * with the specified arguments, by use of {@link #forNameByThread}.
     *
     * <p>It uses {@link #forNameByThread} to get the class.
     *
     * @param clsName the class name of the instance to create
     * @param argTypes the argument types of the constructor to invoke
     * @param args the arguments to initialize the instance
     * @return the new instance
     *
     * @exception NoSuchMethodException if a matching method is not found
     * @exception InstantiationException if the class that declares the
     * underlying constructor represents an abstract class
     * @exception InvocationTargetException if the underlying constructor
     * throws an exception
     * @exception ClassNotFoundException if the specified class name is not a class
     * @see #newInstance(Class, Class[], Object[])
     */
    public static final Object
    newInstanceByThread(String clsName, Class<?>[] argTypes, Object[] args)
    throws NoSuchMethodException, InstantiationException,
    InvocationTargetException, ClassNotFoundException, IllegalAccessException {
         return newInstance(forNameByThread(clsName), argTypes, args);
    }
    /**
     * Creates and initializes a new instance of the specified class name
     * with default constructor, by use of {@link #forNameByThread}.
     */
    public static final Object newInstanceByThread(String clsName)
    throws NoSuchMethodException, InstantiationException,
    InvocationTargetException, ClassNotFoundException, IllegalAccessException {
        return newInstance(forNameByThread(clsName), null, null);
    }
    /**
     * Returns the Class object of the specified class name, using
     * the current thread's context class loader and the class will be initialized by default.
     * <p>It first tries Thread.currentThread().getContextClassLoader(),
     * and then {@link Classes}'s class loader if not found.
     *
     * <p>In additions, it handles the primitive types, such as int and double.
     *
     * @param clsName fully qualified name of the desired class
     * @return the Class object representing the desired class
     * @exception ClassNotFoundException if the class cannot be located by the specified class loader
     * @see #forNameByThread(String, boolean)
     */
    public static final Class<?> forNameByThread(String clsName)
            throws ClassNotFoundException {
        return forNameByThread(clsName, true);
    }

    /**
     * Returns the Class object of the specified class name, using
     * the current thread's context class loader.
     * <p>It first tries Thread.currentThread().getContextClassLoader(),
     * and then {@link Classes}'s class loader if not found.
     *
     * <p>In additions, it handles the primitive types, such as int and double.
     *
     * @param clsName fully qualified name of the desired class
     * @param initialize if {@code true} the class will be initialized.
     * @return the Class object representing the desired class
     * @exception ClassNotFoundException if the class cannot be located by the specified class loader
     * @since 10.0.0
     * @see #forNameByThread(String)
     */
    public static final Class<?> forNameByThread(String clsName, boolean initialize)
            throws ClassNotFoundException {
        clsName = toInternalForm(clsName);
        final Class<?> cls = Primitives.toClass(clsName);
        if (cls != null)
            return cls;

        // ZK-3762: Use the default class loader first if the property is not ready
        ClassLoader cl = _contextClassLoaderName == null
                ? Thread.currentThread().getContextClassLoader()
                : getContextClassLoaderForName(clsName);
        if (cl != null)
            try {
                return Class.forName(clsName, initialize, cl);
            } catch (ClassNotFoundException ex) { //ignore and try the other
            }
        return Class.forName(clsName);
    }

    /** Returns whether the specified class exists for the current thread's
     * context class loader.
     * @param clsnm the class name to test
     * @since 3.0.7
     */
    public static final boolean existsByThread(String clsnm) {
        try {
            forNameByThread(clsnm);
            return true;
        } catch (Throwable ex) {
            return false;
        }
    }

    /**
     * Change class name to internal form (e.g. byte[] -&gt; [B]). If already in
     * internal form, then just return it.
     */
    public static final String toInternalForm(String clsName) {
        final int k = clsName.indexOf('[');
        if (k <= 0) { //not an array, or already in internal form
            return clsName;    //just return
        }

        //component class
        final String elm = clsName.substring(0, k).trim();
        if (elm.length() == 0)
            throw new IllegalArgumentException("Not a legal class name: \""+clsName+'"');

        //array depth
        boolean leftb = false;
        final String stub = clsName.substring(k);
        final StringBuffer sb = new StringBuffer(128);
        for (int j = 0; j < stub.length(); j++) {
            final char ch = stub.charAt(j);
            if (ch == '[') {
                if (leftb)
                    throw new IllegalArgumentException("Not a legal class name: \""+clsName+'"');
                leftb = true;
                sb.append('[');
            } else if (ch == ']') {
                if (!leftb)
                    throw new IllegalArgumentException("Not a legal class name: \""+clsName+'"');
                leftb = false;
            }
        }
        if (leftb)
            throw new IllegalArgumentException("Not a legal class name: \""+clsName+'"');

        final char code = Primitives.getNotation(elm);
        if (code != (char)0) {//primitive array
            sb.append(code);
        } else {//object array
            sb.append('L').append(elm).append(';');
        }
        return sb.toString();
    }

    /**
     * Gets the topmost interface of the specified class or interface that
     * implements or extends the specified interface.
     * For example, if A extends B, and C implements A,
     * then getTopInterface(C, B) returns A.
     *
     * <p>The interfaces implemented by the specified class is checked first,
     * and then the subclass.
     *
     * @param cls the class or interface
     * @param subIF the sub-interface
     * @return the topmost interface extending subIF, or null if subIF
     * is not implemented by cls
     */
    public static Class<?> getTopmostInterface(Class<?> cls, Class<?> subIF) {
        if (cls.isInterface())
            return subIF.isAssignableFrom(cls) ? cls: null;

        while (cls != null) {
            final Class<?>[] ifs = cls.getInterfaces();
            for (int j = 0; j < ifs.length; ++j)
                if (subIF.isAssignableFrom(ifs[j]))
                    return ifs[j];

            cls = cls.getSuperclass();
        }
        return null;
    }
    /** Returns all interfaces that are implemented by the specified class.
     * <p>Unlike {@link Class#getInterfaces()}, it recursively searches
     * for all derived classes.
     */
    public static Class<?>[] getAllInterfaces(Class<?> cls) {
        final List<Class<?>> l = new LinkedList<>();
        while (cls != null) {
            final Class<?>[] ifs = cls.getInterfaces();
            Collections.addAll(l, ifs);

            cls = cls.getSuperclass();
        }
        final int sz = l.size();
        return l.toArray(new Class<?>[sz]);
    }        

    /**
     * Tests whether a class contains the specified method.
     * Only public methods are tested.
     *
     * @param cls the class to test
     * @param name the method name
     * @param paramTypes the list of parameter types
     * @return true if it contains the method
     */
    public static final boolean containsMethod
    (Class<?> cls, String name, Class<?>[] paramTypes) {
        try {
            cls.getMethod(name, paramTypes);
            return true;
        } catch (NoSuchMethodException ex) {//no found
            return false;
        }
    }

    /** Corrects a string to a valid Java name.
     * Currently, it only removes '-' and capitalizes the succeeding
     * character. Example, 'field-name' becomes 'fieldName'.
     */
    public final static String correctFieldName(String name) {
        int j = name.indexOf('-');
        if (j < 0)
            return name; //nothing to change

        for (final var sb = new StringBuilder(name);;) {
            sb.deleteCharAt(j);
            if (sb.length() == j)
                return sb.toString();

            sb.setCharAt(j, Character.toUpperCase(sb.charAt(j)));

            j = sb.indexOf("-", j);
            if (j < 0)
                return sb.toString();
        }
    }

    /**
     * Convert an attribute name, returned by toAttributeName, to
     * a method name.
     *
     * <p>toMethodName("true", "is") =&gt; "isTrue"<br>
     * toMethodName("true", "") =&gt; "true"
     *
     * @param attrName the attribute name
     * @param prefix the prefix; one of is, get and set
     * @return the method name
     * @see #toAttributeName
     */
    public static final String toMethodName(String attrName, String prefix) {
        if (prefix.isEmpty())
            return attrName;

        var sb = new StringBuilder(prefix);
        char[] buf = attrName.toCharArray();
        buf[0] = Character.toUpperCase(buf[0]);
        return sb.append(buf).toString();
    }

    /**
     * Tests if a method name is an attribute, i.e., prefixing with is,
     * get or set. Caller could then test if it is a setter or getter
     * by charAt(0)=='s'.
     *
     * <p>Note 'set' is considered as an attribute, whose name is an
     * empty string.
     *
     * @param methodName the method name to test
     * @return true if it is setter or getter
     */
    public static final boolean isAttribute(String methodName) {
        int len = methodName.length();
        if (len < 2)
            return false;

        int j;
        switch (methodName.charAt(0)) {
        case 's':
        case 'g':
            if (len<3 || methodName.charAt(1)!='e' || methodName.charAt(2)!='t')
                return false;
            j = 3;
            break;
        case 'i':
            if (methodName.charAt(1)!='s')
                return false;
            j = 2;
            break;
        default:
            return false;
        }
        return j==len || Character.isUpperCase(methodName.charAt(j));
    }

    /**
     * Converts a method name to an attribute name by removing the prefix
     * is, get or set, or null if it doesn't start with is, get or set.
     *
     * <p>The code is optimized for better performance.
     *
     * @param methodName the method name
     * @return the attribute name; null if it is not an attribute name
     * @see #toMethodName
     */
    public static final String toAttributeName(String methodName) {
        int len = methodName.length();
        if (len < 2)
            return null;

        int j;
        switch (methodName.charAt(0)) {
        case 's':
        case 'g':
            if (len<3 || methodName.charAt(1)!='e' || methodName.charAt(2)!='t')
                return null;
            j = 3;
            break;
        case 'i':
            if (methodName.charAt(1)!='s')
                return null;
            j = 2;
            break;
        default:
            return null;
        }
        if (j==len || Character.isUpperCase(methodName.charAt(j))) {
            char[] buf = new char[len - j];
            if (buf.length>0) {
                methodName.getChars(j, len, buf, 0);
                buf[0] = Character.toLowerCase(buf[0]);
            }
            return new String(buf);
        }
        return null;
    }

    //--Support Class--
    /**The method info class used for {@link #parseMethod(String signature)}.
     * This info describe the method return type, method name and two collections for
     * arguments type and arguments name;
     */
    public static class MethodInfo {
        /** The return type (class name), or null if no specified. */
        public String returnType;
        public String method;
        public String[] argTypes;
        public String[] argNames;
        public String throwsEx;
        
        //constructor
        protected MethodInfo(String r, String m, String[] argTs, String[] argNs, String tEx) {
            returnType = r;
            method = m;
            argTypes = argTs;
            argNames = argNs;
            throwsEx = tEx;
        }
    }
    /**
     * Gets the method information from a signature. 
     * It returns a method info with the return type, method name and two collections 
     * of arguments type and arguments name.
     *
     * @param signature the method signature.
     * @return MethodInfo The method information including return type, method name
     *            and two collections for argument type and arguments name.
     */
    public static final MethodInfo parseMethod(String signature) 
    throws IllegalSyntaxException {
        int len = signature.length();
        int j = Strings.skipWhitespaces(signature, 0);
        int k = Strings.anyOf(signature, "( \t\n\r", j);
        k = Strings.skipWhitespaces(signature, k);
        if (k >= len)
            throw new IllegalSyntaxException(signature);
        
        String returnType = null;
        char cc = signature.charAt(k);
        if (cc != '(') {    
            //skip the return type
            returnType = signature.substring(j, k).trim();
            //
            j = k;
            k = signature.indexOf('(', j + 1);
            if (k < 0)
                throw new IllegalSyntaxException(signature);    
        }
        String method = signature.substring(j, k).trim();
        
        Collection<String> argTypes = new LinkedList<>();
        Collection<String> argNames = new LinkedList<>();
        do {
            j = Strings.skipWhitespaces(signature, k + 1);
            if (signature.charAt(j) == ')') break;
            k = Strings.anyOf(signature, ",) \t\n\r", j);
            k = Strings.skipWhitespaces(signature, k);
            if (k >= len)
                throw new IllegalSyntaxException(signature);
            argTypes.add(signature.substring(j, k).trim());

            cc = signature.charAt(k);
            if (cc != ',' && cc != ')') { //parameter name found
                k = Strings.anyOf(signature, ",) \t\n\r", j = k);
                k = Strings.skipWhitespaces(signature, k);
                argNames.add(signature.substring(j, k).trim());

                k = Strings.anyOf(signature, ",)", k);
                if (k >= len)
                    throw new IllegalSyntaxException(signature);
            } else {
                argNames.add(""); //no name specified
            }
        } while (signature.charAt(k) == ',');

        assert argTypes.size() == argNames.size();

        //process throws ...
        String strThrows = "throws";
        String tEx = null;
        j = signature.indexOf(strThrows, k);
        if (j >= k && j < len) { //got throws
            k = signature.indexOf(';', j);
            if (k >= 0) 
                tEx = signature.substring(j + strThrows.length(), k).trim();
            else
                tEx = signature.substring(j + strThrows.length(), len).trim();
        }
    
        return new MethodInfo(returnType, method, 
                    argTypes.toArray(new String[argTypes.size()]), 
                    argNames.toArray(new String[argNames.size()]), 
                    tEx);
    }
    
    /**
     * Gets the method based on the signature with a class resolver.
     * It also returns the parameter names to the params list.
     *
     * <p>Like {@link #getMethodInPublic(Class, String, Class[])}, it returns
     * only public method in a public class/interface.
     *
     * <p>For example, "find(java.lang.String name)" will return
     * the method with one String-typed argument and params will hold "name".
     * The return type is optional (actually ignored).
     *
     * <p>If params is null, the parameter names are not returned and
     * the signature could be simplified as "find(java.lang.String)".
     *
     * <p>A cache mechanism is implemented, so you don't need to cache it
     * again in the caller.
     *
     * @param cls the class to look
     * @param signature the method signature; the return type is optional.<br/>
     * Notice that the argument's type must be a full-qualified class name,
     * unless its package is java.lang or it can be resolved by <code>resolver</code>.
     * @param params the collection to hold the parameter names returned;
     * null means no parameter names to return
     * @param resolver the class resolver used to resolve the class specified
     * in the signature. Ignored if null.
     * @since 6.0.0
     */
    public static final Method
    getMethodBySignature(Class<?> cls, String signature, Collection<String> params,
    ClassResolver resolver)
    throws NoSuchMethodException, ClassNotFoundException {
        MethodInfo mi = parseMethod(signature);

        LinkedList<Class<?>> argTypes = new LinkedList<Class<?>>();
        for (int i = 0; i < mi.argTypes.length; i++) {
            final String clsnm = mi.argTypes[i];
            argTypes.add(getClassOfSignature(resolver, clsnm));
            if (params != null)
                params.add(mi.argNames[i]);    //param name found
        }
        
        return getMethodInPublic(cls, mi.method,
            argTypes.toArray(new Class<?>[argTypes.size()]));
    }
    /**
     * Gets the method based on the signature. It also returns the parameter
     * names to the params list.
     *
     * <p>Like {@link #getMethodInPublic(Class, String, Class[])}, it returns
     * only public method in a public class/interface.
     *
     * <p>For example, "find(java.lang.String name)" will return
     * the method with one String-typed argument and params will hold "name".
     * The return type is optional (actually ignored).
     *
     * <p>If params is null, the parameter names are not returned and
     * the signature could be simplified as "find(java.lang.String)".
     *
     * <p>A cache mechanism is implemented, so you don't need to cache it
     * again in the caller.
     *
     * @param cls the class to look
     * @param signature the method signature; the return type is optional<br/>
     * Notice that the argument's type must be a full-qualified class name,
     * unless its package is java.lang.
     * @param params the collection to hold the parameter names returned;
     * null means no parameter names to return
     */
    public static final Method
    getMethodBySignature(Class<?> cls, String signature, Collection<String> params)
    throws NoSuchMethodException, ClassNotFoundException {
        return getMethodBySignature(cls, signature, params, null);
    }
    /** Resolves a class for the method signature.
     * This method will try java.lang.X if X is not found.
     */
    private static final
    Class getClassOfSignature(ClassResolver resolver, String clsnm)
    throws ClassNotFoundException {
        try {
            return resolver != null ?
                resolver.resolveClass(clsnm): forNameByThread(clsnm);
        } catch (ClassNotFoundException ex) {
            if (clsnm == null || clsnm.indexOf('.') >= 0)
                throw ex;
            try {
                return forNameByThread("java.lang." + clsnm);
            } catch (Throwable t) {
                throw ex;
            }
        }
    }

    /**
     * Gets the method that is declared in a public class/interface.
     *
     * <p>Class.getMethod returns a public method but the class itself
     * might not be public. However, in many cases, that class
     * also implements a public interface or class.
     *
     * <p>This method will search all its public classes to look for
     * the method that is 'real' public.
     *
     * <p>NoSuchMethodException is thrown if no public 
     * class/interface is found to have the method.
     */
    public static final Method
    getMethodInPublic(Class<?> cls, String name, Class<?>[] argTypes)
    throws NoSuchMethodException {
        final Method m = cls.getMethod(name, argTypes);
        if (Modifier.isPublic(m.getDeclaringClass().getModifiers()))
            return m;

        final Class<?>[] clses = cls.getInterfaces();
        for (int j = 0; j< clses.length; ++j)
            try {
                return getMethodInPublic(clses[j], name, argTypes);
            } catch (NoSuchMethodException ex) { //ignore it
            }

        final Class<?> basecls = cls.getSuperclass();
        if (basecls != null)
            try {
                return getMethodInPublic(basecls, name, argTypes);
            } catch (NoSuchMethodException ex) { //ignore it
            }

        throw newNoSuchMethodException(cls, name, argTypes);
    }
    private static NoSuchMethodException newNoSuchMethodException(Class cls,
    String name, Object[] args) {
        return new NoSuchMethodException(cls.getName()+": no method called "+name+" for "+Objects.toString(args));
    }

    /** Gets one of the close method by specifying the arguments, rather
     * than the argument types. It actually calls {@link #getCloseMethod}.
     */
    public static final
    Method getMethodByObject(Class<?> cls, String name, Object[] args)
    throws NoSuchMethodException {
        if (args == null)
            return getMethodInPublic(cls, name, null);

        Method mtd = match(cls, name, args, false);
        if (mtd == null) {
            mtd = match(cls, name, args, true);
            if (mtd == null)
                throw newNoSuchMethodException(cls, name, args);
        }
        return mtd;
    }
    /**
     * Gets one of the close methods -- a close method is a method
     * with the same name and the compatible argument type.
     * By compatible we mean the real method's argument type is
     * the same as or a superclass of the specified one.
     *
     * <p>It might not be the best fit one, unless there is a method
     * whose argument types are exactly argTypes.
     *
     * <p>You might specify the exact number in argTypes. If any of them is
     * unknown, use null. Example, in the following, the first argument could
     * be anything and the second is anything deriving from MyClass:<br>
     * <code>new Class[] {null, MyClass.class}</code>
     *
     * <p>Note: if an argument accepts int, then Integer is considered
     * as compatible (unlike Class.getMethod). So are long, byte...
     *
     * <p>A cache mechanism is implemented, so you don't need to cache it
     * again in the caller.
     *
     * @param cls the class to locate the method
     * @param name the method name
     * @param argTypes an array of the argument classes;
     * null to denote no argument at all (i.e., exact match).
     * Any argTypes[i] could be null to denote any class.
     * @return the method
     * @exception NoSuchMethodException if the method is not found
     */
    public static final Method
    getCloseMethod(Class<?> cls, String name, Class<?>[] argTypes)
    throws NoSuchMethodException {
        if (argTypes == null || argTypes.length == 0)
            return getMethodInPublic(cls, name, null);

        final AOInfo aoi = new AOInfo(cls, name, argTypes, 0);
        Object m = _closms.get(aoi);
        if( m == NOT_FOUND)
            throw newNoSuchMethodException(cls, name, argTypes);
        
        if (m != null) 
            return (Method) m;
        
        try{
            m = myGetCloseMethod(cls, name, argTypes, false);
        }catch(NoSuchMethodException ex){
            _closms.put(aoi, NOT_FOUND);
            throw ex;
        }
        _closms.put(aoi, m);
        return (Method) m;
    }
    /**
     * Like {@link #getCloseMethod} to get a 'close' method, but
     * it look for subclass of the argument (instead of superclass).
     * In other words, it looks for the method whose argument type is
     * the same as or a subclass of the specified one.
     */
    public static final Method
    getCloseMethodBySubclass(Class<?> cls, String name, Class<?>[] argTypes)
    throws NoSuchMethodException {
        if (argTypes == null || argTypes.length == 0)
            return getMethodInPublic(cls, name, null);

        final AOInfo aoi = new AOInfo(cls, name, argTypes, B_BY_SUBCLASS);
        Object m = _closms.get(aoi);
        if( m == NOT_FOUND)
            throw newNoSuchMethodException(cls, name, argTypes);
        
        if (m != null) 
            return (Method) m;

        try{
            m = myGetCloseMethod(cls, name, argTypes, true);
        }catch(NoSuchMethodException ex){
            _closms.put(aoi, NOT_FOUND);
            throw ex;
        }
        _closms.put(aoi, m);
        return (Method) m;
    }
    private static Cache<AOInfo, Object> _closms = new FastReadCache<AOInfo, Object>(
        Library.getIntProperty("org.zkoss.lang.Classes.methods.cache.maxSize", 600),
        4*60*60*1000);
    private static final
    Method myGetCloseMethod(final Class<?> cls, final String name,
    final Class<?>[] argTypes, final boolean bySubclass)
    throws NoSuchMethodException {
//        assert argTypes != null: "Caller shall handle null";
        for (int j = 0;; ++j) {
            if (j == argTypes.length) {//all argTypes[j] non-null
                try {
                    return getMethodInPublic(cls, name, argTypes);
                } catch (NoSuchMethodException ex) { //ignore it
                    break;
                }
            }
            if (argTypes[j] == null) //special handling required
                break;
        }

        final Method [] ms = cls.getMethods();
        for (int j = 0; j < ms.length; ++j) {
            if (!ms[j].getName().equals(name))
                continue;

            final Class<?>[] mTypes = ms[j].getParameterTypes();
            if (mTypes.length != argTypes.length)
                continue; //not matched

            final boolean bPublic =
                Modifier.isPublic(ms[j].getDeclaringClass().getModifiers());
            for (int k = 0;; ++k) {
                if (k == argTypes.length) { //all matched
                    if (bPublic)
                        return ms[j];
                    try {
                        return getMethodInPublic(
                            cls, ms[j].getName(), ms[j].getParameterTypes());
                    } catch (NoSuchMethodException ex) {
                    }
                    break;//not match; look for next
                }

                final Class<?> argType = argTypes[k], mType = mTypes[k];
                if (argType == null
                || (!bySubclass && mType.isAssignableFrom(argType))
                || (bySubclass && argType.isAssignableFrom(mType)))
                    continue; //match

                final Class<?> c = Primitives.toPrimitive(argType);
                if (c == null || !c.equals(mType))
                    break; //not match
            }
        }
        throw newNoSuchMethodException(cls, name, argTypes);
    }

    /** Returns all close methods that match the specified condition, or
     * a zero-length array if none is found.
     * <p>Unlike {@link #getCloseMethod}, we don't cache the searched result,
     * and it won't throw any exception.
     */
    public static final
    Method[] getCloseMethods(Class<?> cls, String name, Class<?>[] argTypes) {
        if (argTypes == null || argTypes.length == 0) {
            try {
                return new Method[] {getMethodInPublic(cls, name, null)};
            } catch (NoSuchMethodException ex) {
                return new Method[0];
            }
        }
        return myGetCloseMethods(cls, name, argTypes, false);
    }
    /**
     * Like {@link #getCloseMethods} to get all 'close' methods, but
     * it look for subclass of the argument (instead of superclass).
     * In other words, it looks for the method whose argument type is
     * the same as or a subclass of the specified one.
     */
    public static final
    Method[] getCloseMethodsBySubclass(Class<?> cls, String name, Class<?>[] argTypes) {
        if (argTypes == null || argTypes.length == 0)
            return getCloseMethods(cls, name, null);
        return myGetCloseMethods(cls, name, argTypes, true);
    }
    private static final Method[]
    myGetCloseMethods(final Class<?> cls, final String name,
    final Class<?>[] argTypes, final boolean bySubclass) {
//        assert argTypes != null: "Caller shall handle null";
        final List<Method> mtds = new LinkedList<Method>();
        final Method [] ms = cls.getMethods();
        for (int j = 0; j < ms.length; ++j) {
            if (!ms[j].getName().equals(name))
                continue;

            final Class<?>[] mTypes = ms[j].getParameterTypes();
            if (mTypes.length != argTypes.length)
                continue; //not matched

            final boolean bPublic =
                Modifier.isPublic(ms[j].getDeclaringClass().getModifiers());
            for (int k = 0;; ++k) {
                if (k == argTypes.length) { //all matched
                    if (bPublic) {
                        mtds.add(ms[j]);
                    } else {
                        try {
                            mtds.add(getMethodInPublic(
                            cls, ms[j].getName(), ms[j].getParameterTypes()));
                        } catch (NoSuchMethodException ex) { //ignore; not add
                        }
                    }
                    break; //found; next method
                }

                final Class<?> argType = argTypes[k], mType = mTypes[k];
                if (argType == null
                || (!bySubclass && mType.isAssignableFrom(argType))
                || (bySubclass && argType.isAssignableFrom(mType)))
                    continue; //match one argument

                final Class<?> c = Primitives.toPrimitive(argType);
                if (c == null || !c.equals(mType))
                    break; //not match; next method
            }
        }
        return mtds.toArray(new Method[mtds.size()]);
    }

    /**
     * Search the get method; not imply B_METHODONLY.
     */
    public static final int B_GET=0; //must be 0
    /**
     * Search the set method; not imply B_METHODONLY.
     */
    public static final int B_SET=1; //must be 1
    /**
     * Only search for public methods or fields.
     */
    public static final int B_PUBLIC_ONLY=0x0002;
    /**
     * Only search for methods; excluding fields.
     */
    public static final int B_METHOD_ONLY=0x0004;

    /** Used by {@link #getCloseMethodBySubclass} to distinguish
     * {@link #getCloseMethod}.
     */
    private static final int B_BY_SUBCLASS = 0x1000;

    /**
     * Gets the specified accessible object, either a method or a field, by
     * searching the specified name.
     *
     * <p>The search sequence is: (assume field name is body)&gt;<br>
     * getBody(...)<br>
     * isBody(...)<br>
     * body(...)<br>
     * body
     *
     * <p>If B_SET is specified, setBody(...) is searched instead of
     * getBody(...) and isBody(...). The field is searched only if
     * argsType.length is 0 or 1.
     *
     * <p>Note: it uses {@link #getCloseMethod} to get the method.
     *
     * <p>A cache mechanism is implemented, so you don't need to cache it
     * again in the caller.
     *
     * @param cls the class to find
     * @param name the name of the accessible object
     * @param argTypes the parameter type of the method to find
     * @param flags a combination of B_xxx or zero
     * @return the accessible object; either Field or Method
     * @exception NoSuchMethodException if neither the set method of
     *   specified field nor the field itself not found
     * @exception SecurityException if access to the information is denied
     */
    public static final AccessibleObject
    getAccessibleObject(Class<?> cls, String name, Class<?>[] argTypes, int flags)
    throws NoSuchMethodException {
        final AOInfo aoi = new AOInfo(cls, name, argTypes, flags);
        AccessibleObject ao = _acsos.get(aoi);
        if (ao != null)
            return ao;

        ao = myGetAcsObj(cls, name, argTypes, flags);
        _acsos.put(aoi, ao);
        return ao;
    }
    private static Cache<AOInfo, AccessibleObject> _acsos = new FastReadCache<AOInfo, AccessibleObject>(
        Library.getIntProperty("org.zkoss.lang.Classes.methods.cache.maxSize", 600),
        4*60*60*1000);
    private static final AccessibleObject
    myGetAcsObj(Class<?> cls, String name, Class<?>[] argTypes, int flags)
    throws NoSuchMethodException {
        //try public set/get
        final String decoratedName =
            toMethodName(name, (flags&B_SET) != 0 ? "set": "get");
        try {
            return getCloseMethod(cls, decoratedName, argTypes);
        } catch (NoSuchMethodException ex) { //ignore
        }

        //try public is
        String isMethodName = null;
        if ((flags&B_SET) == 0)
            try {
                isMethodName = toMethodName(name, "is");
                return getCloseMethod(cls, isMethodName, argTypes);
            } catch (NoSuchMethodException ex) { //ignore
            }

        //try public same
        try {
            return getCloseMethod(cls, name, argTypes);
        } catch (NoSuchMethodException ex) {
            if ((flags & (B_PUBLIC_ONLY|B_METHOD_ONLY))
                      ==(B_PUBLIC_ONLY|B_METHOD_ONLY))
                throw ex;
        }

        if ((flags & B_PUBLIC_ONLY) == 0) {
            //try any set/get
            try {
                return getAnyMethod(cls, decoratedName, argTypes);
            } catch (NoSuchMethodException ex) { //ignore
            }

            //try any is
            if ((flags&B_SET) == 0)
                try {
                    return getAnyMethod(cls, isMethodName, argTypes);
                } catch (NoSuchMethodException ex) { //ignore
                }

            //try any same
            try {
                return getAnyMethod(cls, name, argTypes);
            } catch (NoSuchMethodException ex) {
                if ((flags & B_METHOD_ONLY) != 0)
                    throw ex;
            }
        }

        if (argTypes != null && argTypes.length > 1)
            throw newNoSuchMethodException(cls, name, argTypes);

        try {
            //try public field
            try {
                return cls.getField(name);
            } catch (NoSuchFieldException ex) { //ignore
                if ((flags & B_PUBLIC_ONLY) != 0)
                    throw ex;
            }

            //try any field
            return getAnyField(cls, name);
        } catch (NoSuchFieldException ex) { //ignore
            throw newNoSuchMethodException(cls, name, argTypes);
        }
    }
    /** The information of the access object. */
    private static class AOInfo {
        private Class<?> cls;
        private String name;
        private Class<?>[] argTypes;
        private int flags;
        private AOInfo(Class<?> cls, String name, Class<?>[] argTypes, int flags) {
            this.cls = cls;
            this.name = name;
            this.argTypes = argTypes;
            this.flags = flags;
        }
        public int hashCode() {
            return cls.hashCode() + name.hashCode() + flags;
        }
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof AOInfo))
                return false;

            AOInfo aoi = (AOInfo)o;
            int len =  argTypes != null ? argTypes.length: 0;
            int len2 = aoi.argTypes != null ? aoi.argTypes.length: 0;
            if (len != len2)
                return false;

            if (flags != aoi.flags || !cls.equals(aoi.cls)
            || !name.equals(aoi.name))
                return false;

            for (int j = 0; j < len; ++j)
                if (!Objects.equals(argTypes[j], aoi.argTypes[j]))
                    return false;
            return true;
        }
    }

    /**
     * Gets the specified method by searching all methods including
     * <i>any</i> access control and any base class.
     * Note: you rarely need to call this method. In most cases,
     * Class.getMethod, {@link #getCloseMethod}, and
     * {@link #getMethodInPublic} are what you need.
     *
     * <p>The search sequence is: this class's methods, and then
     * the superclass's methods.
     *
     * <p>Note: public methods don't be treated different. If the caller
     * wants to search public methods first, it has to call Class.getMethod
     * first.
     *
     * @param cls the class to search
     * @param name the method name
     * @param argTypes the parameter array of types
     * @return the Method object
     * @exception NoSuchMethodException if a matching method is not found.
     * @exception SecurityException if access to the information is denied.
     * @see #getAccessibleObject(Class, String, Class[], int)
     * @see #getAnyField(Class, String)
     */
    public static final Method
    getAnyMethod(Class<?> cls, String name, Class<?>[] argTypes)
    throws NoSuchMethodException {
        try {
            return cls.getDeclaredMethod(name, argTypes);
        } catch (NoSuchMethodException ex) {
            final Class<?>[] clses = cls.getInterfaces();
            for (int j = 0; j< clses.length; ++j)
                try {
                    return getAnyMethod(clses[j], name, argTypes);
                } catch (NoSuchMethodException e2) { //ignore it
                }

            cls = cls.getSuperclass();
            if (cls == null)
                throw ex;
            return getAnyMethod(cls, name, argTypes);
        }
    }

    /**
     * Gets the specified field by searching all fields including
     * any access control and any base class.
     * The search sequence is: this class's fields, and then
     * the superclass's fields.
     *
     * <p>Note: public fields don't be treated different. If the caller
     * wants to search public fields first, it has to call Class.getField
     * first.
     *
     * @param cls the class to search
     * @param name the field name
     * @return the Field object
     * @exception NoSuchFieldException if a matching field is not found.
     * @exception SecurityException if access to the information is denied.
     * @see #getAccessibleObject(Class, String, Class[], int)
     * @see #getAnyMethod(Class, String, Class[])
     */
    public static final Field getAnyField(Class<?> cls, String name)
    throws NoSuchFieldException {
        for (;;) {
            try {
                return cls.getDeclaredField(name);
            } catch (NoSuchFieldException ex) {
                cls = cls.getSuperclass();
                if (cls == null)
                    throw ex;
            }
        }
    }

    /**
     * Searches thru each element of the specified array of classes, and
     * returns classes that are super-classes (or equal) of
     * the specified class.
     *
     * @param cls the specified class; null is not allowed
     * @param clsToCheck the class array to check; null is acceptable
     * @return a subset of clsToCheck that are super-class of cls;
     * null if no one qualified
     */
    public static final Class<?>[] getSuperClasses(Class<?> cls, Class<?>[] clsToCheck) {
        if (clsToCheck!=null) {
            int[] hits = new int[clsToCheck.length];
            int no = 0;
            for (int j=0; j<clsToCheck.length; ++j)
                if (clsToCheck[j].isAssignableFrom(cls))
                    hits[no++] = j;

            if (no != clsToCheck.length) {
                if (no == 0)
                    return null;
                Class<?>[] exc = new Class[no];
                for (int j=0; j<no; ++j)
                    exc[j] = clsToCheck[hits[j]];
                return exc;
            }
        }
        return clsToCheck;
    }

    /**
     * Check whether the specified class is a primitive or a primitive wrapper.
     */
    public static final boolean isPrimitiveWrapper(Class<?> cls) {
        return Objects.equals(cls.getPackage(), Boolean.class.getPackage())
            && (cls.equals(Boolean.class) || cls.equals(Byte.class)
                || cls.equals(Character.class) || cls.equals(Double.class)
                || cls.equals(Float.class) || cls.equals(Integer.class)
                || cls.equals(Long.class) || cls.equals(Short.class));
    }
    /** Checks whether the specified class is a numeric class.
     *
     * @param extend whether to consider Date, char, boolean, Character
     * and Boolean as a numeric object.
     */
    public static final boolean isNumeric(Class<?> cls, boolean extend) {
        if (cls.isPrimitive())
            return extend ||
                (!cls.equals(char.class) && !cls.equals(boolean.class));

        if (Number.class.isAssignableFrom(cls))
            return true;

        return extend &&
            (cls.equals(Date.class) || cls.equals(Boolean.class)
            || cls.equals(Character.class));
    }

    /** Converts an object to the specified class.
     * It is the same as coerce(cls, val, true).
     *
     * @param val the value.
     * @exception ClassCastException if failed to convert
     * @see #coerce(Class, Object, boolean)
     */
    public static Object coerce(Class<?> cls, Object val)
    throws ClassCastException {
        if (cls.isInstance(val))
            return val;

        if (String.class == cls) {
            return Objects.toString(val);
        } else if (BigDecimal.class == cls) {
            if (val == null) {
                return null;
            } else if (val instanceof Double) {
                // B65-ZK-1944: Use String constructor to get the predictable value
                return new BigDecimal(Double.toString((Double)val));
            } else if (val instanceof Float) {
                // B65-ZK-1944: Use  String constructor to get the predictable value
                return new BigDecimal(Float.toString((Float)val));
            } else if (val instanceof BigInteger) {
                return new BigDecimal((BigInteger)val);
            } else if (val instanceof Number) {
                return BigDecimals.toBigDecimal(((Number)val).intValue());
            } else if (val instanceof String) {
                return new BigDecimal((String)val);
            } else if (val instanceof Date) {
                return new BigDecimal(((Date)val).getTime());
            }
        } else if (Integer.class == cls || int.class == cls) {
            if (val == null) {
                return Integer.class == cls ? null: Objects.ZERO_INTEGER;
            } else if (val instanceof Integer) { //int.class
                return val;
            } else if (val instanceof Number) {
                return ((Number) val).intValue();
            } else if (val instanceof String) {
                return Integer.valueOf((String) val);
            }
        } else if (Boolean.class == cls || boolean.class == cls) {
            if (val == null) {
                return Boolean.class == cls ? null: Boolean.FALSE;
            } else if (val instanceof Boolean) { //boolean.class
                return val;
            } else if (val instanceof String) {
                return Boolean.valueOf((String)val);
            } else if (val instanceof BigDecimal) {
                return ((BigDecimal) val).signum() != 0;
            } else if (val instanceof BigInteger) {
                return ((BigInteger) val).signum() != 0;
            } else if (val instanceof Number) {
                return ((Number) val).intValue() != 0;
            } else {
                return Boolean.TRUE; //non-null is true
            }
        } else if (Short.class == cls || short.class == cls) {
            if (val == null) {
                return Short.class == cls ? null: Objects.ZERO_SHORT;
            } else if (val instanceof Short) { //short.class
                return val;
            } else if (val instanceof Number) {
                return ((Number) val).shortValue();
            } else if (val instanceof String) {
                return Short.valueOf((String) val);
            }
        } else if (Long.class == cls || long.class == cls) {
            if (val == null) {
                return Long.class == cls ? null: Objects.ZERO_LONG;
            } else if (val instanceof Long) { //long.class
                return val;
            } else if (val instanceof Number) {
                return ((Number) val).longValue();
            } else if (val instanceof String) {
                return Long.valueOf((String) val);
            } else if (val instanceof Date) {
                return ((Date) val).getTime();
            }
        } else if (Double.class == cls || double.class == cls) {
            if (val == null) {
                return Double.class == cls ? null: Objects.ZERO_DOUBLE;
            } else if (val instanceof Double) { //double.class
                return val;
            } else if (val instanceof Number) {
                return ((Number) val).doubleValue();
            } else if (val instanceof String) {
                return Double.valueOf((String)val);
            } else if (val instanceof Date) {
                return ((Date) val).getTime();
            }
        } else if (BigInteger.class == cls) {
            if (val == null) {
                return null;
            } else if (val instanceof Integer) {
                return BigIntegers.toBigInteger((Integer)val);
            } else if (val instanceof Short) {
                return BigIntegers.toBigInteger((Short)val);
            } else if (val instanceof Byte) {
                return BigIntegers.toBigInteger((Byte)val);
            } else if (val instanceof Number) {
                return BigIntegers.toBigInteger(((Number)val).longValue());
            } else if (val instanceof String) {
                return new BigInteger((String)val);
            } else if (val instanceof Date) {
                return BigIntegers.toBigInteger(((Date)val).getTime());
            }
        } else if (Float.class == cls || float.class == cls) {
            if (val == null) {
                return Float.class == cls ? null: Objects.ZERO_FLOAT;
            } else if (val instanceof Float) { //float.class
                return val;
            } else if (val instanceof Number) {
                return new Float(((Number)val).floatValue());
            } else if (val instanceof String) {
                return new Float((String)val);
            } else if (val instanceof Date) {
                return new Float(((Date)val).getTime());
            }
        } else if (Byte.class == cls || byte.class == cls) {
            if (val == null) {
                return Byte.class == cls ? null: Objects.ZERO_BYTE;
            } else if (val instanceof Byte) { //byte.class
                return val;
            } else if (val instanceof Number) {
                return new Byte(((Number)val).byteValue());
            } else if (val instanceof String) {
                return new Byte((String)val);
            }
        } else if (Character.class == cls || char.class == cls) {
            if (val == null) {
                return Character.class == cls ? null: Objects.NULL_CHARACTER;
            } else if (val instanceof Character) { //character.class
                return val;
            } else if (val instanceof Number) {
                return new Character((char)((Number)val).shortValue());
            } else if (val instanceof String) {
                final String s = (String)val;
                return s.length() > 0 ? new Character(s.charAt(0)): Objects.NULL_CHARACTER;
            }
        } else if (Date.class == cls) {
            if (val == null) {
                return null;
            } else if (val instanceof Number) {
                return new Date(((Number)val).longValue());
            }
        } else if (Number.class == cls) {
            if (val == null) {
                return null;
            } else if (val instanceof String) {
                return new BigDecimal((String)val);
            } else if (val instanceof Date) {
                return new BigDecimal(((Date)val).getTime());
            }
        } else {
            if (val == null) {
                return null;
            } else {
                try {
                    return newInstance(cls, new Object[] {val});
                } catch (Exception ex) {
                    final ClassCastException t =
                        new ClassCastException(
                            Messages.get(MCommon.CLASS_NOT_COMPATIABLE,
                            new Object[] {val.getClass(), cls}));
                    t.initCause(ex);
                    throw t;
                }
            }
        }

        throw new ClassCastException(
            Messages.get(MCommon.CLASS_NOT_COMPATIABLE, new Object[] {
                    val+"("+val.getClass().getName()+")",
                cls}));
    }
    /** Converts to the specified type.
     *
     * @param nullable whether the result could be null.
     * If false, 0 is used for number, the default constructor is used
     * for others. {@link #coerce(Class, Object)} is a special case that
     * is equivalent to nullable=true.
     * @exception ClassCastException if failed to convert
     */
    public static Object coerce(Class<?> cls, Object val, boolean nullable)
    throws ClassCastException {
        if (nullable || val != null)
            return coerce(cls, val);

        if (BigDecimal.class == cls) {
            return Objects.ZERO_BIG_DECIMAL;
        } else if (Integer.class == cls || int.class == cls) {
            return Objects.ZERO_INTEGER;
        } else if (Boolean.class == cls || boolean.class == cls) {
            return Boolean.FALSE;
        } else if (Short.class == cls || short.class == cls) {
            return Objects.ZERO_SHORT;
        } else if (Long.class == cls || long.class == cls) {
            return Objects.ZERO_LONG;
        } else if (Double.class == cls || double.class == cls) {
            return Objects.ZERO_DOUBLE;
        } else if (Byte.class == cls || byte.class == cls) {
            return Objects.ZERO_BYTE;
        } else if (BigInteger.class == cls) {
            return Objects.ZERO_BIG_INTEGER;
        } else if (Float.class == cls || float.class == cls) {
            return Objects.ZERO_FLOAT;
        } else if (Character.class == cls || char.class == cls) {
            return Objects.NULL_CHARACTER;
        } else {
            try {
                return newInstance(cls, new Object[] {val});
            } catch (Exception ex) {
                final ClassCastException t =
                    new ClassCastException(
                        Messages.get(MCommon.CLASS_NOT_COMPATIABLE,
                        new Object[] {"null", cls}));
                t.initCause(ex);
                throw t;
            }
        }
    }

    // a thread-safe statically object for lazy initialized.
    private static class LazyInitializationHolder {
        private static ContextClassLoaderFactory instance;
        static {
            ContextClassLoaderFactory factory = null;
            try {
                if (!Strings.isEmpty(_contextClassLoaderName))
                    factory = (ContextClassLoaderFactory) newInstanceByThread(_contextClassLoaderName);
                else factory = new ThreadBasedContextClassLoaderFactory();
            } catch (Exception e) {
                log.warn("", e);
                if (factory == null)
                    factory = new ThreadBasedContextClassLoaderFactory();
            }
            instance = factory;
        }
    }


    private static class ThreadBasedContextClassLoaderFactory
            implements ContextClassLoaderFactory {

        public ClassLoader getContextClassLoader(Class<?> reference) {
            return Thread.currentThread().getContextClassLoader();
        }

        public ClassLoader getContextClassLoaderForName(String className) {
            return Thread.currentThread().getContextClassLoader();
        }
    }

    /**
     * Returns the context ClassLoader for the reference class.
     * <p>Default: return from the current thread.
     * <br/>
     * Or specify the library property of <code>org.zkoss.lang.contextClassLoader.class</code>
     * in zk.xml to provide a customized class loader.
     * </p>
     * @param reference the reference class where it is invoked from.
     * @since 8.0.2
     */
    public static ClassLoader getContextClassLoader(Class<?> reference) {
        return LazyInitializationHolder.instance.getContextClassLoader(reference);
    }

    /**
     * Returns the context ClassLoader for the given class name
     * <p>Default: return from the current thread.
     * <br/>
     * Or specify the library property of <code>org.zkoss.lang.contextClassLoader.class</code>
     * in zk.xml to provide a customized class loader.
     * </p>
     * @param className the class name to be loaded by the returned class loader
     * @since 8.5.1
     */
    public static ClassLoader getContextClassLoaderForName(String className) {
        // Avoid loading "org.zkoss.lang.contextClassLoader.class" looping
        if (LazyInitializationHolder.instance == null)
            return Thread.currentThread().getContextClassLoader();
        return LazyInitializationHolder.instance.getContextClassLoaderForName(className);
    }

    /**
     * Sets the customized context ClassLoader name by the library property
     * of <code>org.zkoss.lang.contextClassLoader.class</code>.
     * It is intended to be called by ZK internally.
     * @since 8.5.1
     */
    public static void configureContextClassLoader() {
        _contextClassLoaderName = Library.getProperty("org.zkoss.lang.contextClassLoader.class", "");
    }
}