/* Title:		Parameter.java
 * Version	
 * Author		Sada
 * Company: USDA-ARS
 * Date:    July, 2003
 * Description: Holds parameter information (name and  value)
 * 
 */
package usda.weru.mcrew;

import java.util.*;

import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.xml.sax.SAXException;

/**
 * Holds parameter information (name and  value) and the methods that are used for
 * data retrieval and manipulation.
 */

public class Parameter implements Cloneable
{
	String mParameterName = "";
    Hashtable mParameter;
   
	/**
     * Default constructor tha builds the object initializing some data structures 
     * which holds the required values.
     */
    public Parameter()
	{
		mParameterName = "";
		mParameter = new Hashtable();
	}
    
    /**
     * Single parameter constructor that takes in the name of the parameter as 
     * its argument.
     * @param pName The name of the parameter.
     */    
	public Parameter(String pName)
	{
		mParameterName = pName;
		mParameter = new Hashtable();
	}
    
    /**
     * Two parameter constructor that takes in the name of the parameter and the
     * value to be assigned to it as its arguments.
     * @param pName The name of the parameter.
     * @param pValue The value to be assigned to that parameter.
     */    
	public Parameter(String pName, Object pValue)
	{
		mParameter = new Hashtable();
		mParameter.put(XMLConstants.sname, pName);
		mParameter.put(XMLConstants.svalue, pValue);
	}
  
    /**
     * Getter method that fetches the object associated with the parameter as its value.
     * @return The object as the parameter value from the hashtable that stores it 
     * based on parameter's name as its key.
     */    
  public Object getValue()
	{
		if(mParameter != null)
			return mParameter.get(XMLConstants.svalue);
		return null;
	}
  
  /**
   * This function can be used to set the value of the parameter 
   * @param pVal The new value to be set as a replacement value.
   */
  public void setValue(Object pVal)
  {
  	if(mParameter != null){
		 mParameter.remove(XMLConstants.svalue); // remove old value
                 mParameter.put(XMLConstants.svalue, pVal);
        }				
  }
  
  /**
   * Fetches the value associated with the name attribute of the parameter
   * @return The name of the parameter.
   */  
	public String getName()
	{
		if(mParameter != null){
                    String param = (String)mParameter.get(XMLConstants.sname);
                    ////System.out.println( " Parameter : getName() : " + param);
                    return param;
                }
		return null;
	}
	
    /**
     * Gets the value of the parameter pName stored in the vector as objects.
     * @param pName The argument pName is 'name' or 'value' under which the objects are stored
     * @return The value stored as string object.
     */    
	public String get(String pName)
	{
		if(mParameter != null)
		{
			if( pName.equals(XMLConstants.sparamname))
				return (String)mParameter.get(XMLConstants.sname);
		
			return (String)mParameter.get(pName); //the argument pName is 'name' or 'value' under which the objects are also stored
		}
		return null;
	}
		
    /**
     * This method initializes by retrieving the node info for the parameters
     * and populating the data structures like hashtables with the node name as key 
     * and the text data from the XML file as their data values.
     * @param pNode The node whose data need to be initialized/updated.
     */    
	public void initialize(Node pNode)
	{
		/* Parameters are stored this way
			KEY 			OBJECT
			"name"		"killflg"
			"value"		2
			i.e there are 2 entries in the mParameter table corresponding to the <param> entity in the data XML files
		*/
		DocumentTraversal traversable;
		
		if(pNode.getOwnerDocument() == null)
		{
			traversable = (DocumentTraversal)pNode;	
		}
		else
			traversable = (DocumentTraversal)pNode.getOwnerDocument();

		TreeWalker walker = traversable.createTreeWalker(pNode,NodeFilter.SHOW_ALL, null, false);

		Node nodeChild = walker.firstChild();
		while(nodeChild != null)
		{
			String nodeChildName = nodeChild.getNodeName();
			String nodeChildData = XMLDoc.getTextData(nodeChild);
                       // //System.out.println("Parameter :initialize()" + " nodeChildName = " + nodeChildName + ", nodeChildData = " + nodeChildData );
			
			if( ( nodeChildName != null) && ( nodeChildData != null) )
				mParameter.put(nodeChildName, nodeChildData);
		
			/*Node textNode = walker.firstChild();
			String nodeChildData = "";
			if(textNode != null) 
				nodeChildData = textNode.getNodeValue();
			mParameter.put(nodeChildName, nodeChildData);

			walker.parentNode(); */
			nodeChild = walker.nextSibling();
		}
		
	}

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Parameter other = (Parameter) obj;
        if (this.mParameterName != other.mParameterName && (this.mParameterName == null || !this.mParameterName.equals(other.mParameterName))) {
            return false;
        }
        if (this.mParameter != other.mParameter && (this.mParameter == null || !this.mParameter.equals(other.mParameter))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 17 * hash + (this.mParameterName != null ? this.mParameterName.hashCode() : 0);
        return hash;
    }
	
    /**
     * This method initializes by retrieving the node info for the parameters
     * and populating the data structures like hashtables with the node name as key 
     * and the text data from the XML file as their data values.
     * @param pName The name of the parameter whose value is to be initialised.
     * @param pValue The value to be set during initializarion of the parameter
     * with name pName.
     */    
	public void initialize(String pName, String pValue)
	{
		mParameter.put(XMLConstants.sname, pName);
		mParameter.put(XMLConstants.svalue, pValue);
	}
	
    /**
     * This method is used to change the values of the parameter to whatever value
     * pParam is set to.
     * @param pParam The parameter with new set of values to be assigned.
     */    
	public void changeValue(Parameter pParam)
	{
		String newValue = pParam.get(XMLConstants.svalue);
		if(newValue != null)
		{	mParameter.remove(XMLConstants.svalue);
			mParameter.put(XMLConstants.svalue, newValue);
		}
		else
                {}	//System.err.println("Parameter:changeValue()->" +"Theres no value for parameter "+ mParameter.get(XMLConstants.sname));
	}
	
    /**
     * Fetches the parameter node by creating a name and a valu node for each parameter 
     * and finally appending it to the document as a new node.
     * @param doc The document that the node should be appended to
     * @return The new parameter node that was just created.
     */    
	public Node getNode(Document doc)
	{
		Node paramNode = doc.createElement(XMLConstants.sparam);
		
		if(mParameter == null) return paramNode;
		
		Node nameNode = doc.createElement(XMLConstants.sname);
		String name = (String)mParameter.get(XMLConstants.sname);
		XMLDoc.setTextData(nameNode, name, doc); // set the name as text node child of nameNode
		
		Node valueNode = doc.createElement(XMLConstants.svalue);
		String value = (String)mParameter.get(XMLConstants.svalue);
		XMLDoc.setTextData(valueNode, value, doc);
		
		paramNode.appendChild(nameNode);
		paramNode.appendChild(valueNode);
		
		return paramNode;
	}

    /**
     * Provides with a carbon copy of the existing parameter object.
     * @return The new copy of the parameter object.
     */    
	public Object clone()
	{
		try{
			Parameter copy = (Parameter)super.clone();
			//copy.mParameterName = (String)mParameterName.clone(); // wont work as clone() in Object is protected
			copy.mParameterName = mParameterName;
			// copy.mParameter = (Hashtable)mParameter.clone(); // This just creates a shallow copy of the Hashtable
			copy.mParameter = new Hashtable();
		
			Enumeration e = mParameter.keys();
			while(e.hasMoreElements())
			{
				String key = (String)e.nextElement();
				String value = (String)mParameter.get(key);
				
				String keyCopy = key;
				String valueCopy = value;
				copy.mParameter.put(keyCopy, valueCopy);
			}
			return copy;
		}
		catch(CloneNotSupportedException e ) 
		{ 
			//System.err.println("Parameter:clone():" + "Clone Not supported!");
      
    } 
		return null;
	}
	
}
