/*
 * @(#)InputProperties.java    1.0.1  1 June 2001
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.util.*;
import java.io.Serializable;
import java.util.Hashtable;

/**
 * <P>A property list used to store properties 
 * pertaining to input components.</P>
 *
 * <P>There are four bound properties for an input properties object
 * that represent the standard properties for an input component
 * and the parameters it contributes 
 * to a standard error strategy:</P>
 * <UL>
 *     <LI><CODE>INPUT_PROMPT</CODE>
 *     <LI><CODE>DIALOG_TITLE</CODE>
 *     <LI><CODE>INPUT_MODEL</CODE>
 *     <LI><CODE>SUGGESTION</CODE>
 * </UL>
 * <P>This class implements a data structure that can be used 
 * to store any number of input properties of any type,
 * keyed using <CODE>String</CODE> property names.
 *
 * An input properties object can be created so that it shadows
 * the properties stored in a preexisting input properties object
 * or the base input properties object 
 * that contains default property values.
 *
 * Properties with the same name that are stored in 
 * the top level input properties object
 * are returned in favor of lower level properties stored
 * in the shadowed input properties object(s).
 * 
 * Properties stored in the lower level input properties object(s)
 * are returned by the top level input properties object
 * if those properties are not shadowed by higher level properties
 * with the same names.</P>
 *
 * @author  Jeff Raab
 * @version 2.2
 * @since   1.0
 */
public class InputProperties 
    implements JPTConstants, Cloneable, Serializable 
{
    /** 
     * Bound property name for the prompt to be used
     * as a parameter for a standard error strategy
     * if the view state of this input component is malformed.
     */
    public static final String INPUT_PROMPT = "input.prompt";
    
    /** 
     * Bound property name for the dialog box title to be used
     * as a parameter for a standard error strategy
     * if the view state of this input component is malformed.
     */
    public static final String DIALOG_TITLE = "dialog.title";
    
    /**
     * Bound property name for the input model for this view.
     *
     * @see JPTConstants#MANDATORY
     * @see JPTConstants#OPTIONAL
     */
    public static final String INPUT_MODEL = "input.model";
    
    /**
     * Bound property name 
     * for a suggested view state for this view.
     */
    public static final String SUGGESTION = "suggestion";

    /** Shared base property list for input property objects. */
    public static final InputProperties BASE_PROPERTIES = 
        new InputProperties(null);

    // static code executed upon loading of this class
    // by a Java Virtual Machine
    static {
        BASE_PROPERTIES.setProperty(
            INPUT_PROMPT, "Please enter a valid value:");
        BASE_PROPERTIES.setProperty(
            DIALOG_TITLE, "Input");
        BASE_PROPERTIES.setProperty(
            INPUT_MODEL, new Integer(OPTIONAL));
        BASE_PROPERTIES.setProperty(
            SUGGESTION, null);
    }

    /** Hashtable used to map property names to property values. */
    protected Hashtable table = new Hashtable();
    
    /** Property list that this property list shadows. */
    protected InputProperties next = null;
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructor for an empty property list
     * that shadows the shared base property object.
     */
    public InputProperties() {
        next = BASE_PROPERTIES;
    }
    
    /**
     * Constructor for an empty property list 
     * that shadows the given property list.
     *
     * If the given property list is <CODE>null</CODE>,
     * the property list will not shadow another property list. 
     *
     * @param properties the desired property list to shadow
     */
    public InputProperties(InputProperties properties) {
        next = properties;
    }
    
    ////////////////
    // Public API //
    ////////////////

    /**
     * Returns <CODE>true</CODE> if this property list
     * is equivalent to the given object,
     * or <CODE>false</CODE> if it is not.
     *
     * @param other the object to compare with
     */
    public boolean equals(Object other) {
        if (this == other)
            return true;
    
        if (!(other instanceof InputProperties))
            return false;

        if (table == null)
            return false;

        // the conditional below represents these criteria:
        //    a) the maps at this level are equal via
        //       the equals method in Hashtable
        //    b) both environments have valid
        //       next environments that are equal
        //       via recursion
        //    c) neither environment has a valid
        //       next environment
        // in the following fashion:
        //    a * (b + c)
        InputProperties p = (InputProperties)other;
        return (table.equals(p.table)) &&
               (((next != null) && 
                   (p.next != null) &&
                   (next.equals(p.next))) ||
                ((next == null) && 
                   (p.next == null)));
    }

    /**
     * Stores the given property under the provided name.
     *
     * @param propertyName the <CODE>String</CODE> name 
     *      of the property
     * @param property the value of the property 
     *      to be stored in the list
     */
    public void setProperty(String propertyName, Object property) {

        // remove stored property with this name
        if (table.containsKey(propertyName))
            table.remove(propertyName);
        
        // store the given property if appropriate
        if (property != null)
            table.put(propertyName, property);
    }
    
    /**
     * Returns the value of the property with the provided name,
     * or <CODE>null</CODE> if the property has not been set.
     *
     * @param propertyName the <CODE>String</CODE> name
     *      of the desired property value
     */
    public Object getProperty(String propertyName) {
        Object found = table.get(propertyName);
        
        // look in remaining environment if necessary
        if ((found == null) && (next != null))
            found = next.getProperty(propertyName);
        
        return found;    
    }
    
    /**
     * Sets the input prompt text 
     * to the given prompt text.
     *
     * @param inputPrompt the new prompt text
     */
    public void setInputPrompt(String inputPrompt) {
        setProperty(INPUT_PROMPT, inputPrompt);
    }
    
    /**
     * Returns the input prompt text 
     * stored in this property list.
     */
    public String getInputPrompt() {
        return (String)getProperty(INPUT_PROMPT);
    }
    
    /**
     * Sets the dialog title 
     * to the given title text.
     *
     * @param dialogTitle the new title text
     */
    public void setDialogTitle(String dialogTitle) {
        setProperty(DIALOG_TITLE, dialogTitle);
    }
    
    /**
     * Returns the dialog title text
     * stored in this property list.
     */
    public String getDialogTitle() {
        return (String)getProperty(DIALOG_TITLE);
    }
    
    /**
     * Sets the input model value 
     * to the given value.
     *
     * @param inputModel the new input model value
     * @see #MANDATORY
     * @see #OPTIONAL
     */
    public void setInputModel(int inputModel) {
        setProperty(INPUT_MODEL, new Integer(inputModel));
    }
    
    /**
     * Returns the input model value
     * stored in this property list.
     *
     * @see #MANDATORY
     * @see #OPTIONAL
     */
    public int getInputModel() {
        return ((Integer)getProperty(INPUT_MODEL)).intValue();
    }
    
    /**
     * Sets the suggested view state 
     * to the given <CODE>String</CODE> data.
     *
     * @param suggestion the new suggestion text
     */
    public void setSuggestion(String suggestion) {
        setProperty(SUGGESTION, suggestion);
    }
    
    /**
     * Returns the suggested view state
     * stored in this property list.
     */
    public String getSuggestion() {
        return (String)getProperty(SUGGESTION);
    }
}
