/*
 * (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.util.*;
import org.w3c.dom.*;

/**
 * The DTD class implements the DocumentType interface as defined by the Document Object Model (DOM).
 * <p>The DTD defines the document class (type) and indicates where to find its syntactic grammar
 * definition.
 * <p>The external DTD subset is outside the XML document.  Typically, it is a standardized
 * document.  The internal DTD subset forms part of the XML document itself.  The internal
 * DTD subset often contains entity declarations that are used throughout the XML document
 * as well as other document-specific declarations.
 * <p>When an XML document has both internal and external DTD subsets, the internal subset
 * definitions are processed first, and take precedence over external subset definitions.
 * <p>The following examples illustrate an external DTD reference, an internal DTD subset, 
 * and an external DTD reference combined with an internal DTD subset:
 * <pre>
 *
 * &lt;!DOCTYPE PGML SYSTEM &quot;http://www.ibm.com/pgml.xml&quot;&gt;
 *
 * &lt;!DOCTYPE PGML [ <var>internal DTD subset</var> ]&gt;
 *
 * &lt;!DOCTYPE PGML SYSTEM &quot;http://www.ibm.com/pgml.xml&quot; [ <var>internal DTD subset ]</var>&gt;
 * </pre>
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see org.w3c.dom.DocumentType
 * @see com.ibm.xml.parser.Parent
 * @see com.ibm.xml.parser.Child
 * @see com.ibm.xml.parser.ExternalID
 */
public class DTD extends Parent implements DocumentType {

    /**
     * A constant symbol for use by <code>getInsertableElements()</code> and <code>getAppendableElements()</code> methods.
     */
    public static final String CM_EOC = " *EOC* ";
    /**
     * A constant symbol for use by <code>getInsertableElements()</code> and <code>getAppendableElements()</code> methods.
     */
    public static final String CM_ERROR = " *ERROR* ";
    /**
     * A constant symbol for use by <code>getInsertableElements()</code> and <code>getAppendableElements()</code> methods.
     */
    public static final String CM_PCDATA = "#PCDATA";
 
    private String      name            =   null;
    private String      xmlEncoding     =   null;
    private Vector      internalChildren=   new Vector();
    private Vector      externalChildren=   new Vector();
    private ExternalID  externalID      =   null;
    private Hashtable   elementDecls    =   new Hashtable();
    private Hashtable   attListDecls    =   new Hashtable();
    private Hashtable   notationDecls   =   new Hashtable();
    private boolean     parsingExternal =   false;
            boolean     isPrintInternalDTD = true;
    private Hashtable   idHash          =   null;
            EntityPool  entityPool      =   null;

    /**
     * Constructor.
     */
    public DTD(){
    }
    
    /**
     * Constructor.
     * @param name          The name of this DTD.  This value is also known as the <code>DOCTYPE</code>
     *                      and the root Element Name.
     * @param externalID    The external ID associated with this DTD.
     * @see #getName
     * @see #setName
     * @see com.ibm.xml.parser.ExternalID
     */
    public DTD(String name, ExternalID externalID) {
        this.name       = name;
        this.externalID = externalID;
    }

    /**
     * Clone this DTD Node and its children using the appropriate factories.
     * <strong>Note:</strong> checkID() isn't working in the clone.
     * <p>This method is defined by Child.
     * @return          Cloned DTD Node.
     * @see com.ibm.xml.parser.Child#clone
     * @see #checkID(java.lang.String)
     */
    public synchronized Object clone() {
        checkFactory();
        DTD d = factory.createDTD(this.name, this.externalID);
        d.setFactory(getFactory());
        d.parsingExternal = this.parsingExternal;
        d.isPrintInternalDTD = this.isPrintInternalDTD;
        d.xmlEncoding = this.xmlEncoding;
        d.setParsingExternal(true);
        d.externalChildren.ensureCapacity(this.externalChildren.size());
        for (int i = 0;  i < this.externalChildren.size();  i ++)
            d.addElement((Child)((Child)this.externalChildren.elementAt(i)).clone());
        d.setParsingExternal(false);
        d.internalChildren.ensureCapacity(this.internalChildren.size());
        for (int i = 0;  i < this.internalChildren.size();  i ++)
            d.addElement((Child)((Child)this.internalChildren.elementAt(i)).clone());
        return d;
    }

    /**
     * Returns that this object is a DTD Node. 
     * <p>This method is defined by DOM.
     * @return          DTD Node indicator.
     */
    public int getNodeType() {
        return Child.DOCUMENTTYPE;
    }
    
    /**
     * Returns this DTD's name.  This value is also known as the <code>DOCTYPE</code>
     * and the root Element Name. 
     * <p>This method is defined by DOM.
     * @return      This DTD's name, or <var>null</var> if no name.
     * @see #setName
     */
    public String getName() {
        return this.name;
    }
      
    /**
     * Sets this DTD's name.  This value is also known as the <code>DOCTYPE</code>
     * and the root Element Name. 
     * <p>This method is defined by DOM.
     * @param   name    This DTD's name.
     * @see #getName
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Returns a <var>NodeIterator</var> of the internal DTD subset children; 
     * this iterator is "live" (see Parent#getChildNodes for details).
     * <p>This method is defined by DOM.
     * @return          A NodeIterator of Attributes, or <var>null</var> if no internal DTD subset. 
     * @see #hasChildNodes
     * @see #elements
     * @see #addElement
     * @see #getChildrenArray
     * @see #internalElements
     * @see com.ibm.xml.parser.Parent#getChildNodes
     */
    public NodeIterator getChildNodes() {
        return new VectorNodeIterator(this.internalChildren);
    }
    
    /**
     * Returns <var>true</var> if this DTD has any internal subset children, or <var>false</var> 
     * if this DTD has no internal subset 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 DTD interal subset children exist; otherwise, returns <var>false</var>.
     * @see #getChildNodes
     * @see #elements
     * @see #addElement
     * @see #getChildrenArray
     * @see #internalElements
     */
    public boolean hasChildNodes() {
        return this.internalChildren.size() > 0;
    }
    
    /**
     * Return an Enumeration instance of all internal subset children of this DTD.  
     * @return          An enumeration of all internal subset children of this DTD.
     * @see #getChildNodes
     * @see #hasChildNodes
     * @see #addElement
     * @see #getChildrenArray
     * @see #internalElements
     */
    public Enumeration elements() {
        return this.internalChildren.elements();
    }

    /**
     * Inserts the specified <var>newChild</var> as the last Child in this DTD subset; the
     * <var>newChild</var> is added to the internal or external DTD subset depending on the
     * value of <var>parsingExternal</var>.
     * @param newChild  The Child being inserted.
     * @see #setParsingExternal
     * @see #isParsingExternal
     * @see #getChildNodes
     * @see #hasChildNodes
     * @see #elements
     * @see #getChildrenArray
     * @see #internalElements
     */
    public void addElement(Child newChild) {
        if (newChild == null)  return;
        if (this.parsingExternal)
            this.externalChildren.addElement(newChild);
        else
            this.internalChildren.addElement(newChild);
        newChild.setParent(this);

        if (newChild instanceof ElementDecl) {
            ElementDecl ed = (ElementDecl)newChild;
            this.elementDecls.put(ed.getName(), ed);
        } else if (newChild instanceof Attlist) {
            Attlist al = (Attlist)newChild;
            String en = al.getName();
            Hashtable atthash = (Hashtable)this.attListDecls.get(en);
            if (null == atthash) {
                atthash = new Hashtable();
                this.attListDecls.put(en, atthash);
            }
            for (int i = 0;  i < al.size();  i ++) {
                AttDef ad = al.elementAt(i);
                if (null == atthash.get(ad.getName()))
                    atthash.put(ad.getName(), ad);
            }
        } else if (newChild instanceof TXNotation) {
            TXNotation nn = (TXNotation)newChild;
            this.notationDecls.put(nn.getName(), nn);
        }
    }

    /**
     * Return an array of <var>Child</var> Nodes for all internal subset children of this DTD.  
     * @return          An array of all internal subset children of this DTD, or <var>null</var> if no children.
     * @see #getChildNodes
     * @see #hasChildNodes
     * @see #elements
     * @see #addElement
     * @see #internalElements
     */
    public Child[] getChildrenArray() {
        Child[] ret = new Child[this.internalChildren.size()];
        this.internalChildren.copyInto(ret);
        return ret;
    }
    
    /**
     * Sets the flag which indicates if the internal of external DTD subset is currently
     * being parsed.
     * @param   flag    =true if parsing the external DTD subset; =false if parsing in the 
     *                  internal DTD subset.
     * @see #isParsingExternal
     * @see #addElement
     */
    public void setParsingExternal(boolean flag) {
        this.parsingExternal = flag;
    }
    
    /**
     * Returns whether the internal of external DTD subset is currently being parsed.
     * @return          =true if parsing the external DTD subset; =false if parsing in the 
     *                  internal DTD subset.
     * @see #setParsingExternal
     * @see #addElement
     */
    public boolean isParsingExternal() {
        return this.parsingExternal;
    }

    /**
     * Returns an Enumeration instance of all internal subset children of this DTD.  
     * @return          An enumeration of all internal subset children of this DTD.
     * @see #getChildNodes
     * @see #hasChildNodes
     * @see #elements
     * @see #addElement
     * @see #getChildrenArray
     * @see #externalElements
     */
    public Enumeration internalElements() {
        return elements();
    }
    
    /**
     * Returns an Enumeration instance of all external subset children of this DTD.  
     * @return          An enumeration of all external subset children of this DTD.
     * @see #addElement
     * @see #internalElements
     */
    public Enumeration externalElements() {
        return this.externalChildren.elements();
    }

    /**
     * Invalidate this <code>Parent</code> class function because DTDs have multiple Child lists.
     * @deprecated This method will not be implemented in XML4J.
     * @see #addElement
     */
    public void insert(Child child, int index) {
    }
    
    /**
     * Invalidate this <code>Parent</code> class function because DTDs have multiple Child lists.
     * @deprecated This method will not be implemented in XML4J.
     * @return          Always returns <var>null</var>.
     * @see #addElement
     */
    public Node insertBefore(Node newChild, Node refChild) {
        return null;
    }
    
    /**
     * Invalidate this <code>Parent</code> class function because DTDs have multiple Child lists.
     * @deprecated This method will not be implemented in XML4J.
     * @return          Always returns <var>null</var>.
     * @see #addElement
     */
    public Node replaceChild(Node oldChild, Node newChild) {
        return null;
    }
    
    /**
     * Invalidate this <code>Parent</code> class function because DTDs have multiple Child lists.
     * @deprecated This method will not be implemented in XML4J.
     * @return          Always returns <var>null</var>.
     * @see #addElement
     */
    public Node removeChild(Node oldChild) {
        return null;
    }
    
    /**
     * Returns the external ID associated with this DTD.  An external ID contains system,
     * and optionally, public identifiers.
     * @return          The external ID associated with this DTD, or <var>null</var> if no
     *                  external DTD reference.
     * @see #setExternalID
     * @see com.ibm.xml.parser.ExternalID
     */
    public ExternalID getExternalID() {
        return this.externalID;
    }

    /**
     * Sets the external ID associated with this DTD.  An external ID contains system,
     * and optionally, public identifiers.
     * @param externalID    The external ID associated with this DTD.
     * @see #getExternalID
     * @see com.ibm.xml.parser.ExternalID
     */
    public void setExternalID(ExternalID externalID) {
        this.externalID = externalID;
    }

    /**
     * Return an Enumeration instance of all attribute list declarations for the
     * the specified <var>elementName</var> in this DTD's internal and external subsets. 
     * @param   elementName The Element name to match in the internal and external DTD subsets.
     * @return              An enumeration of all attribute list declarations.
     * @see #addElement
     * @see #getAttributeDeclaration
     * @see #isAttributeDeclared
     */
    public Enumeration getAttributeDeclarations(String elementName) {
        Hashtable atthash = (Hashtable)this.attListDecls.get(elementName);
        if (null == atthash)  atthash = new Hashtable();
        return atthash.elements();
    }

    /**
     * Return an <var>AttDef</var> instance that matches the specified <var>elementName</var>
     * and <var>attributeName</var> in this DTD's internal and external subsets. 
     * @param   elementName   The Element name to match in the internal and external DTD subsets.
     * @param   attributeName The Attribute name to match in <var>elementName</var>.
     * @return                The matching attribute definition, or <var>null</var> if no match.
     * @see #addElement
     * @see #getAttributeDeclarations
     * @see #isAttributeDeclared
     */
    public AttDef getAttributeDeclaration(String elementName, String attributeName) {
        Hashtable atthash = (Hashtable)this.attListDecls.get(elementName);
        if (null == atthash)  return null;
        return (AttDef)atthash.get(attributeName);
    }

    /**
     * Return whether an attribute definition exists that matches the specified <var>elementName</var>
     * and <var>attributeName</var> in this DTD's internal and external subsets. 
     * @param   elementName   The Element name to match in the internal and external DTD subsets.
     * @param   attributeName The Attribute name to match in <var>elementName</var>.
     * @return                =true if the attribute definition exists; otherwise, =false.
     * @see #addElement
     * @see #getAttributeDeclarations
     * @see #getAttributeDeclaration
     */
    public boolean isAttributeDeclared(String elementName, String attributeName) {
        return null != getAttributeDeclaration(elementName, attributeName);
    }

    /**
     * Return an Enumeration instance of all element declarations in this DTD's internal 
     * and external subsets. 
     * @return              An enumeration of all element declarations.
     * @see #addElement
     * @see #getElementDeclaration
     * @see #isElementDeclared
     * @see #makeContentElementList
     */
    public Enumeration getElementDeclarations() {
        return this.elementDecls.elements();
    }

    /**
     * Return an <var>ElementDecl</var> instance that matches the specified <var>elementName</var>
     * in this DTD's internal and external subsets. 
     * @param   elementName   The Element name to match in the internal and external DTD subsets.
     * @return                The matching element definition, or <var>null</var> if no match.
     * @see #addElement
     * @see #getElementDeclarations
     * @see #isElementDeclared
     * @see #makeContentElementList
     */
    public ElementDecl getElementDeclaration(String elementName) {
        return (ElementDecl)this.elementDecls.get(elementName);
    }

    /**
     * Return whether an element definition exists that matches the specified <var>elementName</var>
     * in this DTD's internal and external subsets. 
     * @param   elementName   The Element name to match in the internal and external DTD subsets.
     * @return                =true if the element definition exists; otherwise, =false.
     * @see #addElement
     * @see #getElementDeclarations
     * @see #getElementDeclaration
     * @see #makeContentElementList
     */
    public boolean isElementDeclared(String elementName) {
        return this.elementDecls.containsKey(elementName);
    }

    /**
     * Returns a <var>Vector</var> of the children of the specified <var>elementDeclName</var>
     * as defined by this DTD's internal and external subset.
     * @param   elementDeclName The element definition name to match in the internal and
     *                          external DTD subsets.
     * @return                  The children of the element definition, or <var>null</var>
     *                          if the element is not defined or the element's content model
     *                          is not defined as <var>MODEL_GROUP</var>.
     * @see #addElement
     * @see #getElementDeclarations
     * @see #getElementDeclaration
     * @see com.ibm.xml.parser.ElementDecl
     */
    public Vector makeContentElementList(String elementDeclName) {
        ElementDecl ed = (ElementDecl)this.elementDecls.get(elementDeclName);
        if (null == ed)  return null;
        return ed.getXML4JContentModel().getChildrenOrder();
    }

    /**
     * Return an Enumeration instance of all notations in this DTD's internal and external subsets. 
     * @return              An enumeration of all notations.
     * @see #addElement
     * @see #getNotation
     */
    public Enumeration getNotationEnumeration() {
        return this.notationDecls.elements();
    }

    /**
     * Return an <var>Notation</var> instance that matches the specified <var>notationName</var>
     * in this DTD's internal and external subsets. 
     * @param   notationName  The Notation name to match in the internal and external DTD subsets.
     * @return                The matching Notation, or <var>null</var> if no match.
     * @see #addElement
     * @see #getNotationEnumeration
     */
    public Notation getNotation(String notationName) {
        return (Notation)this.notationDecls.get(notationName);
    }

    /**
     * Sets the value of the XML encoding parameter from the XML prolog declaration (e.g. <code>&lt;?xml encoding="..."&gt;</code>).
     * This value is used when printing the DTD external subset.
     * <p>The supported XML encodings are the intersection of XML-supported code sets and 
     * those supported in JDK 1.1:
     * <ul>
     * <li>UTF-16
     * <li>ISO-10646-UCS-2
     * <li>ISO-10646-UCS-4
     * <li>UTF-8
     * <li>US-ASCII
     * <li>ISO-8859-1 ... ISO-8859-9
     * <li>ISO-2022-JP
     * <li>Shift_JIS
     * <li>EUC-JP
     * <li>GB2312
     * <li>Big5
     * </ul>
     * @param   xmlEncoding Value of the XML encoding parameter.  
     * @see com.ibm.xml.parser.MIME2Java#convert
     * @see #printExternal
     * @see com.ibm.xml.parser.TXDocument#setEncoding
     */
    public void setEncoding(String xmlEncoding) {
        this.xmlEncoding = xmlEncoding;
    }

    /**
     * Print this DTD's external subset in XML format using the specified character encoding.
     * 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 printExternal(java.io.PrintWriter pw, String enc) {
        if (null != this.xmlEncoding) {
            pw.print("<?xml encoding=\"");
            pw.print(this.xmlEncoding);
            pw.println("\"?>");
        }
        for (int i = 0;  i < this.externalChildren.size();  i ++) {
            ((Child)this.externalChildren.elementAt(i)).print(pw, enc);
        }
        pw.flush();
    }

    /**
     * Returns whether the specified <var>element</var> is declared in this DTD's internal and external
     * subsets, AND whether it currently adheres to its defined content model (see ElementDecl for details).
     * @param   element The Element to check in this DTD's internal and external subset.
     * @return          =true if <var>element</var> exists AND it adheres to its content
     *                  model; otherwise =false.
     * @see com.ibm.xml.parser.ElementDecl
     * @see #getContentModel
     * @see #prepareTable
     * @see #getInsertableElements
     * @see #getAppendableElements
     */
    public boolean checkContent(TXElement element) {
        ElementDecl ed = (ElementDecl)this.elementDecls.get(element.getName());
        if (null == ed)  return false;
        return ed.getXML4JContentModel().check(element);
    }
    
    /**
     * Returns the <var>ContentModel</var> for the specified <var>elementDeclName</var>
     * in this DTD's internal and external subset (see ElementDecl for details).
     * @param   elementDeclName The name of element definition to check in this DTD's internal and 
     *                          external subset.
     * @return                  The element definition's content model, or <var>null</var>
     *                          if the element definition does not exist.
     * @see com.ibm.xml.parser.ElementDecl
     * @see #checkContent
     * @see #getContentType
     * @see #prepareTable
     * @see #getInsertableElements
     * @see #getAppendableElements
     */
    public ContentModel getContentModel(String elementDeclName) {
        ElementDecl ed = (ElementDecl)this.elementDecls.get(elementDeclName);
        if (null == ed)  return null;
        return ed.getXML4JContentModel();
    }

    /**
     * Returns the content model type for the specified <var>elementDeclName</var>
     * in this DTD's internal and external subset (see ElementDecl for details).
     * @param   elementDeclName The name of element definition to check in this DTD's internal and 
     *                          external subset.
     * @return                  The element definition's content model type, or <var>null</var>
     *                          if the element definition does not exist.
     *                          Must be one of org.w3c.dom.ElementDefinition#ContentType.
     * @see com.ibm.xml.parser.ElementDecl
     * @see #checkContent
     * @see #getContentModel
     * @see #prepareTable
     * @see #getInsertableElements
     * @see #getAppendableElements
     */
    public int getContentType(String elementDeclName) {
        ElementDecl ed = (ElementDecl)this.elementDecls.get(elementDeclName);
        if (null == ed)  return -1;
        return ed.getXML4JContentModel().getType();
    }
    
    /**
     * Prepare a hash table for use by the <code>getInsertableElements()</code> and
     * <code>getAppendableElements()</code> methods.  Refer to <code>getInsertableElements</code> 
     * for details on how this table is used.
     * @param   elementName The name of Element to use in building the hash table.
     * @return              The constructed hash table of acceptable Elements that may
     *                      be inserted; acceptable Elements are defined by the element
     *                      definition defined in this DTD's internal and external subset.
     * @see #getInsertableElements
     * @see #getAppendableElements
     * @see com.ibm.xml.parser.InsertableElement
     */
    public Hashtable prepareTable(String elementName) {
        ElementDecl ed = (ElementDecl)this.elementDecls.get(elementName);
        if (null == ed)  return null;
        return ed.getXML4JContentModel().prepareTable();
    }
    
    /**
     * Returns a hash table which defines what kind of Element you can insert into the specified <var>index</var> of <var>element</var>
     * according to the document grammar specified by this DTD's internal and external subset.
     * <p>Usage:</p>
     * <blockquote><code>
     *     DTD dtd = ...;<br>
     *     Hashtable table = dtd.prepareTable(element.getName());<br>
     *     dtd.getInsertableElements(element, index, table);
     * </code></blockquote>
     * <p>The resulting <var>table</var> instance consists of Element names as keys and <var>InsertableElement</var> as values.
     * The following information is available about <var>table</var>:</p>
     * <dl>
     *   <dt><code>insertableElement = (InsertableElement)hashtable.get("<var>elementName</var>")</code>
     *   <dd>If <code>insertableElement != null</code> AND <code>insertableElement.status == true</code>
     *       you can insert <var>elementName</var> Elements under <var>element</var>.  
     *       Note that this does not mean that <var>element</var> will be guaranteed to have 
     *       correct contents after the insertion. Otherwise, the <var>elementName</var> 
     *       may not be inserted.
     *   <dd>When the <var>element</var>'s model group is <var>ANY</var> AND <code>insertableElement == null</code>,
     *       <var>elementName</var> may be inserted.  Otherwise, <var>elementName</var> may 
     *       not be inserted.
     *   <dt><code>insertableElement = (InsertableElement)hashtable.get(DTD.CM_PCDATA)</code>
     *   <dd>When <code>insertableElement.status == true</code>, you can insert any
     *       a TXText Node under <var>element</var>.  Note that this does not mean
     *       that <var>element</var> will be guaranteed to have correct contents after the 
     *       insertion.   Otherwise, a TXText Node may not be inserted.
     *   <dt><code>insertableElement = (InsertableElement)hashtable.get(DTD.CM_ERROR)</code>
     *	 <dd><code>insertableElement.status</code> indicates whether <var>element</var> has 
     *       correct and complete contents in the subsequence of children [0, <var>index</var>-1].
     *	     When <var>insertableElement.status</var> is <code>=true</code>,
     *	     <var>insertableElement.index</var> is the index of the first incorrect Child 
     *       Node of the specified <var>element</var>.  Note that you might still be able to 
     *       insert Elements under <var>element</var> even if 
     *       <code>insertableElement.status</code> is <code>=true</code>.
     *  </dl>
     * @param   element     The TXElement to use in building the hash table.
     * @param   index       The 0-based index of <var>element</var>'s children which are
     *                      to be examined for validity in preparation for insertion.
     * @param   hashtable   The hash table built by <code>prepareTable</code>.
     * @return              The constructed hash table, or <var>null</var> if unable
     *                      to build the hash table with the specified <var>elementName</var>.
     * @see #prepareTable
     * @see #getAppendableElements
     * @see #getInsertableElementsForValidContent
     * @see com.ibm.xml.parser.InsertableElement
     */
    public Hashtable getInsertableElements(TXElement element, int index, Hashtable hashtable) {
        // This method is not used by the parser.
        ContentModel cm = getContentModel(element.getName());
        if (null == cm)  return null;
        return cm.getInsertableElements(element, index, hashtable, false);
    }
    
    /**
     * Returns a hash table which defines what kind of Element you can append onto specified <var>element</var>
     * according to the document grammar specified by this DTD's internal and external subset.
     * Refer to <code>getInsertableElements</code> for details on how this table is used.
     * @param   element     The TXElement to use in building the hash table.
     * @param   hashtable   The hash table built by <code>prepareTable</code>.
     * @return              The constructed hash table, or <var>null</var> if unable
     *                      to build the hash table with the specified <var>elementName</var>.
     * @see #prepareTable
     * @see #getInsertableElements
     * @see com.ibm.xml.parser.InsertableElement
     */
    public Hashtable getAppendableElements(TXElement element, Hashtable hashtable) {
        return getInsertableElements(element, element.size(), hashtable);
    }

    /**
     * Returns a hash table which defines what kind of Element you can insert into the specified <var>index</var> of <var>element</var>
     * according to the document grammar specified by this DTD's internal and external subset.
     * This method can be used when <VAR>element</VAR> already has valid content.
     * After insertion of returned element to <VAR>element</VAR>, <VAR>element</VAR> will have
     * valid content.
     *
     * @param   element     The TXElement to use in building the hash table.
     * @param   index       The 0-based index of <var>element</var>'s children which are
     *                      to be examined for validity in preparation for insertion.
     * @param   hashtable   The hash table built by <code>prepareTable</code>.
     * @return              The constructed hash table, or <var>null</var> if unable
     *                      to build the hash table with the specified <var>elementName</var>.
     * @see #prepareTable
     * @see #getInsertableElements
     * @see com.ibm.xml.parser.InsertableElement
     */
    public Hashtable getInsertableElementsForValidContent(TXElement element,
                                                          int index, Hashtable hashtable) {
        // This method is not used by the parser.
        ContentModel cm = getContentModel(element.getName());
        if (null == cm)  return null;
        return cm.getInsertableElements(element, index, hashtable, true);
    }

    /**
     * Register the identifier attribute associated with the specified <var>element</var> in the XML document.
     * Registered identifiers must be unique within an XML document.
     * This method is automatically called while parsing a document with DTD.
     *
     * @param   element     Element to be registered.
     * @param   id          Identifier to be registered.
     * @return              =true if successfully register; =false if <var>id</var> already
     *                      is registered in the XML document.
     * @see #checkID
     * @see #IDs
     */
    public boolean registID(Element element, String id) {
        if (null == this.idHash)  this.idHash = new Hashtable();
        if (this.idHash.containsKey(id))  return false; // already exists
        this.idHash.put(id, element);
        return true;
    }

    /**
     * Returns the <var>Element</var> associated with the specified <var>id</var> in the
     * XML document.
     * @param   id          Identifier to be matched.
     * @return              Matching Element, or <var>null</var> if <var>id</var> is not registered.
     * @see #registID
     * @see #IDs
     */
    public Element checkID(String id) {
        if (null == this.idHash)  return null;
        return (Element)this.idHash.get(id);
    }

    /**
     * Returns an <var>Enumeration</var> of all Element IDs currently registered in the XML document.
     * @return              An enumeration of <var>String</var> representing all currently
     *                      registered Element IDs.
     * @see #checkID
     * @see #IDs
     */
    public Enumeration IDs() {
        return null == this.idHash ? new Hashtable().keys() : this.idHash.keys();
    }
    
    /**
     * Returns an <var>Enumeration</var> of all entities defined in this DTD's internal and
     * external subsets.
     * @return              An enumeration of <var>EntityValue</var> representing all defined
     *                      entities in this DTD's internal and external subsets.
     * @see #getEntity
     * @see com.ibm.xml.parser.EntityValue
     */
    public Enumeration getEntities() {
        return this.entityPool.elements();
    }

    /**
     * Returns an Entity in this DTD's internal and external subsets that matches the
     * specified <var>name</var> and is of the specified <var>isParameter</var> type.
     * @param   name        The Entity name to match against in this DTD's internal and external
     *                      DTD subset.
     * @param   isParameter =true if a parameter Entity; otherwise =false.
     * @return              The matching Entity, or <var>null</var> if no match.
     * @see #getEntities
     */
    public Entity getEntity(String name, boolean isParameter) {
        Entity ret;
        if (isParameter) {
            ret = this.entityPool.referPara(name);
        } else {
            ret = this.entityPool.refer(name);
        }
        return ret;
    }
    
    /**
     * Returns the <var>Node</var> whose children constitute the set of parameter entities
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are the parameter entitities for this DTD, or
     *                  <var>null</var> if no parameter entitities are defined.
     * @see #setParameterEntities
     */
    public Node getParameterEntities() {
        return null;
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute the set of parameter entities
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param node      Node whose children are the parameter entitities for this DTD.
     * @see #getParameterEntities
     */
    public void setParameterEntities(Node node) {
    }
    
    /**
     * Returns the <var>Node</var> whose children constitute this DTD's external subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are this DTD's external subset, or <var>null</var>
     *                  if no external DTD subset exists.
     * @see #setExternalSubset
     */
    public Node getExternalSubset() {
        return null;
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute this DTD's external subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param   node    Node whose children are this DTD's external subset.
     * @see #getExternalSubset
     */
    public void setExternalSubset(Node node) {
    }

    /**
     * Returns the <var>Node</var> whose children constitute the set of general entities
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are the general entitities for this DTD, or
     *                  <var>null</var> if no general entitities are defined.
     * @see #setGeneralEntities
     */
    public Node getGeneralEntities() {
        return null;
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute the set of general entities
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param   node    Node whose children are the general entitities for this DTD.
     * @see #getGeneralEntities
     */
    public void setGeneralEntities(Node arg) {
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute this DTD's internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are this DTD's internal subset, or <var>null</var>
     *                  if no internal DTD subset exists.
     * @see #setInternalSubset
     */
    public Node getInternalSubset() {
        return null;
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute this DTD's internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param   node    Node whose children are this DTD's internal subset.
     * @see #getInternalSubset
     */
    public void setInternalSubset(Node node) {
    }
    
    /**
     * Returns the <var>Node</var> whose children constitute the set of notations
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are the notations for this DTD, or
     *                  <var>null</var> if no notations are defined.
     * @see #setNotations
     */
    public Node getNotations(){
        return null;
    }
    
    /**
     * Sets the <var>Node</var> whose children constitute the set of notations
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param   node    Node whose children are the notations for this DTD.
     * @see #getNotations
     */
    public void setNotations(Node notations) {
    }
    
    /**
     * Returns the <var>Node</var> whose children constitute the set of element types
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @return          Node whose children are the element type for this DTD, or
     *                  <var>null</var> if no element types are defined.
     * @see #setElementTypes
     */
    public Node getElementTypes() {
        return null;
    }

    /**
     * Sets the <var>Node</var> whose children constitute the set of element types
     * that were defined within this DTD's external and internal subset.
     * <p>This method is defined by DOM.
     * <p>This method is CURRENTLY NOT IMPLEMENTED.
     * @param   node    Node whose children are the element type for this DTD.
     * @see #getElementTypes
     */
    public void setElementTypes(Node node) {
    }

    /**
     * Return a number of Nodes in an internal subset.
     */
    public int getInternalSize() {
        return this.internalChildren.size();
    }

    /**
     * Return a number of Nodes in an external subset.
     */
    public int getExternalSize() {
        return this.externalChildren.size();
    }

    /**
     * Sets the flag which indicates if the <CODE>print()</CODE> method prints internal subset.
     *
     * @see #isPrintInternalDTD
     * @see com.ibm.xml.parser.Child#print
     */
    public void setPrintInternalDTD(boolean flag) {
        this.isPrintInternalDTD = flag;
    }
    /**
     * Returns whether the <CODE>print()</CODE> method prints internal subse.
     *
     * @see #setPrintInternalDTD
     * @see com.ibm.xml.parser.Child#print
     */
    public boolean isPrintInternalDTD() {
        return this.isPrintInternalDTD;
    }

    /**
     * Called by Parser.
     */
    void setEntityPool(EntityPool ent) {
        this.entityPool = ent;
    }
    
}
