//*****************************************************************************
/*
** FILE:   xg_AttList.java
**
** (c) 1998 Steve Withall.
**
** HISTORY:
**    31May98  stevew  Created, with most code extracted from xg_Element.
**    25Jul98  stevew  Implemented MutableAttributeSet (with some code copied
**                      from SimpleAttributeSet).
*/
package xg;

import xa.xa_Keyword;

//TBD See comments below.
//import org.w3c.dom.Attribute;
//import org.w3c.dom.AttributeList;
//import org.w3c.dom.NoSuchAttributeException;

import com.sun.java.swing.text.AttributeSet;
import com.sun.java.swing.text.MutableAttributeSet;

import java.io.IOException;
import java.io.Writer;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

//*****************************************************************************
/** <p>A list of xg_Attributes.</p>
 *
 *  <p>Note that this class implements all the methods of the DOM
 *  org.w3c.dom.AttributeList interface, but since the rest of xg does not,
 *  references to the DOM interfaces is commented out to allow xg to run
 *  without the DOM interfaces being present.</p>
 */
public class xg_AttList implements MutableAttributeSet
//                                   , AttributeList  //TBD
{
    /** Sequential list of xg_Attributes, each with a name and a value. */
    protected Vector        AttributeVector    = new Vector();

    /** Table of xg_Attributes, each with a name and a value. */
    protected Hashtable     AttributeHashtable = new Hashtable();

    /** The parent attribute set to be used to locate attributes if they do not
     *  exist in this set. */
    protected AttributeSet  ResolveParent      = null;

    //*****************************************************************************
    /** Default constructor.
     */
//    public xg_AttList()
//    {
//    }

    //*****************************************************************************
    /** Save this attribute list (ie. its children) in XML source form in InputWriter.
     *
     *  @param  InputWriter   The writer to which the XML will be written
     */
    public void save(Writer  InputWriter) throws IOException
    {
        // Save the attributes (if there are any).
        xg_Attribute  CurrentAttribute = null;
        for (int AttIndex = 0; AttIndex < AttributeVector.size();  AttIndex++)
        {
            CurrentAttribute = getAtt(AttIndex);
            CurrentAttribute.save(InputWriter);  // Save the current attribute
        }
    }

    //*****************************************************************************
    /** Create a new xg_AttList which is exactly the same as this one.
     *
     *  @return  A duplicate attribute list
     */
    public xg_AttList duplicate()
    {
        xg_AttList  NewAttList = new xg_AttList();

        // Duplicate each attribute (if there are any).
        xg_Attribute  ExistingAttribute   = null;
        xg_Attribute  DuplicatedAttribute = null;
        for (int AttIndex = 0; AttIndex < AttributeVector.size(); AttIndex++)
        {
            ExistingAttribute   = getAtt(AttIndex);
            DuplicatedAttribute = ExistingAttribute.duplicate();
            NewAttList.addAttribute(DuplicatedAttribute);
        }

        return NewAttList;
    }

    //*****************************************************************************
    /** Add an attribute to this att list corresponding to each attribute in the
     *  InputAttList.
     *
     *  @param  InputAttList  A duplicate attribute list
     */
    public void duplicateFrom(xg_AttList  InputAttList)
    {
        xg_Attribute  ExistingAttribute   = null;
        xg_Attribute  DuplicatedAttribute = null;
        for (int AttIndex = 0; AttIndex < InputAttList.getLength(); AttIndex++)
        {
            ExistingAttribute   = InputAttList.getAtt(AttIndex);
            DuplicatedAttribute = ExistingAttribute.duplicate();
            addAttribute(DuplicatedAttribute);
        }
    }

    //*****************************************************************************
    /** Clones this set of attributes.
     *
     *  @return  The new set of attributes
     */
    public Object clone()
    {
        return(duplicate() );
    }

    //*****************************************************************************
    /** Add an attribute, given a DOM attribute. Since a DOM attribute does not
     *  contain everything an xg_Attribute does, we create a new xg_Attribute and
     *  add that to our list. If an attribute with this name already exists, it is
     *  replaced with the new version - and is returned.
     *  (Implements org.w3c.dom.AttributeList.setAttribute().)
     *
     *  @param  InputAtt  The attribute to add
     *  @return           The previous attribute with the same name (or null if
     *                     there wasn't one)
     */
/*    public Attribute setAttribute(Attribute  InputAtt)
    {
        xg_Attribute  ExistingAtt = getAtt(InputAtt.getName() );
        if (ExistingAtt == null)
            addAttribute(new xg_Attribute(InputAtt.getName(), InputAtt.getValue() ) );
        else
        {
            xg_Attribute  InterimAtt = ExistingAtt.duplicate();
            ExistingAtt.setName(InputAtt.getName() );
            ExistingAtt.setValue(InputAtt.getValue() );
            ExistingAtt = InterimAtt;
        }
        return ExistingAtt;
    }
*/
    //*****************************************************************************
    /** Add an attribute, given its name and value.
     *
     *  @param  InputAttName   The name of this attribute
     *  @param  InputAttValue  The value of this attribute
     *  @return  Flag indicating success (true) or failure. Failure only occurs if
     *            an attribute with this name already exists (which is an XML
     *            well-formedness error).
     */
  public boolean addAttribute(String  InputAttName,
                                String  InputAttValue)
    {
        return (addAttribute(new xg_Attribute(InputAttName, InputAttValue) ) );
    }

    //*****************************************************************************
    /** Add an attribute.
     *
     *  @param   InputAttribute  The attribute to be added
     *  @return  Flag indicating success (true) or failure. Failure only occurs if
     *            an attribute with this name already exists (which is an XML
     *            well-formedness error).
     */
    public boolean addAttribute(xg_Attribute  InputAttribute)
    {
        if (AttributeHashtable.containsKey(InputAttribute.getName() ) )
            // There is already an attribute with this name.
            return false;

        AttributeVector.addElement(InputAttribute);
        AttributeHashtable.put(InputAttribute.getName(), InputAttribute);
        return true;
    }

    //*****************************************************************************
    /** Remove the named attribute.
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return                The attribute removed
     */
    public xg_Attribute removeAttribute(String InputAttName)
    {
        xg_Attribute  RemovedAtt = (xg_Attribute)AttributeHashtable.remove(InputAttName);
        if (RemovedAtt != null)
            AttributeVector.removeElement(RemovedAtt);
        return RemovedAtt;
    }

    //*****************************************************************************
    /** Remove the named attribute.
     *  (Implements org.w3c.dom.AttributeList.remove().)
     *
     *  @param      InputAttName              The name of the attribute required
     *  @return                               The attribute removed
     *  @exception  NoSuchAttributeException  There is no such attribute.
     */
/*  public Attribute remove(String InputAttName)  throws NoSuchAttributeException
    {
        Attribute  RemovedAtt = (Attribute)removeAttribute(InputAttName);
        if (RemovedAtt == null)
            throw(new NoSuchAttributeException() );
        return RemovedAtt;
    }
*/
    //*****************************************************************************
    /** Get the attribute with the given index.
     *
     *  @param      InputAttIndex  The index of the attribute required
     *  @return                    The attribute
     */
    public xg_Attribute  getAtt(int  InputAttIndex)
    {
        return((xg_Attribute)AttributeVector.elementAt(InputAttIndex));
    }

    //*****************************************************************************
    /** Get the attribute with the given index.
     *  (Implements org.w3c.dom.AttributeList.item().)
     *
     *  @param      InputAttIndex             The index of the attribute required
     *  @return                               The attribute
     *  @exception  NoSuchAttributeException  There is no such attribute.
     */
/*  public Attribute  item(int  InputAttIndex)  throws NoSuchAttributeException
    {
        xg_Attribute  FoundAtt = (xg_Attribute)AttributeVector.elementAt(InputAttIndex);
        if (FoundAtt == null)
            throw(new NoSuchAttributeException() );
        return((Attribute)FoundAtt);
    }
*/
    //*****************************************************************************
    /** Get the value of a named attribute.
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return  The value of the named attribute (or null if there is no attribute
     *            with this name).
     */
    public String getAttributeValue(String  InputAttName)
    {
        String        AttValue = null;
        xg_Attribute  Att      = getAtt(InputAttName);
        if (Att != null)
            AttValue = Att.getValue();
        return AttValue;
    }

    //*****************************************************************************
    /** Get a named attribute.
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return  The named attribute (or null if there is no attribute with this name).
     */
    public xg_Attribute getAtt(String  InputAttName)
    {
        return (xg_Attribute)AttributeHashtable.get(InputAttName);
    }

    //*****************************************************************************
    /** Get a named attribute. (Implements org.w3c.dom.AttributeList.getAttribute().)
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return  The named attribute (or null if there is no attribute with this name)
     */
/*  public Attribute getAttribute(String  InputAttName)
    {
        return(Attribute)AttributeHashtable.get(InputAttName);
    }
*/
    //*****************************************************************************
    /** Get the number of attributes in this list.
     *  (Implements org.w3c.dom.AttributeList.getLength().)
     *
     *  @return  The number of attributes in this list
     */
    public int getLength()
    {
        return(AttributeVector.size() );
    }

    //*****************************************************************************
    //*Methods*implementing*swing.text.AttributeSet********************************
    //*****************************************************************************
    /** Get the number of attributes in this set.
     *
     *  @return  The number of attributes in this set
     */
    public int getAttributeCount()
    {
        return(AttributeVector.size() );
    }

    //*****************************************************************************
    /** Check whether the named attribute has a value specified in the set, without
     *  resolving through another attribute set.
     *
     *  @param   attrName the attribute name
     *  @return  true if the attribute has a value specified
     */
    public boolean isDefined(Object attrName)
    {
    	return(AttributeHashtable.containsKey(attrName) );
    }

    //*****************************************************************************
    /** Determine if the two attribute sets are equivalent.
     *
     *  @param   attr an attribute set
     *  @return  true if the sets are equivalent
     */
    public boolean isEqual(AttributeSet attr)
    {
	    return(    (getAttributeCount() == attr.getAttributeCount() )
		        && containsAttributes(attr) );
    }

    //*****************************************************************************
    /** Return an attribute set that is guaranteed not to change over time.
     *
     *  @return a copy of the attribute set
     */
    public AttributeSet copyAttributes()
    {
	    return (AttributeSet)clone();
    }

    //*****************************************************************************
    /** Get the value of the given attribute. If the value is not found locally,
     *  the search is continued upward through the resolving parent (if one exists)
     *  until the value is either found or there are no more parents. If the value
     *  is not found, null is returned.
     *
     *  @param   InputAttName  The name of the required attribute
     *  @return                The value of the requested attribute
     */
    public Object getAttribute(Object  InputAttName)
    {
        Object  AttValue = getAttributeValue(InputAttName.toString());
	    if (AttValue == null)
        {
    	    AttributeSet  ResolveParent = getResolveParent();
    	    if (ResolveParent != null)
    		    AttValue = ResolveParent.getAttribute(InputAttName);
    	}
	    return AttValue;
    }

    //*****************************************************************************
    /** Return an enumeration over the names of the attributes in the set.
     *  The elements of the enumeration are all Strings. The set does not include
     *  the resolving parent, if one is defined.
     *
     *  @return  The names of all the attributes
     */
    public Enumeration getAttributeNames()
    {
        return AttributeHashtable.keys();
    }

    //*****************************************************************************
    /** Return true if this set contains this attribute, and its value is the same
     *  as that given.
     *
     *  @param   name the non-null attribute name
     *  @param   value the value
     *  @return  true if the set contains the attribute with an equal value
     */
    public boolean containsAttribute(Object name, Object value)
    {
        return value.equals(getAttribute(name));
    }

    //*****************************************************************************
    /** Returns true if this set contains all the attributes with equal values.
     *
     *  @param attributes the set of attributes to check against
     *  @return true if this set contains all the attributes with equal values
     */
    public boolean containsAttributes(AttributeSet attributes)
    {
        boolean result = true;

        Enumeration names = attributes.getAttributeNames();
        while (result && names.hasMoreElements())
        {
            Object name = names.nextElement();
            result = attributes.getAttribute(name).equals(getAttribute(name));
        }

        return result;
    }

    //*****************************************************************************
    /** Get the parent used in resolving attributes not present in this set.
     *
     *  @return  The parent
     */
    public AttributeSet getResolveParent()
    {
        return ResolveParent;
    }

    //*****************************************************************************
    //*Methods*implementing*swing.text.MutableAttributeSet*************************
    //*****************************************************************************
    /** Add an attribute to the list.
     *
     *  @param  InputAttName   the name
     *  @param  InputAttValue  the value
     */
    public void addAttribute(Object InputAttName, Object InputAttValue)
    {
        addAttribute(InputAttName.toString(), InputAttValue.toString() );
    }

    //*****************************************************************************
    /** Add a set of attributes to the list.
     *
     *  @param  attributes  the set of attributes
     */
    public void addAttributes(AttributeSet attributes)
    {
        Enumeration names = attributes.getAttributeNames();
        while (names.hasMoreElements())
        {
            Object name = names.nextElement();
            addAttribute(name, attributes.getAttribute(name));
        }
    }

    //*****************************************************************************
    /** Remove an attribute from the list.
     *
     *  @param  InputAttName  The name of the attribute to remove
     */
    public void removeAttribute(Object InputAttName)
    {
        removeAttribute(InputAttName.toString() );
    }

    //*****************************************************************************
    /** Remove a set of attributes from the list.
     *
     *  @param  InputAttNames  The names of the attributes to remove
     */
    public void removeAttributes(Enumeration  InputAttNames)
    {
        while (InputAttNames.hasMoreElements())
            removeAttribute((String)InputAttNames.nextElement());
    }

    //*****************************************************************************
    /** Remove a set of attributes from the list. Existing attributes with the same
     *  name and different value will remain.
     *
     * @param  InputAttToRemove  The set of attributes to be removed
     */
    public void removeAttributes(AttributeSet  InputAttToRemove)
    {
        Enumeration names = InputAttToRemove.getAttributeNames();
        while (names.hasMoreElements())
        {
            Object name = names.nextElement();
            Object value = InputAttToRemove.getAttribute(name);
            if (value.equals(getAttribute(name)))
                removeAttribute(name);
        }
    }

    //*****************************************************************************
    /** Set the resolving parent. This is the set of attributes to resolve through
     *  if an attribute isn't defined locally.
     *
     *  @param  InputResolveParent  The parent
     */
    public void setResolveParent(AttributeSet  InputResolveParent)
    {
        ResolveParent = InputResolveParent;
    }
}

//*****************************************************************************
