/*
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2005-2005 Julian Hyde
// Copyright (C) 2005-2012 Pentaho
// All Rights Reserved.
*/

package mondrian.util;

import org.apache.commons.collections.iterators.EnumerationIterator;

import java.io.IOException;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;

/**
 * Instantiates a class.
 *
 * <p>Has same effect as calling {@link Class#forName(String)},  but uses the
 * appropriate {@link ClassLoader}.</p>
 */
public interface ClassResolver {

    /** Equivalent of {@link Class#forName(String, boolean, ClassLoader)}. */
    <T> Class<T> forName(String className, boolean initialize)
        throws ClassNotFoundException;

    /**
     * Instantiates a class and constructs an instance using the given
     * arguments.
     *
     * @param className Class name
     * @param args Arguments
     * @param <T> Desired type
     * @throws ClassCastException if resulting object is not an instance of T
     */
    <T> T instantiateSafe(String className, Object... args);

    /** Equivalent of {@link ClassLoader#getResources(String)}. */
    Iterable<URL> getResources(String lookupName) throws IOException;

    /** Default resolver. */
    ClassResolver INSTANCE = new ThreadContextClassResolver();

    /** Implementation of {@link ClassResolver} that calls
     * {@link Thread#getContextClassLoader()} on the current thread. */
    class ThreadContextClassResolver extends AbstractClassResolver {
        protected ClassLoader getClassLoader() {
            return Thread.currentThread().getContextClassLoader();
        }
    }

    /** Partial implementation of {@link ClassResolver}. Derived class just
     * needs to implement {@link #getClassLoader()}. */
    abstract class AbstractClassResolver implements ClassResolver {
        public <T> T instantiateSafe(String className, Object... args) {
            try {
                final Class<T> clazz = forName(className, true);
                if (args.length == 0) {
                    return clazz.newInstance();
                } else {
                    Class[] types = new Class[args.length];
                    for (int i = 0; i < args.length; i++) {
                        types[i] = args[i].getClass();
                    }
                    final Constructor<T> constructor =
                        clazz.getConstructor(types);
                    return constructor.newInstance(args);
                }
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        public <T> Class<T> forName(String className, boolean initialize)
            throws ClassNotFoundException
        {
            //noinspection unchecked
            return (Class<T>) Class.forName(
                className,
                initialize,
                getClassLoader());
        }

        /** Returns the class loader to use for the current operation. May be
         * null. */
        protected abstract ClassLoader getClassLoader();

        /** Returns the class loader to use for the current operation, never
         * null. */
        protected ClassLoader getClassLoaderNotNull() {
            final ClassLoader classLoader = getClassLoader();
            return classLoader != null
                ? classLoader
                : getClass().getClassLoader();
        }

        public Iterable<URL> getResources(String name) throws IOException {
            final Enumeration<URL> resources =
                getClassLoaderNotNull().getResources(name);
            //noinspection unchecked
            return new IteratorIterable<URL>(
                new EnumerationIterator(resources));
        }
    }
}

// End ClassResolver.java
