/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.tools.guiframework.event.handlers;

import com.iplanet.jato.RequestContext;

import com.sun.enterprise.tools.guiframework.view.HandlerContext;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 *
 */
public class AttributeHandlers {

    /**
     *	<p> This method retrieves the object stored in an request attribute
     *	    under the given key.  If the object does not exist, null will be
     *	    returned.  It requires that a parameter named KEY be passed in via
     *	    the parameter Map.</p>
     *
     *	@param	reqCtx		The RequestContext
     *	@param	handlerCtx	The HandlerContext
     */
    public void getAttribute(RequestContext reqCtx, HandlerContext handlerCtx) {
    	Object key = handlerCtx.getInputValue(KEY);
	if (key == null) {
	    throw new IllegalArgumentException(
		"The parameter map did not contain a key!");
	}
	handlerCtx.setOutputValue(
	    VALUE, reqCtx.getRequest().getAttribute(key.toString()));
    }


    /**
     *	This method stores the given value in an attribute.  The key under
     *	which the value is to be stored must be specified in the parameter Map
     *	under KEY. 
     *
     *	@param	reqCtx		The RequestContext
     *	@param	handlerCtx	The HandlerContext
     */
    public void setAttribute(RequestContext reqCtx, HandlerContext handlerCtx) {
    	Object key = handlerCtx.getInputValue(KEY);
    	Object value = handlerCtx.getInputValue(VALUE);
	if (key == null) {
	    throw new IllegalArgumentException(
		"The parameter map did not contain a key!");
	}
	reqCtx.getRequest().setAttribute(key.toString(), value);
    }


    /**
     *	This method retrieves the value from a List within an attribute.  It
     *	requires a parameter named KEY be passed in via the parameter Map to
     *	specify the request attribute.  It also requires a parameter named
     *	INDEX that specifies the index within the List to attempt to
     *	retrieve.  This Mapping supports List types as well as array types.
     *	The index may be specified as a Integer or String.
     *
     *	@param	reqCtx		The RequestContext
     *	@param	handlerCtx	The HandlerContext
     */
    public void getListElement(RequestContext reqCtx, HandlerContext handlerCtx) {
    	// Get the key
    	Object key = handlerCtx.getInputValue(KEY);
	if (key == null) {
	    throw new IllegalArgumentException(
		"The parameter map did not contain 'key'!");
	}

    	// Get the index
	int index = getIndex((Integer)handlerCtx.getInputValue(INDEX));

	// Get the List (or array)
	Object attr = reqCtx.getRequest().getAttribute(key.toString());
	if (attr == null) {
	    throw new IllegalArgumentException(
		"No request attribute is stored under the key: '"+key+"'.");
	}

	// Check for "List"
	if (attr instanceof List) {
	    // Set the output value
	    handlerCtx.setOutputValue(VALUE, ((List)attr).get(index));
	    return;
	}

	// Check for an array
	if (attr.getClass().isArray()) {
	    // Return the value
	    handlerCtx.setOutputValue(VALUE, Array.get(attr, index));
	    return;
	}

	// Not an array or List, throw an exception
	throw new IllegalArgumentException(
	    "Value stored under request attribute '"+key+
	    "' is not an array or List!");
    }


    /**
     *	This method stores the given value in an array or List within an
     *	attribute.  The attribute key and the array index must be specified in
     *	the parameter Map under KEY and INDEX respectively.  The List or
     *	array will be extended to accomodate INDEX if INDEX is greater than
     *	the current last index.  If there is no array or List stored under the
     *	specified attribute, then a new List will be created.
     *
     *	@param	reqCtx	The RequestContext
     *	@param	handlerCtx	The HandlerContext
     */
    public void setListElement(RequestContext reqCtx, HandlerContext handlerCtx) {
    	Object value = handlerCtx.getInputValue(VALUE);
    	// Get the key
    	Object key = handlerCtx.getInputValue(KEY);
	if (key == null) {
	    throw new IllegalArgumentException(
		"The parameter map did not contain 'key'!");
	}

    	// Get the index
	int index = getIndex((Integer)handlerCtx.getInputValue(INDEX));

	// Get the List (or array)
	Object list = reqCtx.getRequest().getAttribute(key.toString());
	if (list == null) {
	    list = new ArrayList(index);
	}

	// Check for List...
	if (list instanceof List) {
	    int len = ((List)list).size();
	    if (index >= len) {
	    	// In this case we are growing the List
		// 1st make sure the List is 1 too short by adding null's
		Object val = null;
		while (index > len) {
		    try {
			try {
			    ((List)list).add(val);
			} catch (NullPointerException ex) {
			    // Some Lists don't support 'null', try ""
			    val = "";
			    ((List)list).add(val);
			}
		    } catch (ClassCastException ex2) {
		    	// Some Lists may require certain types... assume that
			// the 'value' is of the right type and use it
			val = value;
			((List)list).add(val);
		    }
		    len++;
		}

		// Add 1 more value (the one we want)
		((List)list).add(value);
	    } else {
	    	// In this case we are replacing a value
		((List)list).set(index, value);
	    }

	// Check for array...
	} else if (list.getClass().isArray()) {
	    // Make sure index is valid
	    int len = Array.getLength(list);
	    Class type = list.getClass().getComponentType();
	    if (index >= len) {
		// index is out of bounds, grow array
		Object newList = Array.newInstance(type, index+1);

		// Copy values from old array to new array
		for (int count=0; count<len; count++) {
		    Array.set(newList, count, Array.get(list, count));
		}

		// Use the new (bigger) list
		list = newList;
	    }

	    // Set the value in the array
	    try {
		Array.set(list, index, value);
	    } catch (Exception ex) {
		throw new RuntimeException(
		    "Unable to set '"+value+"' to array!", ex);
	    }
	} else {
	    throw new IllegalArgumentException(
		"Value stored in request attribute '"+key+
		"' is not an array or List: '"+list.getClass()+"'.");
	}

	// Call set setAttribute in case we created in the List or array
	reqCtx.getRequest().setAttribute(key.toString(), list);
    }


    /**
     *	This method returns the int index value given an Integer
     *	representation of the desired int.  If unable to do so, an
     *	IllegalArgumentException may be thrown.
     *
     *	@param	idx	The index object to convert to an int
     *
     *	@param The value of 'idx' as an int
     */
    private int getIndex(Integer idx) {
	if (idx == null) {
	    throw new IllegalArgumentException(
		"The parameter map did not contain 'index'!");
	}
	int index = ((Integer)idx).intValue();

	// Make sure the index is not negative
	if (index < 0) {
	    throw new IllegalArgumentException(
		"'index' must be non-negative (received '"+index+"').");
	}

	// Return the value
	return index;
    }

    /**
     *	This method calls a particular method of a given object.
     *
     *	@param	reqCtx	The RequestContext
     *	@param	handlerCtx	The HandlerContext
     *
     */    
    public String callMethod(RequestContext reqCtx, HandlerContext handlerCtx) {
    	Object obj = handlerCtx.getInputValue(OBJECT);
        String methodName = (String)handlerCtx.getInputValue(METHOD);
        if (obj == null || methodName == null) {
            throw new IllegalArgumentException("Null parameter(s) sent to 'callMethod'.");
        }
        Method method = null;
        try {
            method = obj.getClass().getMethod(methodName, (Class[])null);
        } catch (Exception ex) {
            throw new RuntimeException("Method not found: " + methodName, ex);
        }
        Object value = obj;
        try {
            value = method.invoke(obj, (Object[])null);
        } catch (Exception ex) {
            throw new RuntimeException("Error calling: " + methodName, ex);
        }
        handlerCtx.setOutputValue(VALUE, value);
        if (value != null)
            return value.toString();
        else 
            return "";
    }

    /**
     *
     */
    public static final String KEY	= "key";
    public static final String VALUE	= "value";
    public static final String INDEX	= "index";
    public static final String OBJECT   = "object";
    public static final String METHOD   = "method";
}
