/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Velocity", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.velocity.tools.tools;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;

/**
 * <p>A view tool that allows template designers to load
 * an arbitrary object into the context. Any object
 * with a public constructor without parameters can be used
 * as a view tool.</p>
 * <p>Enhancments were added to allow a tool map to be assigned to the loader
 * when the tool loader is constructed, allow for up to four constructor arguments,
 * and to optionally allow for the use of the class name loading feature.
 *
 * <p>THIS CLASS IS HERE AS A PROOF OF CONCEPT ONLY. IT IS NOT
 * INTENDED FOR USE IN PRODUCTION ENVIRONMENTS. USE AT YOUR OWN RISK.</p>
 *
 * @author <a href="mailto:sidler@teamup.com">Gabe Sidler</a>
 * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
 *
 * @version $Id: ToolLoader.java,v 1.3 2002/05/10 05:42:17 sidler Exp $
 *
 */


public class ToolLoader
{
    /**
     * controls if tools can be loaded via class name
     */
    protected boolean classNameAllowed;

    /**
     * Maps the registered tool names to their class names
     */
    protected Map toolMap;

    public static final Object[] EMPTY_OBJECT_ARRAY = new Object[ 0 ];

    /**
     * Default constructor.
     * For backward compataility, this allows use of class name for tool names
     * with an empty tool name map.
     */
    public ToolLoader()
    {
        this( new HashMap(), true );
    }

    /**
     * Contrstructs a tool loader, initializing it with a tool map and
     * control of class names as tool names.
     * @param toolMap the tool map to use for mapping tool names to class names
     * @param classNameAllowed controls if a class name can be used to load
     * objects as tools.
     */
    public ToolLoader( Map toolMap, boolean classNameAllowed )
    {
        this.toolMap = toolMap;
        this.classNameAllowed = classNameAllowed;
    }

    /**
     * Loads a tool based on the tool name using the default constructor.
     * @param toolName the name of the tool
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     * @see #load( String toolName, Object[] args )
     */
    public Object load( String toolName )
    {
        return load( toolName, EMPTY_OBJECT_ARRAY );
    }

    /**
     * Loads a tool based on the tool name using a constructor with arguments.
     * @param toolName the name of the tool
     * @param arg0 first constructor argument
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     * @see #load( String toolName, Object[] args )
     */
    public Object load( String toolName, Object arg0 )
    {
        return load( toolName, new Object[] { arg0 } );
    }

    /**
     * Loads a tool based on the tool name using a constructor with arguments.
     * @param toolName the name of the tool
     * @param arg0 first constructor argument
     * @param arg1 second constructor argument
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     * @see #load( String toolName, Object[] args )
     */
    public Object load( String toolName, Object arg0, Object arg1 )
    {
        return load( toolName, new Object[] { arg0, arg1 } );
    }

    /**
     * Loads a tool based on the tool name using a constructor with arguments.
     * @param toolName the name of the tool
     * @param arg0 first constructor argument
     * @param arg1 second constructor argument
     * @param arg2 third constructor argument
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     * @see #load( String toolName, Object[] args )
     */
    public Object load( String toolName, Object arg0, Object arg1, Object arg2 )
    {
        return load( toolName, new Object[] { arg0, arg1, arg2 } );
    }

    /**
     * Loads a tool based on the tool name using a constructor with arguments.
     * @param toolName the name of the tool
     * @param arg0 first constructor argument
     * @param arg1 second constructor argument
     * @param arg2 third constructor argument
     * @param arg3 fourth constructor argument
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     * @see #load( String toolName, Object[] args )
     */
    public Object load( String toolName, Object arg0, Object arg1, Object arg2, Object arg3 )
    {
        return load( toolName, new Object[] { arg0, arg1, arg2, arg3 } );
    }

    /**
     * Loads a tool based on the tool name. The tool name is looked up in the
     * tool map. If found, the value of the map is used as the class name of
     * the tool.
     * <p>If the tool name is not found in the tool map and the tool loader is
     * configured to use the tool name as a class name, the toolName will be used
     * as the class name.</p>
     * @param toolName the name of the tool
     * @param args arguments to the contructor of the class for the tool. If null,
     * the default constructor will be used.
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     */
    public Object load( String toolName, Object[] args )
    {
        Object tool = null;

        // lookup the toolname here in the toolmap
        Object mapValue = toolMap.get( toolName );
        if ( mapValue != null && mapValue instanceof String )
        {
            tool = createTool( (String) mapValue, args );
        }
        else if ( tool == null && classNameAllowed )
        {
            // treat toolName as a class name
            tool = createTool( toolName, args );
        }

        return tool;
    }

    /**
     * Creates the tool from the className and arguments.
     * @param the class name of the tool to construct.
     * @param args arguments to the contructor of the class for the tool. If null,
     * the default constructor will be used.
     * @return the tool. A null value is returned if no tool could be found and/or constructed
     */
    protected Object createTool( String className, Object[] args )
    {
        Object tool = null;

        try
        {
            Class toolClass = Class.forName( className );
            if ( args == null || args.length == 0 )
            {
                tool = toolClass.newInstance();
            }
            else
            {
                Constructor constructor = toolClass.getConstructor( createParameterTypes( args ) );
                tool = constructor.newInstance( args );
            }
        }
        catch ( Exception e )
        {
            // do nothing (log error?)
        }

        return tool;
    }

    /**
     * Creates an array of classes for each parameter of the constructor based on
     * the class of each argument.
     * @param args the array of arguments
     * @return an array of parameters types
     */
    protected static Class[] createParameterTypes( Object[] args )
    {
        Class parameterTypes[] = new Class[ args.length ];
        for ( int i = 0; i < args.length; i++ )
        {
            parameterTypes[i] = args[i].getClass();
        }
        return parameterTypes;
    }

    /**
     * Indicates if class names are allowed for tool lookup.
     * @return true, if class names are allowed; false, if not.
     */
    public boolean isClassNameAllowed()
    {
        return this.classNameAllowed;
    }

}

