/*
 * (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 java.io.Serializable;
import org.w3c.dom.Node;
import org.w3c.dom.NodeIterator;
import org.w3c.dom.NotMyChildException;

/**
 * The Child class implements the Node interface as defined by the Document Object Model (DOM).
 * <p>Node is the base type of most objects in the Document Object Model. It may have an arbitrary number (including zero) of
 * sequentially ordered Child Nodes. It usually has a parent Node; the exception being that the root Node in a document
 * hierarchy has no parent.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see org.w3c.dom.Node
 * @see com.ibm.xml.parser.TXElement
 * @see com.ibm.xml.parser.TXText
 * @see com.ibm.xml.parser.TXComment
 * @see com.ibm.xml.parser.TXPI
 * @see com.ibm.xml.parser.TXCDATASection
 * @see com.ibm.xml.parser.TXNotation
 * @see com.ibm.xml.parser.TXAttribute
 * @see com.ibm.xml.parser.Parent
 */
public abstract class Child implements Node, Cloneable, Serializable {

    public static final int DOCUMENTTYPE        = 11;
    public static final int ELEMENTDEFINITION   = 12;
    public static final int ATTLIST             = 13;
    public static final int ATTDEF              = 14;
    public static final int ENTITY              = 15;
    public static final int NOTATION            = 16;
    public static final int GENERALREFERENCE    = 20;
    public static final int PSEUDONODE          = 21;

            Node             parent      = null;
            Node             prevSibling = null;
            Node             nextSibling = null;
            transient byte[] digest      = null;
            ElementFactory   factory     = null;

    /**
     * Clone this object.
     * @return          Copy of this object.
     */
    abstract public Object clone();

    /**
     * Returns the parent of the given Node instance. If this Node is the root of the document object tree,
     * or if the Node has not been added to a document tree, <var>null</var> is returned.
     * <p>This method is defined by DOM.
     * @return          Parent Node associated with this object, or <var>null</var> if no parent.
     */
    public Node getParentNode() {
        return this.parent;
    }

    /**
     * Returns an empty NodeIterator object because Child Nodes are leaf Nodes.  
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @return          Always returns an empty Node iterator.
     * @see com.ibm.xml.parser.Parent#getChildNodes
     */
    public NodeIterator getChildNodes() {
        return VectorNodeIterator.emptyIterator;
    }

    /**
     * Returns <var>false</var> because Child Nodes are leaf Nodes.  
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @return          Always returns <var>false</var>.
     * @see com.ibm.xml.parser.Parent#hasChildNodes
     */
    public boolean hasChildNodes() {
        return false;
    }

    /**
     * Returns <var>null</var> because Child Nodes are leaf Nodes.
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @return          Always returns <var>null</var>.
     * @see com.ibm.xml.parser.Parent#getFirstChild
     */
    public Node getFirstChild() {
        return null;
    }

    /**
     * Returns the Node immediately preceding this Node in a breadth-first traversal of the tree. If
     * there is no such Node, <var>null</var> is returned.
     * <p>This method is defined by DOM.
     *
     * @return          The Child Node immediately preceding this object in the Parent's Child list,
     *                    or <var>null</var> if this object is the first sibling.
     * @see #getNextSibling
     */
    public Node getPreviousSibling() {
        return this.prevSibling;
    }

    /**
     * Returns the Node immediately following this Node in a breadth-first traversal of the tree. If
     * there is no such Node, <var>null</var> is returned.
     * <p>This method is defined by DOM.
     *
     * @return          The Child Node immediately following this object in the parent's Child list,
     *                    or <var>null</var> if this object is the last sibling.
     * @see #getPreviousSibling
     */
    public Node getNextSibling() {
        return this.nextSibling;
    }

    /**
     * Throws a <var>NotMyChildException</var> because Child Nodes are leaf Nodes.
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @param   newChild    Not used.
     * @param   refChild    Not used.
     * @return              Nothing is ever returned (an exception is thrown).
     * @exception NotMyChildException Thrown to indicate that no children exist in leaf Nodes.
     * @see com.ibm.xml.parser.Parent#insertBefore
     */
    public Node insertBefore(Node newChild, Node refChild) throws NotMyChildException {
        throw new NotMyChildException("com.ibm.xml.parser.Child#insertBefore(): Node "+refChild+" is not a child.");
    }

    /**
     * Throws a <var>NotMyChildException</var> because Child Nodes are leaf Nodes.
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @param   oldChild    Not used.
     * @param   newChild    Not used.
     * @return              Nothing is ever returned (an exception is thrown).
     * @exception NotMyChildException Thrown to indicate that no children exist in leaf Nodes.
     * @see com.ibm.xml.parser.Parent#replaceChild
     */
    public Node replaceChild(Node oldChild, Node newChild) throws NotMyChildException {
        throw new NotMyChildException("com.ibm.xml.parser.Child#replaceChild(): Node "+oldChild+" is not a child.");
    }

    /**
     * Throws a <var>NotMyChildException</var> because Child Nodes are leaf Nodes.
     * If the Child class is extended by non-leaf Nodes, this method should be overridden.
     * <p>This method is defined by DOM.
     *
     * @param   oldChild    Not used.
     * @return              Nothing is ever returned (an exception is thrown).
     * @exception NotMyChildException Thrown to indicate that no children exist in leaf Nodes.
     * @see com.ibm.xml.parser.Parent#removeChild
     */
    public Node removeChild(Node oldChild) throws NotMyChildException {
        throw new NotMyChildException("com.ibm.xml.parser.Child#removeChild(): Node "+oldChild+" is not a child.");
    }

    /**
     * Returns all text associated with this Node without considering entities.                                           
     * This method is intended to be overridden by DOM-defined Node types.
     * @return          Always returns <var>""</var>.
     * @see com.ibm.xml.parser.TXText#getText
     * @see #toString
     */
    public String getText() {
        return "";
    }

    /**
     * Returns the first <var>TXElement</var> match through all ancestors.
     * Searching is done recursively, not just for the immediate Parent Node.
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * @param   qName   Qualified name to match against in all ancestors.
     * @return          The first matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement searchAncestors(String qName) {
        return searchAncestors(Match.QNAME, null, qName);
    }

    /**
     * Returns the first <var>TXElement</var> match through all ancestors.
     * All specified input parameters must match for an Element to be judged as matched (see Namespace for details).
     * Various namespace matching algorithms are supported: qualified name, local name & URI, or URI.
     * Searching is done recursively, not just for the immediate Parent Node.
     * @param   matchType           Namespace match type: <code>Match.QNAME, Match.NSLOCAL, Match.NS</code>
     * @param   uri                 When matching a URI, specify the value to match; otherwise, specify <var>null</var>.
     * @param   qNameOrLocalName    When matching a qualified name or local name, specify the value to match; otherwise, specify <var>null</var>.
     * @return                      The first matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement searchAncestors(int matchType, String uri, String qNameOrLocalName) {
        // This method is not used by the parser.
        Node parent = this;
        while (null != (parent = parent.getParentNode())) {
            if (!(parent instanceof TXElement))  return null;
            if (Match.matchName((Namespace)parent, matchType, uri, qNameOrLocalName))
                return (TXElement)parent;
        }
        return null;
    }

    /**
     * Returns this Node in XML format using the default character encoding.
     * Entities embedded in the returned string will be respected.
     * For example, the characters <code>&lt;,  &gt;,  &amp;,  &apos;, and &quot; </code> 
     * will be represented as <code>&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot; </code>. 
     * @return          XML formatted string.
     * @see #getText
     * @see com.ibm.xml.parser.Entity
     */
    public String toString() {
        return toString(null);
    }

    /**
     * Returns this Node in XML format using the specified character encoding.
     * Entities embedded in the returned string will be respected.
     * For example, the characters <code>&lt;,  &gt;,  &amp;,  &apos;, and &quot; </code> 
     * will be represented as <code>&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot; </code>. 
     * @param enc       Java character encoding to use.
     * @return          XML formatted string.
     * @see #getText
     * @see com.ibm.xml.parser.Entity
     */
    public String toString(String enc) {
        checkFactory();
        return this.factory.toString(this, enc);
    }

    /**
     * Print this Node in XML format, using the specified character encoding, and indented by <var>indent</var> spaces.
     * Entities embedded in the output stream will be respected.
     * For example, the characters <code>&lt;,  &gt;,  &amp;,  &apos;, and &quot; </code> 
     * will be represented as <code>&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot; </code>. 
     * @param pw        The character output stream to use.
     * @param enc       Java character encoding in use by <VAR>pw</VAR>.
     * @see com.ibm.xml.parser.Entity
     */
    public void print(java.io.PrintWriter pw, String enc) {
        checkFactory();
        this.factory.print(this, pw, enc);
    }

    /**
     * Print this Node in XML format, using the default character encoding, and indented by <var>indent</var> spaces.
     * Entities embedded in the output stream will be respected.
     * For example, the characters <code>&lt;,  &gt;,  &amp;,  &apos;, and &quot; </code> 
     * will be represented as <code>&amp;lt;, &amp;gt;, &amp;amp;, &amp;apos;, &amp;quot; </code>. 
     * @param pw        The character output stream to use.
     * @see com.ibm.xml.parser.Entity
     */
    public void print(java.io.PrintWriter pw) {
        print(pw, null);
    }

    /**
     * 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 digest value for this Node and any children.  If no digest value currently 
     * exists, calculate it.
     * @return          The array of bytes for the resulting hash value.
     * @see #clearDigest
     */
    public byte[] getDigest() {
        if (null == this.digest) {
            checkFactory();
            this.digest = this.factory.makeDigest(this);
        }
        return this.digest;
    }

    /**
     * Clears the digest value for this Node and any ancestors.
     * @see #getDigest
     */
    public void clearDigest() {
        if (null != this.digest) {
            this.digest = null;
            if (null != this.parent)
                ((Child)this.parent).clearDigest();
        }
    }

    /**
     * Return an XPointer instance representing this node.
     *
     * @return XPointer instance, or <VAR>null</VAR> if this node isn't placed in a tree.
     * @see com.ibm.xml.xpointer.XPointer
     */
    public com.ibm.xml.xpointer.XPointer makeXPointer() {
        return com.ibm.xml.xpointer.XPointer.makeXPointer(this);
    }

    void checkFactory() {
        if (null == this.factory)
            this.factory = DefaultElementFactory.getInstance();
    }

    void setParent(Node parent) {
        this.parent = parent;
    }
    void setPreviousSibling(Node prevSibling) {
        this.prevSibling = prevSibling;
    }
    void setNextSibling(Node nextSibling) {
        this.nextSibling = nextSibling;
    }
}
