/*
 * (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.*;
import java.util.Vector;

/**
 * The Parent class extends Child and provides services in support of non-leaf Nodes.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see com.ibm.xml.parser.TXElement
 * @see com.ibm.xml.parser.TXDocument
 * @see com.ibm.xml.parser.DTD
 * @see com.ibm.xml.parser.Child
 * @see org.w3c.dom.Node
 */
public abstract class Parent extends Child {

            TXNodeList children =   new TXNodeList();

    /**
     * Returns a NodeIterator object that will enumerate all children of this Node. If there 
     * are no children, an empty iterator is returned. The content of the 
     * returned NodeIterator is "live" in the sense that changes to the children of the Node 
     * object that it was created from will be immediately reflected in the Nodes returned by 
     * the iterator; it is not a static snapshot of the content of the Node. Similarly, changes 
     * made to the Nodes returned by the iterator will be immediately reflected in the tree, 
     * including the set of children of the Node that the NodeIterator was created from.
     * <p>This method is defined by DOM.
     * @return          An iterator of all children of this Parent Node.
     * @see #hasChildNodes
     * @see #elements
     * @see #getFirstChild
     * @see #getChildrenArray
     */
    public NodeIterator getChildNodes() {
        return this.children.getIterator();
    }

    /**
     * Returns <var>true</var> if this Node has any children, or <var>false</var> if this Node has no children at all. 
     * This method exists both for convenience as well as to allow implementations to be able 
     * to bypass object allocation, which may be required for implementing getChildNodes().
     * <p>This method is defined by DOM.
     * @return          <var>True</var> if any children exist; otherwise, returns <var>false</var>.
     * @see #getChildNodes
     * @see #elements
     * @see #getFirstChild
     * @see #getChildrenArray
     */
    public boolean hasChildNodes() {
        return 0 < this.children.getLength();
    }

    /**
     * Return an Enumeration instance of all children of this Node.  
     * @return          An enumeration of all children of this Parent Node, or <var>null</var> if no children.
     * @see #hasChildNodes
     * @see #getChildNodes
     * @see #getFirstChild
     * @see #getChildrenArray
     */
    public java.util.Enumeration elements() { 
        return this.children.elements();
    }

    /**
     * Return an array of <var>Child</var> Nodes for all children of this Node.  
     * @return          An array of all children of this Parent Node, or <var>null</var> if no children.
     * @see #hasChildNodes
     * @see #getChildNodes
     * @see #getFirstChild
     * @see #elements
     */
    public Child[] getChildrenArray() {
        Child[] ac = new Child[this.children.getLength()];
        for (int i = 0;  i < this.children.getLength();  i ++)
            ac[i] = (Child)this.children.item(i);
        return ac;
    }

    /**
     * Returns the first Child of this Node. If there are no children, <var>null</var> is returned.
     * <p>This method is defined by DOM.
     * @return          The first Child of this Parent Node, or <var>null</var> if no children.
     * @see #hasChildNodes
     * @see #getChildNodes
     * @see #elements
     */
    public Node getFirstChild() {
        return 0 < this.children.getLength() ? this.children.item(0) : null;
    }

    /**
     * Insert a Child Node into the specified position in this Node's list of children.
     * @param Child     The Child Node being inserted.
     * @param index     0-based index into the list of children.
     * @exception NoSuchNodeException Thrown if <var>index</var> is not valid.
     * @see #insertBefore
     * @see #insertAfter
     * @see #insertFirst
     * @see #insertLast
     */
    public void insert(Child child, int index) throws NoSuchNodeException {
        this.children.insert(index, child);
        child.setParent(this);
        clearDigest();
    }

    /**
     * Inserts a Child Node (newChild) before the existing Child Node (refChild). If <var>refChild</var> is <var>null</var>, insert
     * <var>newChild</var> at the end of the list of children. If <var>refChild</var> is not a Child of this Node,
     * a <var>NotMyChildException</var> is thrown.
     * <p>This method is defined by DOM.
     * @param newChild  The Child Node being inserted.
     * @param refChild  The Child Node to insert <var>newChild</var> before, or <var>null</var> if <var>newChild</var> is to be inserted at the end of the Child list.
     * @return          The Child Node being inserted.
     * @exception NotMyChildException Thrown if <var>refChild</var> is not a Child of this object.
     * @see #insert
     * @see #insertAfter
     * @see #insertFirst
     * @see #insertLast
     */
    public Node insertBefore(Node newChild, Node refChild) throws NotMyChildException {
        if (null == refChild) {
            insert((Child)newChild, (int)this.children.getLength());
        } else {
            int index = this.children.indexOf(refChild);
            if (0 > index)  throw new NotMyChildException();
            insert((Child)newChild, index);
        }
        return newChild;
    }
    
    /**
     * Inserts a Child Node (newChild) after the existing Child Node (refChild). If <var>refChild</var> is <var>null</var>, insert
     * <var>newChild</var> at the beginning of the list of children. If <var>refChild</var> is not a Child of this Node,
     * a <var>NotMyChildException</var> is thrown.
     * @param newChild  The Child Node being inserted.
     * @param refChild  The Child Node to insert <var>newChild</var> after, or <var>null</var> if <var>newChild</var> is to be inserted at the beginning of the Child list.
     * @return          The Child Node being inserted.
     * @exception NotMyChildException Thrown if <var>refChild</var> is not a Child of this object.
     * @see #insertBefore
     * @see #insert
     * @see #insertFirst
     * @see #insertLast
     */
    public Node insertAfter(Node newChild, Node refChild) throws NotMyChildException {
        if (null == refChild) {
            insert((Child)newChild, 0);
        } else {
            insertBefore(newChild, refChild.getPreviousSibling());
        }
        return newChild;
    }
    
    /**
     * Inserts the specified Node as the first Child in this Node's list of children. 
     * @param newChild  The Child Node being inserted.
     * @return          The Child Node being inserted.
     * @see #insertBefore
     * @see #insertAfter
     * @see #insert
     * @see #insertLast
     */
    public Node insertFirst(Node newChild) {
        insert((Child)newChild, 0);
        return newChild;
    }
    
    /**
     * Inserts the specified Node as the last Child in this Node's list of children. 
     * @param newChild  The Child Node being inserted.
     * @return          The Child Node being inserted.
     * @see #insertBefore
     * @see #insertAfter
     * @see #insertFirst
     * @see #insert    
     * @see #addElement
     */
    public Node insertLast(Node newChild) {
        insert((Child)newChild, this.children.getLength());
        return newChild;
    }
    
    /**
     * Inserts the specified Node as the last Child in this Node's list of children. 
     * @param newChild  The Child Node being inserted.
     * @return          The Child Node being inserted.
     * @see #insertBefore
     * @see #insertAfter
     * @see #insertFirst
     * @see #insert    
     * @see #insertLast
     */
    public void addElement(Child newChild) {
        if (newChild != null)
            insert(newChild, this.children.getLength());
    }

    /**
     * Replaces the Child Node <var>oldChild</var> with <var>newChild</var> in this Node's list of children, and return
     * the <var>oldChild</var> Node. If <var>oldChild</var> was not already a Child of this Node,
     * a <var>NotMyChildException</var> is thrown.
     * <p>This method is defined by DOM.
     *
     * @param oldChild  The Child Node being replaced.
     * @param newChild  The Child Node to replace with.
     * @return          The Child Node being replaced.
     * @exception NotMyChildException Thrown if <var>oldChild</var> is not a Child of this object.
     * @see #removeChild  
     */
    public Node replaceChild(Node oldChild, Node newChild) throws NotMyChildException {
        int index = this.children.indexOf(oldChild);
        if (0 > index)  throw new NotMyChildException();
        Child child = (Child)newChild;
        this.children.replace(index, child);
        child.setParent(this);
        clearDigest();
        return oldChild;
    }
    
    /**
     * Removes the Child Node indicated by <var>oldChild</var> from this Nodes list of children, and returns it. If <var>oldChild</var>
     * was not a Child of this Node, a <var>NotMyChildException</var> is thrown.
     * <p>This method is defined by DOM.
     * @param oldChild  The Child Node being removed.
     * @return          The Child Node being removed.
     * @exception NotMyChildException Thrown if <var>oldChild</var> is not a Child of this object.
     * @see #replaceChild 
     */
    public Node removeChild(Node oldChild) throws NotMyChildException {
        int index = this.children.indexOf(oldChild);
        if (0 > index) throw new NotMyChildException();
        this.children.remove(index);
        clearDigest();
        return oldChild;
    }

}
