/*
 * $Id: DomDoctype.java,v 1.1.1.1 2002/09/30 15:08:51 smartine Exp $
 * Copyright (C) 1999-2000 David Brownell
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package xml.dom;

import org.w3c.dom.*;


// $Id: DomDoctype.java,v 1.1.1.1 2002/09/30 15:08:51 smartine Exp $

/**
 * <p> "DocumentType" implementation (with no extensions for supporting
 * any document typing information).  This is a non-core DOM class,
 * supporting the "XML" feature. </p>
 *
 * <p> <em>Few XML applications will actually care about this partial
 * DTD support</em>, since it doesn't expose any (!) of the data typing
 * facilities which can motivate applications to use DTDs.  It does not
 * expose element content models, or information about attribute typing
 * rules.  Plus the information it exposes isn't very useful; as one example,
 * DOM exposes information about unparsed ENTITY objects, which is only used
 * with certain element attributes, but does not expose the information about
 * those attributes which is needed to apply that data! </p>
 *
 * <p> Also, note that there are no nonportable ways to associate even the
 * notation and entity information exposed by DOM with a DocumentType.  While
 * there is a DOM L2 method to construct a DocumentType, it only gives access
 * to the textual content of the &lt;!DOCTYPE ...&gt; declaration.  </p>
 *
 * <p> In short, <em>you are strongly advised not to rely on this incomplete
 * DTD functionality</em> in your application code.</p>
 *
 * @see DomEntity
 * @see DomEntityReference
 * @see DomNotation
 *
 * @author David Brownell 
 * @version $Date: 2002/09/30 15:08:51 $
 */
public class DomDoctype extends DomExtern implements DocumentType
{
    private DomNamedNodeMap	notations;
    private DomNamedNodeMap	entities;
    private DOMImplementation	implementation;
    private String		subset;


    /**
     * Constructs a DocumentType node associated with the specified
     * implementation, with the specified name.
     *
     * <p>This constructor should only be invoked by a DOMImplementation as
     * part of its createDocumentType functionality, or through a subclass
     * which is similarly used in a "Sub-DOM" style layer.
     *
     * <p> Note that at this time there is no standard SAX API granting
     * access to the internal subset text, so that relying on that value
     * is not currently portable.
     *
     * @param impl The implementation with which this object is associated
     * @param name Name of this root element
     * @param publicId If non-null, provides the external subset's
     *	PUBLIC identifier
     * @param systemId If non-null, provides the external subset's
     *	SYSTEM identifier
     * @param internalSubset Provides the literal value (unparsed, no
     *	entities expanded) of the DTD's internal subset.
     */
    protected DomDoctype (
	DOMImplementation impl,
	String name,
	String publicId,
	String systemId,
	String internalSubset
    )
    {
	super (null, DOCUMENT_TYPE_NODE, name, publicId, systemId);
	implementation = impl;
	subset = internalSubset;
    }


    /**
     * <b>DOM L1</b>
     * Returns the root element's name (just like getNodeName).
     */
    final public String getName () { return getNodeName (); }


    /**
     * <b>DOM L1</b>
     * Returns information about any general entities declared
     * in the DTD.
     *
     * <p><em>Note:  DOM L1 doesn't throw a DOMException here, but
     * then it doesn't have the strange construction rules of L2.</em>
     *
     * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
     *	is not associated with a document.
     */
    public NamedNodeMap getEntities ()
    {
	if (entities == null) {
	    if (getOwnerDocument () == null)
		throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
	    entities = new DomNamedNodeMap (getOwnerDocument ());
	}
	return entities;
    }


    /**
     * Records the declaration of a general entity in this DocumentType.
     *
     * @param name Name of the entity
     * @param publicId If non-null, provides the entity's PUBLIC identifier
     * @param systemId Provides the entity's SYSTEM identifier
     * @param notation If non-null, provides the entity's notation
     *	(indicating an unparsed entity)
     * @return The Entity that was declared.
     *
     * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
     *	DocumentType is no longer writable.
     * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
     *	is not associated with a document.
     */
    public Entity declareEntity (
	String name,
	String publicId,
	String systemId,
	String notation
    )
    {
	DomEntity entity;

	if (isReadonly ())
	    throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
	getEntities ();

	DomDocument.verifyXmlName (name);

	entity = new DomEntity (getOwnerDocument (),
		name, publicId, systemId, notation);
	entities.setNamedItem (entity);
	return entity;
    }

    /**
     * <b>DOM L1</b>
     * Returns information about any notations declared in the DTD.
     *
     * <p><em>Note:  DOM L1 doesn't throw a DOMException here, but
     * then it doesn't have the strange construction rules of L2.</em>
     *
     * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
     *	is not associated with a document.
     */
    public NamedNodeMap getNotations ()
    {
	if (notations == null) {
	    if (getOwnerDocument () == null)
		throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
	    notations = new DomNamedNodeMap (getOwnerDocument ());
	}
	return notations;
    }


    /**
     * Records the declaration of a notation in this DocumentType.
     *
     * @param name Name of the notation
     * @param publicId If non-null, provides the notation's PUBLIC identifier
     * @param systemId If non-null, provides the notation's SYSTEM identifier
     * @return The notation that was declared.
     *
     * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
     *	DocumentType is no longer writable.
     * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
     *	is not associated with a document.
     */
    public Notation declareNotation (
	String name,
	String publicId,
	String systemId
    )
    {
	DomNotation notation;

	if (isReadonly ())
	    throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
	getNotations ();

	DomDocument.verifyXmlName (name);

	notation = new DomNotation (getOwnerDocument (),
		name, publicId, systemId);
	notations.setNamedItem (notation);
	return notation;
    }


    /**
     * <b>DOM L2</b>
     * Returns the internal subset of the document, as a string of unparsed
     * XML declarations (and comments, PIs, whitespace); or returns null if
     * there is no such subset.  There is no vendor-independent expectation
     * that this attribute be set, or that declarations found in it be
     * reflected in the <em>entities</em> or <em>notations</em> attributes
     * of this Document "Type" object.
     *
     * <p> Some application-specific XML profiles require that documents
     * only use specific PUBLIC identifiers, without an internal subset
     * to modify the interperetation of the declarations associated with
     * that PUBLIC identifier through some standard.
     */
    public String getInternalSubset ()
    {
	return subset;
    }
    

    /**
     * Sets the internal "readonly" flag so the node and its associated
     * data (only lists of entities and notations, no type information
     * at the moment) can't be changed.
     */
    public void makeReadonly ()
    {
	super.makeReadonly ();
	entities.makeReadonly ();
	notations.makeReadonly ();
    }


    /**
     * <b>DOM L2</b>
     * Consults the DOM implementation to determine if the requested
     * feature is supported.
     */
    final public boolean supports (String feature, String version)
    {
	return implementation.hasFeature (feature, version);
    }

    
    /**
     * Returns the implementation associated with this document type.
     */
    final public DOMImplementation getImplementation ()
    {
	return implementation;
    }
}
