/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-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 acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *     "Apache Jetspeed" 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" or
 *    "Apache Jetspeed", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * 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.jetspeed.modules;

// jetspeed stuff
import org.apache.jetspeed.modules.parameters.ParameterPresentationStyle;
import org.apache.jetspeed.modules.parameters.ParameterPresentationStyleFactory;
import org.apache.jetspeed.services.resources.JetspeedResources;

// Java Core Classes
import java.util.Vector;
import java.util.Map;
import java.util.Hashtable;
import java.util.Iterator;

// Turbine Utility Classes
import org.apache.turbine.modules.GenericLoader;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.assemblerbroker.AssemblerBrokerService;
import org.apache.turbine.util.RunData;
import org.apache.turbine.util.Log;

/**
 * The purpose of this class is to allow one to load and execute
 * Parameter modules.
 *
 * @author <a href="mailto:mark_orciuch@ngsltd.com">Mark Orciuch</a>
 * @version $Id: ParameterLoader.java,v 1.2 2002/11/27 17:56:38 morciuch Exp $
 */
public class ParameterLoader extends GenericLoader
{
    /**
     * The single instance of this class.
     */
    private static ParameterLoader instance = new ParameterLoader(JetspeedResources.getInt("parameter.cache.size", 50));

    /**
     * Logging verbosity level for debug statements (the higher the more output)
     */
    private static int verbosity = JetspeedResources.getInt("parameter.verbosity", 1);

    /**
     * These ctor's are private to force clients to use getInstance()
     * to access this class.
     */
    private ParameterLoader() {

        super();
    }

    /**
     * These ctor's are private to force clients to use getInstance()
     * to access this class.
     * 
     * @param i
     */
    private ParameterLoader(int i) {
        super(i);
    }

    /**
     * Adds an instance of an object into the hashtable.
     * 
     * @param name   Name of object.
     * @param param
     */
    private void addInstance(String name, ParameterPresentationStyle param) {

        if (cache()) {
            this.put(name, (ParameterPresentationStyle)param );
        }
    }

    /**
     * Attempts to load and render a parameter using custom style. For example, one might define
     * a custom parameter presentation style TextArea which displays current value of the parameter
     * using HTML text area presentation. Assuming that TextArea is rendered using two optional
     * parameters: rows and cols, the map passed to this method could contain the following values:
     * <li>symbols.style.rows = 5
     * <li>symbols.style.cols = 80
     * and the call might look like this:
     *<p>
     * String symbols = eval(data, "TextArea", "symbols", "MSFT,SUNW,EMC,ORCL", parms);
     * 
     * @param data     Turbine information.
     * @param provider Custom parameter class name (without the package)
     * @param name     Name for rendered HTML tag
     * @param value    Current value
     * @param parms    Optional rendition parameters
     * @return 
     * @exception Exception a generic exception.
     */
    public String eval(RunData data, String provider, String name, String value, Map parms) throws Exception {

        // Execute parameter
        ParameterPresentationStyle prm = getInstance(provider);

        // Filter out style params
        Map styleparms = extractStyleParameters(parms, name);
        prm.setParms(styleparms);

        return prm.getContent(data, name, value, styleparms);

    }

    /**
     * This method is not used.
     *
     * @param data Turbine information.
     * @param name Name of object that will execute the screen.
     * @exception Exception a generic exception.
     */
    public void exec(RunData data, String name) throws Exception {

        //this.eval(data, name);
    }

    /**
     * Pulls out an instance of the object by name.  Name is just the
     * single name of the object.
     * 
     * @param provider   Name of object instance.
     * @return A Screen with the specified name, or null.
     * @exception Exception a generic exception.
     */
    public ParameterPresentationStyle getInstance(String provider) throws Exception {

        ParameterPresentationStyle prm = null;

        // Check if the parameter is already in the cache
        if (cache() && this.containsKey(provider)) {

            prm = (ParameterPresentationStyle) this.get(provider);
            if (verbosity > 0 && Log.getLogger().isDebugEnabled()) {
                Log.debug("ParameterLoader: Serving parameter: "+provider+", prm="+prm+" from cache");            
            }

        } else {

            // We get the broker service
            AssemblerBrokerService ab =
                (AssemblerBrokerService)TurbineServices.getInstance()
                .getService (AssemblerBrokerService.SERVICE_NAME);

            try {
                // Attempt to load the presentation style
                prm = (ParameterPresentationStyle)ab.getAssembler("parameter", provider);
                if (prm == null) {
                    if (verbosity > 0 && Log.getLogger().isDebugEnabled()) {
                        Log.debug("ParameterLoader: Registering the factory");
                    }
                    ab.registerFactory("parameter", new ParameterPresentationStyleFactory());
                    prm = (ParameterPresentationStyle)ab.getAssembler("parameter", provider);
                }
                if (verbosity > 0 && Log.getLogger().isDebugEnabled()) {
                    Log.debug("ParameterLoader: Loaded parameter: "+provider+", prm="+prm);
                }
            } catch (ClassCastException cce) {
                prm = null;
            }

            if (prm == null) {
                // If we did not find a screen we should try and give
                // the user a reason for that...
                // FIX ME: The AssemblerFactories should each add it's own string here...
                Vector packages = JetspeedResources.getVector("module.packages");

                throw new ClassNotFoundException( "\n\n\tRequested Parameter not found: " +
                                                  provider + "\n" +
                                                  "\tTurbine looked in the following modules.packages path: \n\t" +
                                                  packages.toString() + "\n");
            } else if(cache()) {

                addInstance(provider, prm);
            }

        }

        return prm;
    }

    /**
     * The method through which this class is accessed.
     * 
     * @return The single instance of this class.
     */
    public static ParameterLoader getInstance() {

        return instance;
    }

    /**
     * Extracts any parameters to parameter style.
     * 
     * @param parms  portlet parameters
     * @param parm   parameter name
     * @return hashtable of optional parameters for the style
     */
    public static Map extractStyleParameters(Map parms, String parmName) {

        Hashtable result = new Hashtable();

        if (parms != null) {
            String key = parmName.concat(".style.");
            Iterator it = parms.keySet().iterator();
            while (it.hasNext()) {
                String parmkey = (String)it.next();
                if (parmkey.startsWith(key)) {
                    try {
                        String stylekey = parmkey.substring(parmkey.lastIndexOf(".")+1);
                        if (verbosity > 1 && Log.getLogger().isDebugEnabled())
                        {
                            Log.debug("ParameterLoader: parm name [" + parmName + "] - storing option [" + stylekey + 
                                      "] with value [" + parms.get(parmkey) + "]");
                        }
                        result.put(stylekey, parms.get(parmkey));
                    } catch (Exception e) {
                        Log.error(e);
                    }
                }
            }
        }

        return result;
    }

}
