/*
 * (C) Copyright IBM Corp. 1997-1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import org.w3c.dom.*;

/**
 * The TXAttributeList class implements the AttributeList interface as 
 * defined by the Document Object Model (DOM).
 * <p>AttributeLists are used to represent collections of Attributes 
 * which can be accessed by name or by ordinal. In most cases, 
 * AttributeLists are created from Element objects. 
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see org.w3c.dom.AttributeList
 */
public class TXAttributeList extends java.util.Vector
    implements AttributeList, Cloneable, java.io.Serializable {
    
            Element         parentElement   =   null;
            ElementFactory  factory         =   null;
    
    /**
     * Constructor.
     */
    public TXAttributeList() {
        super();
    }
    
    /**
     * Clone this TXAttributeList using the appropriate factory.  Note that this
     * method does clone Child Attributes.
     * @return          Cloned TXAttributeList and children.
     */
    public synchronized Object clone() {
        TXAttributeList attl = null == this.factory ? new TXAttributeList()
            : this.factory.createAttributeList();
        attl.parentElement = this.parentElement;
        for (int i = 0;  i < getLength();  i ++) {
            attl.setAttribute((Attribute)((TXAttribute)item(i)).clone());
        }
        return attl;
    }
    
    /**
     * Returns the factory used for creating this Node.
     * @return          The factory used for creating this Node, or <var>null</var> if no factory.
     * @see #setFactory
     */
    public ElementFactory getFactory() {
        return this.factory;
    }

    /**
     * Sets the factory to be used in creating this Node.
     * @param factory   The factory to be used in creating this Node.
     * @see #getFactory
     */
    public void setFactory(ElementFactory factory) {
        this.factory = factory;
    }

    /**
     * Returns the index of the Attribute whose name matches the specified <var>name</var>.
     * @param name      Name to match against in the AttributeList.
     * @return          0-based index of matching Attribute, or <var>-1</var> if no matches.
     * @see #getAttribute
     */
    public synchronized int indexOf(String name) {
        for (int i = 0;  i < size();  i ++) {
            TXAttribute a = (TXAttribute)elementAt(i);
            if (a.getName().equals(name))  return i;
        }
        return -1;
    }

    /**
     * Returns an Attribute instance whose name matches the specified <var>name</var>.  
     * <p>This method is defined by DOM.
     * @param name      Name to match against in the AttributeList.
     * @return          The matching Attribute instance, or <var>null</var> if no matches.
     * @see #indexOf
     * @see #setAttribute
     */
    public Attribute getAttribute(String name) {
        int index = indexOf(name);
        return index < 0 ? null : (Attribute)elementAt(index);
    }
    
    /**
     * Adds or replaces an Attribute. If the Attribute name already exists in this list, the
     * previous Attribute is replaced, and returned. If no Attribute of the same name 
     * exists <var>null</var> is returned, and the specified <var>attribute</var> is added to the 
     * end of this AttributeList.
     * <p>This method is defined by DOM.
     * @param   attribute   The Attribute to add or replace.
     * @return              If replaced, the Attribute that was replaced; otherwise, <var>null</var>.
     * @see #getAttribute
     * @see #remove
     */
    public Attribute setAttribute(Attribute attribute) {
        TXAttribute txAttribute;
        if (attribute instanceof TXAttribute) {
            txAttribute = (TXAttribute)attribute;
        } else {
            txAttribute = new TXAttribute(attribute.getName(), null);
            txAttribute.setValue(attribute.toString());
        }

        Attribute ret = null;
        int index = indexOf(attribute.getName());
        if (0 > index) {
            TXAttribute tail = size() > 0 ? (TXAttribute)elementAt(size()-1) : null;
            txAttribute.setNextSibling(null);
            txAttribute.setPreviousSibling(tail);
            if (null != tail)
                tail.setNextSibling(txAttribute);
            txAttribute.setParent(this.parentElement);
            addElement(txAttribute);
        } else {
            ret = (Attribute)elementAt(index);
            setElementAt(txAttribute, index);
            txAttribute.setPreviousSibling(ret.getPreviousSibling());
            txAttribute.setNextSibling(ret.getNextSibling());
            txAttribute.setParent(this.parentElement);
        }
        if (null != this.parentElement)
            ((Child)this.parentElement).clearDigest();
        return ret;
    }

    /**
     * Removes the Attribute instance specified by <var>name</var> from this list and returns 
     * it. If the name provided does not exist, the NoSuchAttributeException is thrown. 
     * <p>This method is defined by DOM.
     * @param   name    Name to match against in the AttributeList.
     * @return          The matching Attribute instance that was removed.
     * @exception NoSuchNodeException Thrown if no matching Attribute name can be found in the list.
     * @see #setAttribute
     * @see #clear
     */
    public Attribute remove(String name) throws NoSuchNodeException {
        int ind = indexOf(name);
        if (0 > ind)  throw new NoSuchNodeException();
        TXAttribute ret = (TXAttribute)elementAt(ind);
        TXAttribute prev = (TXAttribute)ret.getPreviousSibling();
        TXAttribute next = (TXAttribute)ret.getNextSibling();
        removeElementAt(ind);
        if (null != prev)  prev.setNextSibling(next);
        if (null != next)  next.setPreviousSibling(prev);

        ret.setPreviousSibling(null);
        ret.setNextSibling(null);
        ret.setParent(null);

        if (null != this.parentElement)
            ((Child)this.parentElement).clearDigest();
        return ret;
    }

    /**
     * Removes all Attributes from this AttributeList. 
     * @see #remove
     */
    public void clear() {
        removeAllElements();
    }

    /**
     * Returns the Attribute at the specified <var>index</var>.
     * If the specified <var>index</var> is greater than or equal to the number of Nodes 
     * in this list, a NoSuchAttributeException exception is thrown. 
     * <p>This method is defined by DOM.
     * @param   index   0-based index which identifies the Attribute to return.
     * @return          The Attribute at the specified index.
     * @exception NoSuchNodeException Thrown if the specified index is invalid.
     */
    public Attribute item(int index) {          
        if (index < 0 || size() <= index)  throw new NoSuchNodeException();
        return (Attribute)elementAt(index);
    }

    /**
     * Returns the number of Attributes in this AttributeList. 
     * <p>This method is defined by DOM.
     * @return          The number of Attributes in this list.
     */
    public int getLength() {                   
        return size();
    }

    /**
     * Sets this AttributeList and all its Child Attributes to have the   
     * specified <var>parent</var> Node. 
     * @param   parent      The Parent for this AttributeList and its child Attributes.
     */
    public void setParent(Element parent) {  
        for (int i = 0;  i < getLength();  i ++) {
            TXAttribute attr = (TXAttribute)item(i);
            attr.setParent(parent);
        }
        this.parentElement = parent;
    }

    /**
     * Returns all Attributes in this list as an array of <var>TXAttributes</var>. 
     * @return          All Attributes in this list.
     */
    public TXAttribute[] makeArray() {
        TXAttribute[] taa = new TXAttribute[size()];
        copyInto(taa);
        return taa;
    }
}
