/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2005 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.lang;

import java.lang.CharSequence;
import java.util.Collection;
import java.util.Iterator;
import javolution.context.LogContext;
import javolution.text.TextBuilder;
import javolution.util.FastComparator;
import javolution.util.FastMap;
import javolution.util.FastSet;
import javolution.util.FastTable;

/**
 * <p> This utility class greatly facilitates the use of reflection to invoke 
 *     constructors or methods which may or may not exist at runtime or
 *     may be loaded/unloaded dynamically such as when running on a
 *     <a href="http://www.osgi.org/">OSGi Platform</a>. For example:[code]
 *         public class Activator implements BundleActivator {
 *              public void start(BundleContext context) throws Exception {
 *                   Reflection.getInstance().add(Activator.class.getClassLoader());
 *                   ...
 *              }
 *              public void stop(BundleContext context) throws Exception {
 *                   Reflection.getInstance().remove(Activator.class.getClassLoader());
 *                   ...
 *              }
 *         }[/code]
 *
 * <p> The constructors/methods are identified through their signatures
 *     represented as a {@link String}. When the constructor/method does
 *     not exist (e.g. class not found) or when the platform does not support
 *     reflection, the constructor/method is <code>null</code> 
 *     (no exception raised). Here is an example of timer taking advantage 
 *     of the new (JRE1.5+) high resolution time when available:[code]
 *     public static long microTime() {
 *         if (NANO_TIME_METHOD != null) { // JRE 1.5+
 *             Long time = (Long) NANO_TIME_METHOD.invoke(null); // Static method.
 *             return time.longValue() / 1000;
 *         } else { // Use the less accurate time in milliseconds.
 *             return System.currentTimeMillis() * 1000;
 *         }
 *     }
 *     private static final Reflection.Method NANO_TIME_METHOD 
 *         = Reflection.getInstance().getMethod("java.lang.System.nanoTime()");[/code]</p>
 *   
 * <p> Arrays and primitive types are supported. For example:[code]
 *     Reflection.Constructor sbc = Reflection.getInstance().getConstructor("java.lang.StringBuilder(int)");
 *     if (sbc != null) { // JDK 1.5+
 *        Object sb = sbc.newInstance(new Integer(32));
 *        Reflection.Method append = Reflection.getInstance().getMethod("java.lang.StringBuilder.append(char[], int, int)");
 *        append.invoke(sb, new char[] { 'h', 'i' }, new Integer(0), new Integer(2));
 *        System.out.println(sb);
 *    }
 * 
 *    > hi[/code]</p>
 * 
 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 5.5, April 20, 2009
 */
public abstract class Reflection {

    /**
     * Holds the default implementation (configurable).
     */
    private static volatile Reflection INSTANCE = new Default();

    /**
     * Holds the default implementation (configurable).
     */
    public static final Configurable <Class<? extends Reflection>>  CLASS = new Configurable(Default.class) {

        protected void notifyChange(Object oldValue, Object newValue) {
            try {
                INSTANCE = (Reflection) ((Class) newValue).newInstance();
            } catch (Throwable error) {
                LogContext.error(error);
            }
        }
    };

    /**
     * Default constructor.
     */
    protected Reflection() {
    }

    /**
     * Returns the current reflection instance. The implementation class
     * is defined by {@link #CLASS} (configurable}.
     *
     * @return the reflection instance.
     */
    public static final Reflection getInstance() {
        return INSTANCE;
    }

    /**
     * Adds the specified class loader to the research tree.
     *
     * @param classLoader the class loader being added.
     */
    public abstract void add(Object classLoader);

    /**
     * Removes the specified class loader from the research tree.
     * This method clears any cache data to allow for classes
     * associated to the specified class loader to be garbage collected.
     *
     * @param classLoader the class loader being removed.
     */
    public abstract void remove(Object classLoader);

    /**
     * Returns the class having the specified name. This method searches the
     * class loader of the reflection implementation, then any
     * {@link #add additional} class loaders.
     * If the class is found, it is initialized
     * and returned; otherwise <code>null</code> is returned.
     * The class may be cached for performance reasons.
     *
     * @param name the name of the class to search for.
     * @return the corresponding class or <code>null</code>
     */
    public abstract Class getClass(CharSequence name);

    /**
     * Equivalent to {@link #getClass(CharSequence)} (for J2ME compatibility).
     */
    public Class getClass(String name) {
        Object obj = name;
        if (obj instanceof CharSequence)
            return getClass((CharSequence) obj);
        // String not a CharSequence on J2ME
        TextBuilder tmp = TextBuilder.newInstance();
        try {
            tmp.append(name);
            return getClass(tmp);
        } finally {
            TextBuilder.recycle(tmp);
        }
    }

    /**
     * Returns the parent class of the specified class or interface.
     *
     * @param forClass the class for which the parent class is returned.
     * @return the parent class of the specified class or <code>null</code>
     *         if none (e.g. Object.class or top interface).
     */
    public abstract Class getSuperclass(Class forClass);

    /**
     * Returns the interfaces implemented by the specified class or interface.
     *
     * @param forClass the class for which the interfaces are returned.
     * @return an array holding the interfaces implemented (empty if none).
     */
    public abstract Class[] getInterfaces(Class forClass);

    /**
     * Returns the constructor having the specified signature.
     *
     * @param signature the textual representation of the constructor signature.
     * @return the corresponding constructor or <code>null</code> if none
     *         found.
     */
    public abstract Constructor getConstructor(String signature);

    /**
     * Returns the method having the specified signature.
     *
     * @param signature the textual representation of the method signature.
     * @return the corresponding constructor or <code>null</code> if none
     *         found.
     */
    public abstract Method getMethod(String signature);

    /**
     * Returns the field of specified type which has been attached to a class.
     * If <code>inherited</code> is <code>true</code> the class hierarchy
     * of the given class (parent classes and implementing interfaces) is
     * searched. The method forces the initialization of the specified
     * <code>forClass</code>.
     *
     * @param forClass the base class for which the attached field is searched.
     * @param type the type of field being searched for.
     * @param inherited indicates if the class hierarchy is searched too.
     * @return an attached field of specified type possibly inherited or <code>null</code>
     *         if none found.
     * @see    #setField(java.lang.Object, java.lang.Class, java.lang.Class)
     */
    public abstract  <T>   T  getField(Class forClass, Class <T>  type, boolean inherited);

    /**
     * Attaches a field of specified type to a class (the attached field is
     * dereferenced when the class is unloaded).
     *
     * @param obj the field object being attached.
     * @param forClass the class to which the field is attached.
     * @param type the category type of the field being attached.
     * @throws IllegalArgumentException if a field of specified type is already
     *         attached to the specified class.
     */
    public abstract  <T>  void setField( T  obj, Class forClass, Class <T>  type);

    /**
     * This interface represents a run-time constructor obtained through reflection.
     *
     * Here are few examples of utilization:[code]
     * // Default constructor (fastList = new FastList())
     * Reflection.Constructor fastListConstructor
     *     = Reflection.getInstance().getConstructor("javolution.util.FastList()");
     * Object fastList = fastListConstructor.newInstance();
     *
     * // Constructor with arguments (fastMap = new FastMap(64))
     * Reflection.Constructor fastMapConstructor
     *     = Reflection.getInstance().getConstructor("javolution.util.FastMap(int)");
     * Object fastMap = fastMapConstructor.newInstance(new Integer(64));
     * [/code]
     */
    public interface Constructor {

        /**
         * Returns an array of <code>Class</code> objects that represents
         * the formal parameter types, in declaration order of this constructor.
         *
         * @return the parameter types for this constructor.
         */
        Class[] getParameterTypes();

        /**
         * Invokes this constructor with no argument.
         *
         * @return the object being instantiated.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 0</code>
         */
        Object newInstance();

        /**
         * Invokes this constructor with the specified single argument.
         *
         * @param arg0 the first argument.
         * @return the object being instantiated.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 1</code>
         */
        Object newInstance(Object arg0);

        /**
         * Invokes this constructor with the specified two arguments.
         *
         * @param arg0 the first argument.
         * @param arg1 the second argument.
         * @return the object being instantiated.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 2</code>
         */
        Object newInstance(Object arg0, Object arg1);

        /**
         * Invokes this constructor with the specified three arguments.
         *
         * @param arg0 the first argument.
         * @param arg1 the second argument.
         * @param arg2 the third argument.
         * @return the object being instantiated.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 3</code>
         */
        Object newInstance(Object arg0, Object arg1, Object arg2);
        /**
         * Invokes this constructor with the specified arguments.
         *
         * @param args the arguments.
         * @return the object being instantiated.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != args.length</code>
        */
        Object newInstance(Object... args);
        /**/
    }

    /**
     * This interface represents a run-time method obtained through reflection.
     *
     * Here are few examples of utilization:[code]
     * // Non-static method: fastMap.put(myKey, myValue)
     * Reflection.Method putKeyValue
     *     = Reflection.getInstance().getMethod(
     *         "javolution.util.FastMap.put(java.lang.Object, java.lang.Object)");
     * Object previous = putKeyValue.invoke(fastMap, myKey, myValue);
     *
     * // Static method: System.nanoTime()  (JRE1.5+)
     * Reflection.Method nanoTime
     *     = Reflection.getInstance().getMethod("java.lang.System.nanoTime()");
     * long time = ((Long)nanoTime.invoke(null)).longValue();[/code]
     */
    public interface Method {

        /**
         * Returns an array of <code>Class</code> objects that represents
         * the formal parameter types, in declaration order of this constructor.
         *
         * @return the parameter types for this constructor.
         */
        Class[] getParameterTypes();

        /**
         * Invokes this method on the specified object which might be
         * <code>null</code> if the method is static (convenience method).
         *
         * @param thisObject the object upon which this method is invoked
         *        or <code>null</code> for static methods.
         * @return the result of the invocation.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 0</code>
         */
        Object invoke(Object thisObject);

        /**
         * Invokes this method with the specified single argument
         * on the specified object which might be <code>null</code>
         * if the method is static (convenience method).
         *
         * @param thisObject the object upon which this method is invoked
         *        or <code>null</code> for static methods.
         * @param arg0 the single argument.
         * @return the result of the invocation.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 1</code>
         */
        Object invoke(Object thisObject, Object arg0);

        /**
         * Invokes this method with the specified two arguments
         * on the specified object which might be <code>null</code>
         * if the method is static (convenience method).
         *
         * @param thisObject the object upon which this method is invoked
         *        or <code>null</code> for static methods.
         * @param arg0 the first argument.
         * @param arg1 the second argument.
         * @return the result of the invocation.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 2</code>
         */
        Object invoke(Object thisObject, Object arg0, Object arg1);

        /**
         * Invokes this method with the specified three arguments
         * on the specified object which might be <code>null</code>
         * if the method is static.
         *
         * @param thisObject the object upon which this method is invoked
         *        or <code>null</code> for static methods.
         * @param arg0 the first argument (convenience method).
         * @param arg1 the second argument.
         * @param arg2 the third argument.
         * @return the result of the invocation.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != 3</code>
         */
        Object invoke(Object thisObject, Object arg0, Object arg1,
                Object arg2);
        /**
         * Invokes this method with the specified arguments
         * on the specified object which might be <code>null</code>
         * if the method is static.
         *
         * @param thisObject the object upon which this method is invoked
         *        or <code>null</code> for static methods.
         * @param args the arguments.
         * @return the result of the invocation.
         * @throws IllegalArgumentException if
         *          <code>this.getParametersTypes().length != args.length</code>
        */
        Object invoke(Object thisObject, Object... args);
        /**/
    }

//////////////////////////////////
// Holds Default Implementation //
//////////////////////////////////
    private static final class Default extends Reflection {

        private final FastMap _fields = new FastMap().shared();

        private final Collection _classLoaders = new FastSet().shared();

        private final FastMap _nameToClass = new FastMap().shared().setKeyComparator(FastComparator.LEXICAL);

        public void add(Object classLoader) {
            _classLoaders.add(classLoader);
        }

        public void remove(Object classLoader) {
            _classLoaders.remove(classLoader);
            _nameToClass.clear(); // Clear cache.
            for (Iterator i = _fields.entrySet().iterator(); i.hasNext();) {
                FastMap.Entry entry = (FastMap.Entry) i.next();
                Class cls = (Class) entry.getKey();
                if (cls.getClassLoader().equals(classLoader)) {
                    _fields.remove(cls); // Remove class and its fields.
                }
            }
        }

        public Class getClass(CharSequence name) {
            Class cls = (Class) _nameToClass.get(name); // First search cache.
            return (cls != null) ? cls : searchClass(name.toString());
        }

        private Class searchClass(String name) {
            Class cls = null;
            try {
                cls = Class.forName(name);
            } catch (ClassNotFoundException e1) {
                /* */
                for (Iterator i = _classLoaders.iterator(); i.hasNext();) {
                ClassLoader classLoader = (ClassLoader) i.next();
                try {
                cls = Class.forName(name, true, classLoader);
                } catch (ClassNotFoundException e2) {
                // Not found, continue.
                }
                }
                /**/
            }
            if (cls != null) { // Cache the result.
                _nameToClass.put(name, cls);
            }
            return cls;
        }

        public Constructor getConstructor(String signature) {
            int argStart = signature.indexOf('(') + 1;
            if (argStart < 0) {
                throw new IllegalArgumentException("Parenthesis '(' not found");
            }
            int argEnd = signature.indexOf(')');
            if (argEnd < 0) {
                throw new IllegalArgumentException("Parenthesis ')' not found");
            }
            String className = signature.substring(0, argStart - 1);
            Class theClass = getClass(className);
            if (theClass == null) {
                return null;
            }
            String args = signature.substring(argStart, argEnd);
            if (args.length() == 0) {
                return new DefaultConstructor(theClass);
            }
            /**/
            Class[] argsTypes = classesFor(args);
            if (argsTypes == null) return null;
            try {
            return new ReflectConstructor(theClass.getConstructor(argsTypes),
            signature);
            } catch (NoSuchMethodException e) {
            }
            /**/
            LogContext.warning("Reflection not supported (Reflection.getConstructor(String)");
            return null;
        }

        public Class[] getInterfaces(Class cls) {
            /**/
            if (true) return cls.getInterfaces();
            /**/
            LogContext.warning("Reflection not supported (Reflection.getInterfaces(Class)");
            return new Class[0];
        }

        public Class getSuperclass(Class cls) {
            /**/
            if (true) return cls.getSuperclass();
            /**/
            LogContext.warning("Reflection not supported (Reflection.getSuperclass(Class)");
            return null;
        }

        // Implements abstract method.
        public Method getMethod(String signature) {
            /**/
            int argStart = signature.indexOf('(') + 1;
            if (argStart < 0) {
            throw new IllegalArgumentException("Parenthesis '(' not found");
            }
            int argEnd = signature.indexOf(')');
            if (argEnd < 0) {
            throw new IllegalArgumentException("Parenthesis ')' not found");
            }
            int nameStart = signature.substring(0, argStart).lastIndexOf('.') + 1;
            try {

            String className = signature.substring(0, nameStart - 1);
            Class theClass = getClass(className);
            if (theClass == null) return null;
            String methodName = signature.substring(nameStart, argStart - 1);
            String args = signature.substring(argStart, argEnd);
            Class[] argsTypes = classesFor(args);
            if (argsTypes == null) return null;
            return new ReflectMethod(theClass.getMethod(methodName, argsTypes),
            signature);
            } catch (Throwable t) {
            }
            /**/
            LogContext.warning("Reflection not supported (Reflection.getMethod(String)");
            return null;
        }

        @Override
        public Object getField(Class forClass, Class type, boolean inherited) {
            ClassInitializer.initialize(forClass);
            return getField2(forClass, type, inherited);
        }
        private Object getField2(Class forClass, Class type, boolean inherited) {
            FastMap typeToField = (FastMap) _fields.get(forClass);
            if (typeToField != null) {
                Object field = typeToField.get(type);
                if (field != null)
                    return field;
            }
            if (!inherited)
                return null;

            // Search direct interfaces.
            Class[] interfaces = getInterfaces(forClass);
            for (int i = 0; i < interfaces.length; i++) {
                Object field = getField2(interfaces[i], type, false);
                if (field != null)
                    return field;
            }

            // Recursion with the parent class.
            Class parentClass = getSuperclass(forClass);
            return (parentClass != null) ? getField2(parentClass, type, inherited) : null;
        }

        @Override
        public void setField(Object obj, Class forClass, Class type) {
            synchronized (forClass) { // We don't want to attach simultaneously to the same class.
                FastMap typeToField = (FastMap) _fields.get(forClass);
                if ((typeToField != null) && typeToField.containsKey(type))
                    throw new IllegalArgumentException("Field of type " + type + " already attached to class " + forClass);
                if (typeToField == null) {
                    typeToField = new FastMap();
                    _fields.put(forClass, typeToField);
                }
                typeToField.put(type, obj);
            }
        }

        ////////////////////////////////
        // Constructor Implementation //
        ////////////////////////////////
        private static abstract class BaseConstructor implements Constructor {

            private final Class[] _parameterTypes;

            protected BaseConstructor(Class[] parameterTypes) {
                _parameterTypes = parameterTypes;
            }

            public Class[] getParameterTypes() {
                return _parameterTypes;
            }

            protected abstract Object allocate(Object[] args);

            public final Object newInstance() {
                if (_parameterTypes.length != 0)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                return allocate(EMPTY_ARRAY);
            }

            public final Object newInstance(Object arg0) {
                if (_parameterTypes.length != 1)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0};
                return allocate(args);
            }

            public final Object newInstance(Object arg0, Object arg1) {
                if (_parameterTypes.length != 2)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0, arg1};
                return allocate(args);
            }

            public final Object newInstance(Object arg0, Object arg1, Object arg2) {
                if (_parameterTypes.length != 3)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0, arg1, arg2};
                return allocate(args);
            }
            /**
            */
            public final Object newInstance(Object... args) {
            if (_parameterTypes.length != args.length)
            throw new IllegalArgumentException(
            "Expected number of parameters is " + _parameterTypes.length);
            return allocate(args);
            }
            /**/
        }

        private static class DefaultConstructor extends BaseConstructor {

            final Class _class;

            DefaultConstructor(Class cl) {
                super(new Class[0]); // No arguments.
                _class = cl;
            }

            public Object allocate(Object[] args) {
                try {
                    return _class.newInstance();
                } catch (InstantiationException e) {
                    throw new RuntimeException("Default constructor instantiation error for " + _class.getName() + " (" + e.getMessage() + ")");
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Default constructor illegal access error for " + _class.getName() + " (" + e.getMessage() + ")");
                }
            }

            public String toString() {
                return _class + " default constructor";
            }
        }

        /**/
        private final class ReflectConstructor extends BaseConstructor {
        private final java.lang.reflect.Constructor _value;

        private final String _signature;

        public ReflectConstructor(java.lang.reflect.Constructor value,
        String signature) {
        super(value.getParameterTypes());
        _value = value;
        _signature = signature;
        }

        public Object allocate(Object[] args) {
        try {
        return _value.newInstance(args);
        } catch (InstantiationException e) {
        throw new RuntimeException("Instantiation error for "
        + _signature + " constructor", e);
        } catch (IllegalAccessException e) {
        throw new RuntimeException("Illegal access error for "
        + _signature + " constructor", e);
        } catch (java.lang.reflect.InvocationTargetException e) {
        if (e.getCause() instanceof RuntimeException)
        throw (RuntimeException)e.getCause();
        throw new RuntimeException("Invocation exception  for "
        + _signature + " constructor",
        (java.lang.reflect.InvocationTargetException) e.getCause());
        }
        }

        public String toString() {
        return _signature + " constructor";
        }
        }
        /**/
        ///////////////////////////
        // Method Implementation //
        ///////////////////////////
        private static abstract class BaseMethod implements Method {

            private final Class[] _parameterTypes;

            protected BaseMethod(Class[] parameterTypes) {
                _parameterTypes = parameterTypes;
            }

            public Class[] getParameterTypes() {
                return _parameterTypes;
            }

            protected abstract Object execute(Object thisObject, Object[] args);

            public final Object invoke(Object thisObject) {
                if (_parameterTypes.length != 0)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                return execute(thisObject, EMPTY_ARRAY);
            }

            public final Object invoke(Object thisObject, Object arg0) {
                if (_parameterTypes.length != 1)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0};
                return execute(thisObject, args);
            }

            public final Object invoke(Object thisObject, Object arg0, Object arg1) {
                if (_parameterTypes.length != 2)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0, arg1};
                return execute(thisObject, args);
            }

            public final Object invoke(Object thisObject, Object arg0, Object arg1,
                    Object arg2) {
                if (_parameterTypes.length != 3)
                    throw new IllegalArgumentException(
                            "Expected number of parameters is " + _parameterTypes.length);
                Object[] args = {arg0, arg1, arg2};
                return execute(thisObject, args);
            }
            /**
            */
            public final Object invoke(Object thisObject, Object... args) {
            if (_parameterTypes.length != args.length)
            throw new IllegalArgumentException(
            "Expected number of parameters is " + _parameterTypes.length);
            return execute(thisObject, args);
            }
            /**/
        }

        /**/
        private final class ReflectMethod extends BaseMethod {

        private final java.lang.reflect.Method _value;

        private final String _signature;

        public ReflectMethod(java.lang.reflect.Method value, String signature) {
        super(value.getParameterTypes());
        _value = value;
        _signature = signature;
        }

        public Object execute(Object that, Object[] args) {
        try {
        return _value.invoke(that, args);
        } catch (IllegalAccessException e) {
        throw new IllegalAccessError("Illegal access error for " + _signature + " method");
        } catch (java.lang.reflect.InvocationTargetException e) {
        if (e.getCause() instanceof RuntimeException)
        throw (RuntimeException) e.getCause();
        throw new RuntimeException(
        "Invocation exception for " + _signature + " method",
        (java.lang.reflect.InvocationTargetException) e.getCause());
        }
        }

        public String toString() {
        return _signature + " method";
        }
        }
        /**/
        /**
         * Returns the classes for the specified argument.
         *
         * @param args the comma separated arguments.
         * @return the classes or <code>null</code> if one of the class is not found.
        */
        private Class[] classesFor(String args) {
        args = args.trim();
        if (args.length() == 0) {
        return new Class[0];
        }
        // Counts commas.
        int commas = 0;
        for (int i=0;;) {
        i = args.indexOf(',', i);
        if (i++ < 0) break;
        commas++;
        }
        Class[] classes = new Class[commas + 1];

        int index = 0;
        for (int i = 0; i < commas; i++) {
        int sep = args.indexOf(',', index);
        classes[i] = classFor(args.substring(index, sep).trim());
        if (classes[i] == null) return null;
        index = sep + 1;
        }
        classes[commas] = classFor(args.substring(index).trim());
        if (classes[commas] == null) return null;
        return classes;
        }

        private Class classFor(String className)  {
        int arrayIndex = className.indexOf("[]");
        if (arrayIndex >= 0) {
        if (className.indexOf("[][]") >= 0) {
        if (className.indexOf("[][][]") >= 0) {
        if (className.indexOf("[][][][]") >= 0) {
        throw new UnsupportedOperationException(
        "The maximum array dimension is 3");
        } else { // Dimension three.
        return getClass("[[["
        + descriptorFor(className.substring(0,
        arrayIndex)));
        }
        } else { // Dimension two.
        return getClass("[["
        + descriptorFor(className.substring(0, arrayIndex)));
        }
        } else { // Dimension one.
        return getClass("["
        + descriptorFor(className.substring(0, arrayIndex)));
        }
        }
        if (className.equals("boolean")) {
        return boolean.class;
        } else if (className.equals("byte")) {
        return byte.class;
        } else if (className.equals("char")) {
        return char.class;
        } else if (className.equals("short")) {
        return short.class;
        } else if (className.equals("int")) {
        return int.class;
        } else if (className.equals("long")) {
        return long.class;
        } else if (className.equals("float")) {
        return float.class;
        } else if (className.equals("double")) {
        return double.class;
        } else {
        return getClass(className);
        }
        }

        private static String descriptorFor(String className) {
        if (className.equals("boolean")) {
        return "Z";
        } else if (className.equals("byte")) {
        return "B";
        } else if (className.equals("char")) {
        return "C";
        } else if (className.equals("short")) {
        return "S";
        } else if (className.equals("int")) {
        return "I";
        } else if (className.equals("long")) {
        return "J";
        } else if (className.equals("float")) {
        return "F";
        } else if (className.equals("double")) {
        return "D";
        } else {
        return "L" + className + ";";
        }
        }
        /**/
    }
    private static final Object[] EMPTY_ARRAY = new Object[0]; // Immutable.

}
