/*
 * (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.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Vector;
import org.w3c.dom.*;


/**
 * The TXElement class implements the Element interface as defined by the Document Object Model (DOM),
 * and implements the namespace interface as defined by the W3C.
 * <p>By far the vast majority (apart from text) of Node types that authors will
 * generally encounter when traversing a document will be Element Nodes. An Element
 * consists of its start tag, any Attributes, any children, and its end tag.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see org.w3c.dom.Element
 * @see com.ibm.xml.parser.Namespace
 * @see com.ibm.xml.parser.Parent
 * @see com.ibm.xml.parser.Child
 */
public class TXElement extends Parent implements Element, Namespace {

            String          name            = null;
            TXAttributeList attributes      = null;
            boolean         isPreserveSpace = false;
            String          language        = null;
            String          nsLocalName     = null;
            String          nsURI           = null;

    /**
     * Constructor.
     * @param tagName   This Element's tag name (qualified name).
     *                  In the example <code>&lt;elementExample id="demo"&gt; ... &lt;/elementExample&gt;</code>,
     *                  the tag name is <code>elementExample</code>.
     */
    public TXElement(String tagName) {
        setTagName(tagName);
    }

    /**
     * Constructor for namespace support.
     * @param document      The TXDocument providing a namespace to resolve <var>prefix</var>.
     * @param prefixName    A value of the <code>prefix=</code> attribute of a namespace PI
     *                      in <var>document</var>.
     * @param nsLocalName   The Namespace local name.
     * @see com.ibm.xml.parser.NamespacePI
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement(TXDocument document, String prefixName, String nsLocalName) {
        this.nsLocalName = nsLocalName;
        // translate prefix:localpart to nsURL+localpart for the tagName
        if (prefixName != null) {
            NamespacePI npi = document.getNamespaceFor(prefixName);
            if (npi == null)
                throw new LibraryException("com.ibm.xml.parser.TXElement: no namespace for prefix: "+prefixName);
            this.nsURI = npi.getNs();
            this.name = prefixName+":"+nsLocalName;
        }
    }

    /**
     * Clone this Element Node and its children using the appropriate factories.
     * <p>This method is defined by Child.
     * @return          Cloned Element Node.
     * @see com.ibm.xml.parser.Child#clone
     */
    public synchronized Object clone() {
        TXElement el = cloneWithoutChildren();
        el.children.ensureCapacity((int)children.getLength());
        for (int i = 0;  i < children.getLength();  i ++)
            el.insertBefore((Node)(children.item(i)).clone(), null);
        return el;
    }

    /**
     * Clone this Element Node using the appropriate factory.
     * @return          Cloned Element Node.
     */
    public synchronized TXElement cloneWithoutChildren() {
        checkFactory();
        TXElement el = factory.createElement(getTagName());
        el.setFactory(getFactory());
        //el.setNSPrefixName(getNSPrefixName());
        el.setNSLocalName(getNSLocalName());
        el.setNSName(getNSName());
        el.setPreserveSpace(this.isPreserveSpace);
        if (null != this.attributes)
            el.setAttributes((TXAttributeList)((TXAttributeList)this.attributes).clone());
        return el;
    }

    /**
     * Returns that this object is an Element Node.
     * <p>This method is defined by DOM.
     * @return          Element Node indicator.
     */
    public int getNodeType() {
        return Node.ELEMENT;
    }

    /**
     * Returns this Element's name.
     * In the example <code>&lt;elementExample id="demo"&gt; ... &lt;/elementExample&gt;</code>,
     * the tag name is <code>elementExample</code>.  If the Element's name has a namespace
     * prefix, the prefix will still be attached.
     * <p>This method is defined by DOM.
     * @return          The string that is this Element's name, or <var>null</var> if no name.
     * @see #setTagName
     * @see #getName
     */
    public String getTagName() {
        return this.name;
    }

    /**
     * Returns this Element's name.
     * This method is equivalent to <CODE>getTagName()</CODE>.
     * @return          The string that is this Element's name, or <var>null</var> if no name.
     * @see #getTagName
     * @see #setTagName
     */
    public String getName() {
        return getTagName();
    }

    /**
     * Sets this Element's name.
     * In the example <code>&lt;elementExample id="demo"&gt; ... &lt;/elementExample&gt;</code>,
     * the tag name is <code>elementExample</code>.
     * @param   tagName The string that is this Element's name.
     * @see #getTagName
     */
    public void setTagName(String tagName) {
        this.name = tagName;
        clearDigest();
    }

    /**
     * Returns an Attribute's value from this Element that matches the specified
     * Attribute name.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   name    The name to match in this Element's list of Attributes.
     * @return          The string value of the matching Attribute; otherwise, <var>null</var>.
     * @see #getAttributeNode
     * @see #setAttribute
     * @see #setAttributeNode
     * @see #getLanguage
     */
    public String getAttribute(String name) {
        if (null != this.attributes) {
            TXAttribute a = (TXAttribute)this.attributes.getAttribute(name);
            return null == a ? null : a.getValue();
        }
        return null;
    }

    /**
     * Returns an Attribute's value from this Element that matches the specified
     * Attribute name.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   name    The name to match in this Element's list of Attributes.
     * @return          The matching Attribute Node; otherwise, <var>null</var>.
     * @see #getAttribute
     * @see #setAttribute
     * @see #setAttributeNode
     */
    public Attribute getAttributeNode(String name) {
        if (null != this.attributes)
            return this.attributes.getAttribute(name);
        return null;
    }

    /**
     * Adds a new Attribute name/value pair to this Element using the appropriate
     * factory. If an Attribute by that name is already present in this Element,
     * it's value is changed to be that of <var>value</var>.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   name    The name of the Attribute to create or update.
     * @param   value   The value of the created or updated Attribute.
     * @see #getAttribute
     * @see #getAttributeNode
     * @see #setAttributeNode
     */
    public void setAttribute(String name, String value) {
        checkFactory();
        setAttribute(factory.createAttribute(name, value));
    }

    /**
     * Adds a new Attribute to this Element using the appropriate
     * factory. If an Attribute by that name is already present in this Element,
     * it's value is changed to be that of the specified <var>attribute</var>'s value.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * @param   attribute   The Attribute to create or update.
     * @see #getAttribute
     * @see #getAttributeNode
     * @see #setAttributeNode
     * @deprecated Use setAttributeNode()
     */
    public void setAttribute(TXAttribute attribute) {
        makeAttributeList();
        this.attributes.setAttribute(attribute);
        if ("xml:lang".equals(attribute.getName()))
            this.language = attribute.getValue();
    }

    /**
     * Adds a new Attribute to this Element using the appropriate
     * factory. If an Attribute by that name is already present in this Element,
     * it's value is changed to be that of the specified <var>attribute</var>'s value.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   attribute   The Attribute to create or update.
     * @see #getAttribute
     * @see #getAttributeNode
     * @see #setAttribute
     */
    public void setAttributeNode(Attribute attribute) {
        makeAttributeList();
        this.attributes.setAttribute(attribute);
        if ("xml:lang".equals(attribute.getName()))
            this.language = getAttribute("xml:lang");
    }

    /**
     * Removes the Attribute that matches the specified <var>name</var> from this Element.
     * If the name provided does not exist, the NoSuchAttributeException is thrown.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   name    Attribute name to match against.
     * @exception NoSuchNodeException Thrown if no matching Attribute name can be found in this Element.
     * @see #removeAttributeNode
     */
    public void removeAttribute(String name) throws NoSuchAttributeException {
        if (null != this.attributes)
            this.attributes.remove(name);
    }

    /**
     * Removes the specified <var>attribute</var> from this Element.
     * If the Attribute provided does not exist, the NoSuchAttributeException is thrown.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @param   attribute   Attribute to remove.
     * @exception NoSuchNodeException Thrown if no matching Attribute can be found in this Element.
     * @see #removeAttribute
     */
    public void removeAttributeNode(Attribute attribute) throws NoSuchAttributeException {
        if (null != this.attributes) {
            String value = getAttribute(attribute.getName());
            if (null != value && value.equals(attribute.getValue()))
                this.attributes.remove(attribute.getName());
        }
    }

    /**
     * Returns a <var>NodeIterator</var> of the Attributes of this Element;
     * this iterator is not "live" (see Parent#getChildNodes for details).
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * <p>This method is defined by DOM.
     * @return          A NodeIterator of Attributes, or <var>null</var> if no Attributes.
     * @see #getAttributeArray
     * @see #attributeElements
     * @see #setAttributes
     * @see com.ibm.xml.parser.Parent#getChildNodes
     */
    public NodeIterator getAttributes() {
        makeAttributeList();
        return new VectorNodeIterator(this.attributes);
    }

    /**
     * Returns a TXAttribute array of the Attributes of this Element.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * @return          A TXAttribute array of Attributes, or a 0-length array if no Attributes.
     * @see #getAttributes
     * @see #attributeElements
     * @see #setAttributes
     */
    public TXAttribute[] getAttributeArray() {
        return null == this.attributes ? new TXAttribute[0] : this.attributes.makeArray();
    }

    /**
     * Returns an enumeration of TXAttribute of the Attributes of this Element.
     * Both implicitly-defined (DTD) and explicitly-defined attributes are considered.
     * @return          An enumeration of TXAttribute Attributes, or <var>null</var> if no Attributes.
     * @see #getAttributes
     * @see #attributeElements
     * @see #setAttributes
     */
    public java.util.Enumeration attributeElements() {
        makeAttributeList();
        return this.attributes.elements();
    }

    /**
     * Sets this Element's Attribute list from the specified TXAttributeList
     * (preferred) or AttributeList.
     * @param   attributes  The Attributes to set specified in the form of a
     *                      TXAttributeList or AttributeList.
     * @see #getAttributes
     * @see #getAttributeArray
     * @see #attributeElements
     * @exception  NoSuchNodeException  Thrown if unable to set the specified <var>attributes</var>.
     */
    public void setAttributes(AttributeList attributes) throws NoSuchNodeException {
        if (attributes instanceof TXAttributeList) {
            this.attributes = (TXAttributeList)attributes;
            this.attributes.setParent(this);
        } else {
            this.attributes.clear();
            synchronized (attributes) {
                try {
                    for (int i = 0;  i < attributes.getLength();  i ++)
                        this.attributes.setAttribute((Attribute)attributes.item(i));
                } catch (NoSuchNodeException e) {
                    throw new LibraryException("com.ibm.xml.parser.TXElement#setAttributes(): Internet Error: "+e.toString());
                }
            }
        }
        clearDigest();
    }

    /**
     * Returns the local name of the Namespace.
     * <p>This method is defined by Namespace.
     * @return          The Namespace local name, or <var>null</var> if no local name.
     * @see com.ibm.xml.parser.Namespace
     * @see #setNSLocalName
     */
    public String getNSLocalName() {
        return this.nsLocalName;
    }

    /**
     * Sets the local name of the Namespace.
     * <p>This method is defined by Namespace.
     * @param   nsLocalName The Namespace local name.
     * @see com.ibm.xml.parser.Namespace
     * @see #getNSLocalName
     */
    public void setNSLocalName(String nsLocalName) {
        this.nsLocalName = nsLocalName;
    }

    /**
     * Returns the Namespace URI.
     * <p>This method is defined by Namespace.
     * @return          The Namespace URI, or <var>null</var> if the tag name does not
     *                  contain a prefix name or XML4J parser namespace processing is disabled.
     * @see #setNSName 
     * @see com.ibm.xml.parser.Parser#setProcessNamespace
     * @see com.ibm.xml.parser.Namespace
     */
    public String getNSName() {
        return this.nsURI;
    }
    /**
     * Sets the Namespace URI.
     * <p>This method is defined by Namespace.
     * @param   nsURI   The Namespace URI, or <var>null</var> if no name.
     * @see #getNSName 
     * @see com.ibm.xml.parser.Namespace
     */
    public void setNSName(String nsURI) {
        this.nsURI = nsURI;
    }

    /**
     * If namespace support is enabled and the Element's name contains a prefix name, return a string 
     * of <CODE>getNSName()+":"+getNSLocalName()</CODE>; otherwise, return <CODE>getNSLocalName()</CODE> .
     * @return          The universal name, or <var>null</var> if this Element's name does not
     *                  contain a prefix name or XML4J parser namespace processing is disabled.
     * @see #getNSName
     * @see #getNSLocalName
     * @see com.ibm.xml.parser.Parser#setProcessNamespace
     * @see com.ibm.xml.parser.Namespace
     */
    public String getUniversalName() {
        return getNSName() == null ? getNSLocalName() : getNSName()+":"+getNSLocalName();
    }

    /**
     * Returns whether this Element has any children.
     * @return          =true if this Element has any children; otherwise, =false.
     * @see #elementAt
     * @see #size
     */
    public boolean isEmpty() {
        return 0 == children.getLength();
    }

    /**
     * Returns the number of children in this Element.
     * @return          The number of children in this Element.
     * @see #elementAt
     * @see #isEmpty
     */
    public int size() {
        return children.getLength();
    }

    /**
     * Returns the Child Node at the specified index.
     * @param      index    An index into this Element's children.
     * @return              The Child Node at the specified index.
     * @exception  NoSuchNodeException  Thrown if an invalid index is specified.
     * @see #isEmpty
     * @see #size
     */
    public Child elementAt(int index) throws NoSuchNodeException {
        return children.item(index);
    }

    /**
     * Returns, at the Element level, whether space is to be preserved.  
     * This value is used, for example, to determine if space is to be preserved
     * in Text Nodes during print() operations.
     * @return                  <code>=true</code> space is to be preserved; 
     *                          <code>=false</code> space is to be ignored.
     * @see #setPreserveSpace
     * @see com.ibm.xml.parser.Parser#setPreserveSpace
     * @see com.ibm.xml.parser.TXText#setIsIgnorableWhitespace
     */
    public boolean isPreserveSpace() {
        return this.isPreserveSpace;
    }

    /**
     * Sets, at the Element level, whether space is to be preserved.  
     * This value is used, for example, to determine if space is to be preserved
     * in Text Nodes during print() operations.
     * @param   isPreserveSpace <code>=true</code> space is to be preserved; 
     *                          <code>=false</code> space is to be ignored.
     * @see com.ibm.xml.parser.Parser#setPreserveSpace
     * @see com.ibm.xml.parser.TXText#setIsIgnorableWhitespace
     * @see #isPreserveSpace
     */
    public void setPreserveSpace(boolean isPreserveSpace) {
        this.isPreserveSpace = isPreserveSpace;
    }

    /**
     * Return all text associated with this Node and its children without considering entities.
     * <p>This method is defined by Child.
     * @return          Text associated with all children, or <var>""</var> if no children.
     * @see com.ibm.xml.parser.Child#toString
     */
    public String getText() {
        StringBuffer sb = new StringBuffer(1024);
        synchronized (children) {
            int len = (int)children.getLength();
            for (int i = 0;  i < len;  i ++) {
                Child ch = children.item(i);
                sb.append(ch.getText());
            }
        }
        return sb.toString();
    }

    /**
     * Returns the XML language ID (the value of the <code>xml:lang</code> Attribute) in
     * use for this Element Node.  If this Element Node has no explicitly defined language Attribute,
     * Parent Nodes will be recursively checked.
     * @return          The XML language ID, or <var>null</var> if all Parents have no language ID.
     * @see #getAttribute
     */
    public String getLanguage() {
        if (null != this.language) {
            return this.language;
        } else if (null != parent && parent instanceof TXElement) {
            return ((TXElement)parent).getLanguage();
        } else {
            return null;
        }
    }

    /**
     * Puts all Text Nodes in the sub-tree underneath this Element into a "normal"
     * form where only markup (e.g., tags, comments, PIs, CDATASections, and entity
     * references) separates Text Nodes.  This has the effect of combining Text Nodes
     * that have been separated due to document manipulation.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLMENTED.
     */
    public void normalize() {
    }

    /**
     * Adds the specified <var>text</var> Node to the end of this Element. If the last
     * child of this Element is a Text Node and not a CDATA section, <var>text</var> is
     * appended to the last child.  Otherwise, a new TXText Node is inserted at the end of this
     * Element.
     * @param   text        Text Node to add to this element.
     * @see com.ibm.xml.parser.Parent#addElement(com.ibm.xml.parser.Child)
     */
    public void addTextElement(TXText text) {
        if (text == null)
            return;
        if (text instanceof CDATASection) {
            addElement(text);
            return;
        }
        NodeIterator ni = getChildNodes();
        if (0 == ni.getLength()) {
            addElement(text);
            return;
        }
        Node n = ni.toLastNode();
        if (TXText.isText(n)) {
            ((Text)n).append(text.getData());
        } else
            addElement(text);
    }

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

    /**
     * Returns the first <var>TXElement</var> match through all child Element Nodes.
     * 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 immediate Child Nodes.
     * @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                      An array of matched <var>TXElement</var> Nodes, or <var>null</var> if no matches.
     * @see #getElementNamed
     * @see #getNthElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement searchDescendants(int matchType, String uri, String qNameOrLocalName) {
        NodeIterator ne = getChildNodes();
        Child child;
        while (null != (child = (Child)ne.toNextNode())) {
            if (child instanceof TXElement) {
                if (Match.matchName((TXElement)child, matchType, uri, qNameOrLocalName))
                    return (TXElement)child;
                else {
                    TXElement t = ((TXElement)child).searchDescendants(matchType, uri, qNameOrLocalName);
                    if (null != t)  return t;
                }
            }
        }
        return null;
    }

    /**
     * Returns the first <var>TXElement</var> match through the immediate Child Element Nodes.
     * Only immediate children are searched (i.e. no grandchildren).
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * @param   qName   Qualified name to match against in immediate children.
     * @return          The first matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getNthElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getElementNamed(String qName) {
        return getNthElementNamed(0, Match.QNAME, null, qName);
    }

    /**
     * Returns the first <var>TXElement</var> match through the immediate child Element Nodes.
     * All specified input parameters must match for an Element to be judged as matched (see Namespace for details).
     * Only immediate children are searched (i.e. no grandchildren).
     * @param   uri         The Namespace URI to match against in immediate children.
     * @param   localName   The Namespace local name to match against in immediate children.
     * @return              The first matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getNthElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getElementNamed(String uri, String localName) {
        return getNthElementNamed(0, Match.NSLOCAL, uri, localName);
    }

    /**
     * Returns the first <var>TXElement</var> match through the immediate child Elemen Nodes.
     * 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.
     * Only immediate children are searched (i.e. no grandchildren).
     * @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                      An array of matched <var>TXElement</var> Nodes, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getNthElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getElementNamed(int matchType, String uri, String qNameOrLocalName) {
        return getNthElementNamed(0, matchType, uri, qNameOrLocalName);
    }

    /**
     * Returns the Nth <var>TXElement</var> match through the immediate child Element Nodes.
     * Only immediate children are searched (i.e. no grandchildren).
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * <dl>
     *   <dt>Example:</dt>
     *   <dd><pre>
     *      &lt;BODY&gt;            --- this
     *        &lt;H1&gt;...&lt;/H1&gt;    --- getNthElementNamed(0, "H1")
     *        &lt;P&gt;...&lt;/P&gt;      --- getNthElementNamed(0, "P")
     *        &lt;P&gt;...&lt;/P&gt;      --- getNthElementNamed(1, "P")
     *        &lt;HR/&gt;           --- getNthElementNamed(0, "HR");
     *        &lt;P&gt;...&lt;/P&gt;      --- getNthElementNamed(2, "P")
     *        &lt;HR/&gt;           --- getNthElementNamed(1, "HR");
     *      &lt;/BODY&gt;
     *    </pre></dd>
     *  </dl>
     * @param   nth     0-based match occurrence.
     * @param   qName   Qualified name to match against in immediate children.
     * @return          The Nth matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getNthElementNamed(int nth, String qName) {
        return getNthElementNamed(nth, Match.QNAME, null, qName);
    }

    /**
     * Returns the Nth <var>TXElement</var> match through the immediate child Element Nodes.
     * All specified input parameters must match for an Element to be judged as matched (see Namespace for details).
     * Only immediate children are searched (i.e. no grandchildren).
     * @param   nth         0-based match occurrence.
     * @param   uri         The Namespace URI to match against in immediate children.
     * @param   localName   The Namespace local name to match against in immediate children.
     * @return              The Nth matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getNthElementNamed(int nth, String uri, String localName) {
        return getNthElementNamed(nth, Match.NSLOCAL, uri, localName);
    }

    /**
     * Returns the Nth <var>TXElement</var> match through the immediate child Element Nodes.
     * 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.
     * Only immediate children are searched (i.e. no grandchildren).
     * @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 Nth matching <var>TXElement</var> Node, or <var>null</var> if no matches.
     * @see #searchDescendants
     * @see #getElementNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement getNthElementNamed(int nth, int matchType, String uri, String qNameOrLocalName) {
        NodeIterator ni = getChildNodes();
        Node child;
        while (null != (child = ni.toNextNode())) {
            if (child instanceof TXElement) {
                TXElement te = (TXElement)child;
                if (Match.matchName(te, matchType, uri, qNameOrLocalName)) {
                    if (0 == nth)
                        return te;
                    else
                        nth --;
                }
            }
        }
        return null;                            // Not found
    }

    /**
     * Returns an array of <var>TXElement</var> of matches through the immediate child Element Nodes.
     * Only immediate children are searched (i.e. no grandchildren).
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * @param   qName   Qualified name to match against in immediate children.
     * @return          An array of matched <var>TXElement</var> Nodes, or <var>null</var> if no matches.
     * @see #getElementsByTagName
     * @see #searchDescendantsAll
     * @see #getElementsNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement[] searchChildrenAll(String qName) {
        Vector v = new Vector();
        NodeIterator ne = getChildNodes();
        Node child;
        while (null != (child = ne.toNextNode())) {
            if (child instanceof Element) {
                if (Match.matchName((Namespace)child, Match.QNAME, null, qName))
                    v.addElement(child);
            }
        }
        TXElement[] ae = new TXElement[v.size()];
        v.copyInto(ae);
        return ae;
    }

    /**
     * Returns a <var>NodeIterator</var> of matches through all child Element Nodes.
     * Searching is done recursively, not just for immediate Child Nodes.
     * The returned iterator is not "live" (refer to Parent#getChildNodes for details).
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * <p>This method is defined by DOM.
     * @param   qName   Qualified name to match against in all subordinate Elements.
     * @return          A <var>NodeIterator</var> of matched <var>Element</var> Nodes (<var>TXElement</var>)
     * @see #searchChildrenAll
     * @see #searchDescendantsAll
     * @see #getElementsNamed
     * @see com.ibm.xml.parser.Parent#getChildNodes
     * @see com.ibm.xml.parser.Namespace
     */
    public NodeIterator getElementsByTagName(String qName) {
        java.util.Vector v = new java.util.Vector();
        searchDescendantsAll(v, Match.QNAME, null, qName);
        return new VectorNodeIterator(v);
    }

    /**
     * Returns an array of <var>TXElement</var> matches through all child Element Nodes.
     * Searching is done recursively, not just for immediate Child Nodes.
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * @param   qName   Qualified name to match against in all subordinate Elements.
     * @return          An array of matched <var>TXElement</var> Nodes, or <var>null</var> if no matches.
     * @see #searchChildrenAll
     * @see #getElementsByTagName
     * @see #getElementsNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement[] searchDescendantsAll(String qName) {
        return searchDescendantsAll(Match.QNAME, null, qName);
    }

    /**
     * Returns an array of <var>TXElement</var> matches through all child Element Nodes.
     * 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 immediate Child Nodes.
     * @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                      An array of matched <var>TXElement</var> Nodes, or <var>null</var> if no matches.
     * @see #searchChildrenAll
     * @see #getElementsByTagName
     * @see #getElementsNamed
     * @see com.ibm.xml.parser.Namespace
     */
    public TXElement[] searchDescendantsAll(int matchType, String uri, String qNameOrLocalName) {
        Vector v = new Vector();
        searchDescendantsAll(v, matchType, uri, qNameOrLocalName);
        TXElement[] ae = new TXElement[v.size()];
        v.copyInto(ae);
        return ae;
    }

    /**
     * Returns a <var>NodeIterator</var> of all matches through the immediate child Element Nodes;
     * this iterator is not "live" (see Parent#getChildNodes for details).
     * Only immediate children are searched (i.e. no grandchildren).
     * The specified qualified name refers to the Element tag name (see Namespace for details).
     * @param   qName   Qualified name to match against in immediate children.
     * @return          A <var>NodeIterator</var> of matches, or <var>null</var> if no matches.
     * @see #searchChildrenAll
     * @see #getElementsByTagName
     * @see #searchDescendantsAll
     * @see com.ibm.xml.parser.Parent#getChildNodes
     * @see com.ibm.xml.parser.Namespace
     */
    public NodeIterator getElementsNamed(String qName) {
        return getElementsNamed(Match.QNAME, null, qName);
    }

    /**
     * Returns a <var>NodeIterator</var> of matches through the immediate child Element Nodes;
     * this iterator is not "live" (see Parent#getChildNodes for details).
     * All specified input parameters must match for an Element to be judged as matched (see Namespace for details).
     * Only immediate children are searched (i.e. no grandchildren).
     * @param   uri         The Namespace URI to match against in immediate children.
     * @param   localName   The Namespace local name to match against in immediate children.
     * @return              A <var>TXElement</var> of matches, or <var>null</var> if no matches.
     * @see #searchChildrenAll
     * @see #getElementsByTagName
     * @see #searchDescendantsAll
     * @see com.ibm.xml.parser.Parent#getChildNodes
     * @see com.ibm.xml.parser.Namespace
     */
    public NodeIterator getElementsNamed(String uri, String localName) {
        return getElementsNamed(Match.NSLOCAL, uri, localName);
    }

    /**
     * Returns a <var>NodeIterator</var> of all matches through the immediate child Element Nodes;
     * this iterator is not "live" (see Parent#getChildNodes for details).
     * 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.
     * Only immediate children are searched (i.e. no grandchildren).
     * @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                      A <var>NodeIterator</var> of matches, or <var>null</var> if no matches.
     * @see #searchChildrenAll
     * @see #getElementsByTagName
     * @see #searchDescendantsAll
     * @see com.ibm.xml.parser.Parent#getChildNodes
     * @see com.ibm.xml.parser.Namespace
     */
    public NodeIterator getElementsNamed(int matchType, String uri, String qNameOrLocalName) {
        // This method is not used by the parser.
        Vector v = new Vector();
        NodeIterator ne = getChildNodes();
        Node child;
        while (null != (child = ne.toNextNode())) {
            if (child instanceof Element) {
                if (Match.matchName((Namespace)child, matchType, uri, qNameOrLocalName))
                    v.addElement(child);
            }
        }
        return new VectorNodeIterator(v);
    }


    private void searchDescendantsAll(java.util.Vector v, int matchtype, String nsorpre, String ename) {
        NodeIterator ne = getChildNodes();
        Child p;
        while (null != (p = (Child)ne.toNextNode())) {
            if (p instanceof Element) {
                if (Match.matchName((TXElement)p, matchtype, nsorpre, ename))
                    v.addElement(p);
                ((TXElement)p).searchDescendantsAll(v, matchtype, nsorpre, ename);
            }
        }
    }

    StringBuffer makeHead(String enc) {
        StringBuffer sb = new StringBuffer();
        sb.append("<");
        sb.append(getName());
        if (null != this.attributes) {
            for (int i = 0;  i < this.attributes.getLength();  i ++) {
                TXAttribute aa = (TXAttribute)this.attributes.item(i);
                sb.append(" ");
                sb.append(aa.toXMLString(enc));
            }
        }
        return sb;
    }

    private void makeAttributeList() {
        if (null == this.attributes) {
            checkFactory();
            this.attributes = factory.createAttributeList();
            this.attributes.setParent(this);
        }
    }

}
