- /*
 - * $Id: XmlDocument.java,v 1.10 2001/11/09 08:44:06 edwingo Exp $
 - *
 - * The Apache Software License, Version 1.1
 - *
 - *
 - * Copyright (c) 2000 The Apache Software Foundation. All rights
 - * reserved.
 - *
 - * Redistribution and use in source and binary forms, with or without
 - * modification, are permitted provided that the following conditions
 - * are met:
 - *
 - * 1. Redistributions of source code must retain the above copyright
 - * notice, this list of conditions and the following disclaimer.
 - *
 - * 2. Redistributions in binary form must reproduce the above copyright
 - * notice, this list of conditions and the following disclaimer in
 - * the documentation and/or other materials provided with the
 - * distribution.
 - *
 - * 3. The end-user documentation included with the redistribution,
 - * if any, must include the following acknowledgment:
 - * "This product includes software developed by the
 - * Apache Software Foundation (http://www.apache.org/)."
 - * Alternately, this acknowledgment may appear in the software itself,
 - * if and wherever such third-party acknowledgments normally appear.
 - *
 - * 4. The names "Crimson" and "Apache Software Foundation" must
 - * not be used to endorse or promote products derived from this
 - * software without prior written permission. For written
 - * permission, please contact apache@apache.org.
 - *
 - * 5. Products derived from this software may not be called "Apache",
 - * nor may "Apache" appear in their name, without prior written
 - * permission of the Apache Software Foundation.
 - *
 - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 - * SUCH DAMAGE.
 - * ====================================================================
 - *
 - * This software consists of voluntary contributions made by many
 - * individuals on behalf of the Apache Software Foundation and was
 - * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
 - * http://www.sun.com. For more information on the Apache Software
 - * Foundation, please see <http://www.apache.org/>.
 - */
 - package org.apache.crimson.tree;
 - import java.io.InputStream;
 - import java.io.OutputStreamWriter;
 - import java.io.OutputStream;
 - import java.io.Writer;
 - import java.io.IOException;
 - import java.util.Dictionary;
 - import java.util.Enumeration;
 - import java.util.Hashtable;
 - import java.util.Locale;
 - import org.w3c.dom.*;
 - import org.xml.sax.InputSource;
 - import org.xml.sax.SAXException;
 - import org.xml.sax.SAXParseException;
 - import org.xml.sax.helpers.DefaultHandler;
 - import org.xml.sax.helpers.XMLReaderFactory;
 - import org.xml.sax.XMLReader;
 - import org.apache.crimson.parser.Parser2;
 - import org.apache.crimson.parser.Resolver;
 - import org.apache.crimson.parser.ValidatingParser;
 - import org.apache.crimson.util.MessageCatalog;
 - import org.apache.crimson.util.XmlNames;
 - /**
 - * This class implements the DOM <em>Document</em> interface, and also
 - * provides static factory methods to create document instances. Instances
 - * represent the top level of an XML 1.0 document, typically consisting
 - * of processing instructions followed by one tree of XML data. These
 - * documents may be written out for transfer or storage using a variety
 - * of text encodings.
 - *
 - * <P> The static factory methods do not offer any customization options.
 - * in particular, they do not enforce XML Namespaces when parsing, do not
 - * offer customizable element factories, and discard certain information
 - * which is not intended to be significant to applications. If your
 - * application requires more sophisticated use of DOM, you may need
 - * to use SAX directly with an <em>XmlDocumentBuilder</em>.
 - *
 - * <P> <b>Note: element factories are deprecated</b> because they are
 - * non-standard. They are only provided in this version for backwards
 - * compatibility. Instances are factories for their subsidiary nodes, but
 - * applications may provide their own element factory to bind element tags
 - * to particular DOM implementation classes (which must subclass
 - * ElementNode). For example, a factory may use a set of classes which
 - * support the HTML DOM methods, or which support methods associated with
 - * XML vocabularies for specialized problem domains as found within
 - * Internet Commerce systems. For example, an element tag
 - * <code><PurchaseOrder></code> could be mapped to a
 - * <code>com.startup.commerce.PurchaseOrder</code> class. The factory can
 - * also use XML Namespace information, if desired.
 - *
 - * <P> Since DOM requires nodes to be owned exclusively by one document,
 - * they can't be moved from one document to another using DOM APIs. This
 - * class provides an <em>changeNodeOwner</em> functionality which may be
 - * used to change the document associated with a node, and with any of its
 - * children.
 - *
 - * <P> <em> Only the core DOM model is supported here, not the HTML support.
 - * Such support basically adds a set of convenience element types, and so
 - * can be implemented through element factories and document subclasses.</em>
 - *
 - * @see XmlDocumentBuilder
 - *
 - * @author David Brownell
 - * @author Rajiv Mordani
 - * @version $Revision: 1.10 $
 - */
 - public class XmlDocument extends ParentNode implements DocumentEx
 - {
 - // package private (with jdk 1.1 'javac' bug workaround)
 - static /* final */ String eol;
 - static {
 - String temp;
 - try { temp = System.getProperty ("line.separator", "\n"); }
 - catch (SecurityException e) { temp = "\n"; }
 - eol = temp;
 - }
 - static final MessageCatalog catalog = new Catalog ();
 - private Locale locale = Locale.getDefault ();
 - private String systemId;
 - private ElementFactory factory;
 - // package private
 - int mutationCount;
 - boolean replaceRootElement;
 - /**
 - * Constructs an empty document object.
 - */
 - public XmlDocument() {
 - // No-op
 - }
 - /**
 - * Construct an XML document from the data at the specified URI,
 - * optionally validating. This uses validating parser if
 - * validation is requested, otherwise uses non-validating
 - * parser. XML Namespace conformance is not tested when parsing.
 - *
 - * @param documentURI The URI (normally URL) of the document
 - * @param doValidate If true, validity errors are treated as fatal
 - *
 - * @exception IOException as appropriate
 - * @exception SAXException as appropriate
 - * @exception SAXParseException (with line number information)
 - * for parsing errors
 - * @exception IllegalStateException at least when the parser
 - * is configured incorrectly
 - * @deprecated Use JAXP javax.xml.parsers package instead
 - */
 - public static XmlDocument createXmlDocument (
 - String documentURI,
 - boolean doValidate
 - ) throws IOException, SAXException
 - {
 - return createXmlDocument (new InputSource (documentURI), doValidate);
 - }
 - /**
 - * Construct an XML document from the data at the specified URI,
 - * using the nonvalidating parser. XML Namespace conformance
 - * is not tested when parsing.
 - *
 - * @param documentURI The URI (normally URL) of the document
 - *
 - * @exception IOException as appropriate
 - * @exception SAXException as appropriate
 - * @exception SAXParseException (with line number information)
 - * for parsing errors
 - * @exception IllegalStateException at least when the parser
 - * is configured incorrectly
 - * @deprecated Use JAXP javax.xml.parsers package instead
 - */
 - public static XmlDocument createXmlDocument (String documentURI)
 - throws IOException, SAXException
 - {
 - return createXmlDocument (new InputSource (documentURI), false);
 - }
 - /**
 - * Construct an XML document from input stream, optionally validating.
 - * This document must not require interpretation of relative URLs,
 - * since the base URL is not known. This uses the validating parser
 - * if validation is requested, otherwise uses the non-validating
 - * parser. XML Namespace conformance is not tested when parsing.
 - *
 - * @param in Holds xml document
 - * @param doValidate If true, validity errors are treated as fatal
 - *
 - * @exception IOException as appropriate
 - * @exception SAXException as appropriate
 - * @exception SAXParseException (with line number information)
 - * for parsing errors
 - * @exception IllegalStateException at least when the parser
 - * is configured incorrectly
 - * @deprecated Use JAXP javax.xml.parsers package instead
 - */
 - public static XmlDocument createXmlDocument (
 - InputStream in,
 - boolean doValidate
 - ) throws IOException, SAXException
 - {
 - return createXmlDocument (new InputSource (in), doValidate);
 - }
 - /**
 - * Construct an XML document from the data in the specified input
 - * source, optionally validating. This uses the validating parser
 - * if validation is requested, otherwise uses the non-validating
 - * parser. XML Namespace conformance is not tested when parsing.
 - *
 - * @param in The input source of the document
 - * @param doValidate If true, validity errors are treated as fatal
 - *
 - * @exception IOException as appropriate
 - * @exception SAXException as appropriate
 - * @exception SAXParseException (with line number information)
 - * for parsing errors
 - * @exception IllegalStateException at least when the parser
 - * is configured incorrectly
 - * @deprecated Use JAXP javax.xml.parsers package instead
 - */
 - public static XmlDocument createXmlDocument(InputSource in,
 - boolean doValidate)
 - throws IOException, SAXException
 - {
 - // Create XMLReader allowing user to override using system property
 - // String defaultReader = "org.apache.xerces.parsers.SAXParser";
 - String defaultReader = "org.apache.crimson.parser.XMLReaderImpl";
 - String prop;
 - try {
 - prop = System.getProperty("org.xml.sax.driver", defaultReader);
 - } catch (SecurityException se) {
 - // This can happen if we are running as an applet
 - prop = defaultReader;
 - }
 - XMLReader xmlReader = XMLReaderFactory.createXMLReader(prop);
 - //
 - // Namespace related features needed for XmlDocumentBuilder
 - //
 - String namespaces = "http://xml.org/sax/features/namespaces";
 - xmlReader.setFeature(namespaces, true);
 - String nsPrefixes = "http://xml.org/sax/features/namespace-prefixes";
 - xmlReader.setFeature(nsPrefixes, true);
 - // Create XmlDocumentBuilder instance
 - XmlDocumentBuilder builder = new XmlDocumentBuilder();
 - // Use as the ContentHandler
 - xmlReader.setContentHandler(builder);
 - // org.xml.sax.ext.LexicalHandler
 - String lexHandler = "http://xml.org/sax/properties/lexical-handler";
 - xmlReader.setProperty(lexHandler, builder);
 - // org.xml.sax.ext.DeclHandler
 - String declHandler
 - = "http://xml.org/sax/properties/declaration-handler";
 - xmlReader.setProperty(declHandler, builder);
 - // DTDHandler
 - xmlReader.setDTDHandler(builder);
 - // Validation
 - String validation = "http://xml.org/sax/features/validation";
 - xmlReader.setFeature(validation, doValidate);
 - // If validating, use an error handler that throws an exception for
 - // validation errors.
 - if (doValidate) {
 - xmlReader.setErrorHandler(new DefaultHandler() {
 - public void error(SAXParseException e) throws SAXException {
 - throw e;
 - }
 - });
 - }
 - builder.setDisableNamespaces(true);
 - // Parse the input
 - xmlReader.parse(in);
 - return builder.getDocument();
 - }
 - /**
 - * Returns the locale to be used for diagnostic messages.
 - */
 - public Locale getLocale ()
 - { return locale; }
 - /**
 - * Assigns the locale to be used for diagnostic messages.
 - * Multi-language applications, such as web servers dealing with
 - * clients from different locales, need the ability to interact
 - * with clients in languages other than the server's default.
 - * When an XmlDocument is created, its locale is the default
 - * locale for the virtual machine.
 - *
 - * @see #chooseLocale
 - */
 - public void setLocale (Locale locale)
 - {
 - if (locale == null)
 - locale = Locale.getDefault ();
 - this.locale = locale;
 - }
 - /**
 - * Chooses a client locale to use for diagnostics, using the first
 - * language specified in the list that is supported by this DOM
 - * implementation. That locale is then automatically assigned using <a
 - * href="#setLocale(java.util.Locale)">setLocale()</a>. Such a list
 - * could be provided by a variety of user preference mechanisms,
 - * including the HTTP <em>Accept-Language</em> header field.
 - *
 - * @see org.apache.crimson.util.MessageCatalog
 - *
 - * @param languages Array of language specifiers, ordered with the most
 - * preferable one at the front. For example, "en-ca" then "fr-ca",
 - * followed by "zh_CN". Both RFC 1766 and Java styles are supported.
 - * @return The chosen locale, or null.
 - */
 - public Locale chooseLocale (String languages [])
 - {
 - Locale l = catalog.chooseLocale (languages);
 - if (l != null)
 - setLocale (l);
 - return l;
 - }
 - /**
 - * Writes the document in UTF-8 character encoding, as a well formed
 - * XML construct.
 - *
 - * @param out stream on which the document will be written
 - */
 - public void write (OutputStream out) throws IOException
 - {
 - Writer writer = new OutputStreamWriter (out, "UTF8");
 - write (writer, "UTF-8");
 - }
 - /**
 - * Writes the document as a well formed XML construct. If the
 - * encoding can be determined from the writer, that is used in
 - * the document's XML declaration. The encoding name may first
 - * be transformed from a Java-internal form to a standard one;
 - * for example, Java's "UTF8" is the standard "UTF-8".
 - *
 - * <P> <em>Use of UTF-8 (or UTF-16) OutputStreamWriters is strongly
 - * encouraged. </em> All other encodings may lose critical data,
 - * since the standard Java output writers substitute characters
 - * such as the question mark for data which they can't encode in
 - * the current output encoding. The IETF and other organizations
 - * strongly encourage the use of UTF-8; also, all XML processors
 - * are guaranteed to support it.
 - *
 - * @see #write(java.io.Writer,java.lang.String)
 - *
 - * @param out stream on which the document will be written
 - */
 - public void write (Writer out) throws IOException
 - {
 - String encoding = null;
 - if (out instanceof OutputStreamWriter)
 - encoding = java2std (((OutputStreamWriter)out).getEncoding ());
 - write (out, encoding);
 - }
 - //
 - // Try some of the common conversions from Java's internal names
 - // (which must fit in class names) to standard ones understood by
 - // most other code. We use the IETF's preferred names; case is
 - // supposed to be ignored, note.
 - //
 - // package private
 - static String java2std (String encodingName)
 - {
 - if (encodingName == null)
 - return null;
 - //
 - // ISO-8859-N is a common family of 8 bit encodings;
 - // N=1 is the eight bit subset of UNICODE, and there
 - // seem to be at least drafts for some N >10.
 - //
 - if (encodingName.startsWith ("ISO8859_")) // JDK 1.2
 - return "ISO-8859-" + encodingName.substring (8);
 - if (encodingName.startsWith ("8859_")) // JDK 1.1
 - return "ISO-8859-" + encodingName.substring (5);
 - // XXX seven bit encodings ISO-2022-* ...
 - // XXX EBCDIC encodings ...
 - if ("ASCII7".equalsIgnoreCase (encodingName)
 - || "ASCII".equalsIgnoreCase (encodingName))
 - return "US-ASCII";
 - //
 - // All XML parsers _must_ support UTF-8 and UTF-16.
 - // (UTF-16 ~= ISO-10646-UCS-2 plus surrogate pairs)
 - //
 - if ("UTF8".equalsIgnoreCase (encodingName))
 - return "UTF-8";
 - if (encodingName.startsWith ("Unicode"))
 - return "UTF-16";
 - //
 - // Some common Japanese character sets.
 - //
 - if ("SJIS".equalsIgnoreCase (encodingName))
 - return "Shift_JIS";
 - if ("JIS".equalsIgnoreCase (encodingName))
 - return "ISO-2022-JP";
 - if ("EUCJIS".equalsIgnoreCase (encodingName))
 - return "EUC-JP";
 - // else we can't really do anything
 - return encodingName;
 - }
 - /**
 - * Writes the document in the specified encoding, and listing
 - * that encoding in the XML declaration. The document will be
 - * well formed XML; at this time, it will not additionally be
 - * valid XML or standalone, since it includes no document type
 - * declaration.
 - *
 - * <P> Note that the document will by default be "pretty printed".
 - * Extra whitespace is added to indent children of elements according
 - * to their level of nesting, unless those elements have (or inherit)
 - * the <em>xml:space='preserve'</em> attribute value. This space
 - * will be removed if, when the document is read back with DOM, a
 - * call to <em>ElementNode.normalize</em> is made. To avoid this
 - * pretty printing, use a write context configured to disable it,
 - * or explicitly assign an <em>xml:space='preserve'</em> attribute to
 - * the root node of your document.
 - *
 - * <P> Also, if a SAX parser was used to construct this tree, data
 - * will have been discarded. Most of that will be insignificant in
 - * terms of a "logical" view of document data: comments, whitespace
 - * outside of the top level element, the exact content of the XML
 - * directive, and entity references were expanded. However, <em>if a
 - * DOCTYPE declaration was provided, it was also discarded</em>.
 - * Such declarations will often be logically significant, due to the
 - * attribute value defaulting and normalization they can provide.
 - *
 - * <P> In general, DOM does not support "round tripping" data from
 - * XML to DOM and back without losing data about physical structures
 - * and DTD information. "Logical structure" will be preserved.
 - *
 - * @see #setDoctype
 - * @see #writeXml
 - *
 - * @param out the writer to use when writing the document
 - * @param encoding the encoding name to use; this should be a
 - * standard encoding name registered with the IANA (like "UTF-8")
 - * not a Java-internal name (like "UTF8").
 - */
 - public void write (Writer out, String encoding)
 - throws IOException
 - {
 - //
 - // We put a pretty minimal declaration here, which is the
 - // best we can do given SAX input and DOM. For the moment
 - // this precludes our generating "standalone" annotations.
 - //
 - out.write ("<?xml version=\"1.0\"");
 - if (encoding != null) {
 - out.write (" encoding=\"");
 - out.write (encoding);
 - out.write ('\"');
 - }
 - out.write ("?>");
 - out.write (eol);
 - out.write (eol);
 - writeChildrenXml (createWriteContext (out, 0));
 - out.write (eol);
 - out.flush ();
 - }
 - /**
 - * Returns an XML write context set up not to pretty-print,
 - * and which knows about the entities defined for this document.
 - *
 - * @param out stream on which the document will be written
 - */
 - public XmlWriteContext createWriteContext (Writer out)
 - {
 - return new ExtWriteContext (out);
 - }
 - /**
 - * Returns an XML write context which pretty-prints output starting
 - * at a specified indent level, and which knows about the entities
 - * defined for this document.
 - *
 - * @param out stream on which the document will be written
 - * @param level initial indent level for pretty-printing
 - */
 - public XmlWriteContext createWriteContext (Writer out, int level)
 - {
 - return new ExtWriteContext (out, level);
 - }
 - /**
 - * Writes the document out using the specified context, using
 - * an encoding name derived from the stream in the context where
 - * that is possible.
 - *
 - * @see #createWriteContext(java.io.Writer)
 - * @see #createWriteContext(java.io.Writer,int)
 - *
 - * @param context describes how to write the document
 - */
 - public void writeXml (XmlWriteContext context) throws IOException
 - {
 - Writer out = context.getWriter ();
 - String encoding = null;
 - //
 - // XXX as above, it should be possible to be "told" this name
 - // in order to use more standard names. We can pretty print,
 - // or we can use the right encoding name; not both!!
 - //
 - if (out instanceof OutputStreamWriter)
 - encoding = java2std (((OutputStreamWriter)out).getEncoding ());
 - //
 - // We put a pretty minimal declaration here, which is the
 - // best we can do given SAX input and DOM. For the moment
 - // this precludes our generating "standalone" annotations.
 - //
 - out.write ("<?xml version=\"1.0\"");
 - if (encoding != null) {
 - out.write (" encoding=\"");
 - out.write (encoding);
 - out.write ('\"');
 - }
 - out.write ("?>");
 - out.write (eol);
 - out.write (eol);
 - writeChildrenXml (context);
 - }
 - /**
 - * Writes all the child nodes of the document, following each one
 - * with the end-of-line string in use in this environment.
 - */
 - public void writeChildrenXml (XmlWriteContext context) throws IOException
 - {
 - int length = getLength ();
 - Writer out = context.getWriter ();
 - if (length == 0)
 - return;
 - for (int i = 0; i < length; i++) {
 - ((NodeBase)item (i)).writeXml (context);
 - out.write (eol);
 - }
 - }
 - // package private -- overrides base class method
 - void checkChildType (int type)
 - throws DOMException
 - {
 - switch (type) {
 - case ELEMENT_NODE:
 - case PROCESSING_INSTRUCTION_NODE:
 - case COMMENT_NODE:
 - case DOCUMENT_TYPE_NODE:
 - return;
 - default:
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - }
 - }
 - /**
 - * Assigns the URI associated with the document, which is its
 - * system ID.
 - *
 - * @param uri The document's system ID, as used when storing
 - * the document.
 - */
 - final public void setSystemId (String uri)
 - {
 - systemId = uri;
 - }
 - /**
 - * Returns system ID associated with the document, or null if
 - * this is unknown.
 - *
 - * <P> This URI should not be used when interpreting relative URIs,
 - * since the document may be partially stored in external parsed
 - * entities with different base URIs. Instead, use methods in the
 - * <em>XmlReadable</em> interface as the document is being parsed,
 - * so that the correct base URI is available.
 - */
 - final public String getSystemId ()
 - {
 - return systemId;
 - }
 - // DOM support
 - /**
 - * DOM: Appends the specified child node to the document. Only one
 - * element or document type node may be a child of a document.
 - *
 - * @param node the node to be appended.
 - */
 - public Node appendChild (Node n)
 - throws DOMException
 - {
 - if (n instanceof Element && getDocumentElement () != null)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - if (n instanceof DocumentType && getDoctype () != null)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - return super.appendChild (n);
 - }
 - /**
 - * DOM: Inserts the specified child node into the document. Only one
 - * element or document type node may be a child of a document.
 - *
 - * @param n the node to be inserted.
 - * @param refNode the node before which this is to be inserted
 - */
 - public Node insertBefore (Node n, Node refNode)
 - throws DOMException
 - {
 - if (!replaceRootElement && n instanceof Element &&
 - getDocumentElement () != null)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - if (!replaceRootElement && n instanceof DocumentType
 - && getDoctype () != null)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - return super.insertBefore (n, refNode);
 - }
 - /**
 - * <b>DOM:</b> Replaces the specified child with the new node,
 - * returning the original child or throwing an exception.
 - * The new child must belong to this particular document.
 - *
 - * @param newChild the new child to be inserted
 - * @param refChild node which is to be replaced
 - */
 - public Node replaceChild (Node newChild, Node refChild)
 - throws DOMException
 - {
 - if (newChild instanceof DocumentFragment ) {
 - int elemCount = 0;
 - int docCount = 0;
 - replaceRootElement = false;
 - ParentNode frag = (ParentNode) newChild;
 - Node temp;
 - int i = 0;
 - while ((temp = frag.item (i)) != null) {
 - if (temp instanceof Element)
 - elemCount++;
 - else if (temp instanceof DocumentType)
 - docCount++;
 - i++;
 - }
 - if (elemCount > 1 || docCount > 1)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - else
 - replaceRootElement = true;
 - }
 - return super.replaceChild (newChild, refChild);
 - }
 - /** DOM: Returns the DOCUMENT_NODE node type constant. */
 - final public short getNodeType () { return DOCUMENT_NODE; }
 - /** DOM: returns the document type (DTD) */
 - final public DocumentType getDoctype ()
 - {
 - // We ignore comments, PIs, whitespace, etc
 - // and return the first (only!) doctype.
 - for (int i = 0; true; i++) {
 - Node n = item (i);
 - if (n == null)
 - return null;
 - if (n instanceof DocumentType)
 - return (DocumentType) n;
 - }
 - }
 - /**
 - * Establishes how the document prints its document type. If a system
 - * ID (URI) is provided, that is used in a SYSTEM (or PUBLIC, if a public
 - * ID is also provided) declaration. If an internal subset is provided,
 - * that will be printed. The root element in the DTD will be what the
 - * document itself provides.
 - *
 - * @param dtdPublicId Holds a "public identifier" used to identify the
 - * last part of the external DTD subset that is read into the DTD.
 - * This may be omitted, and in any case is ignored unless a system
 - * ID is provided.
 - * @param dtdSystemId Holds a "system identifier" (a URI) used to
 - * identify the last part of the external DTD subset that is read
 - * into the DTD. This may be omitted, in which case the document
 - * type will contain at most an internal subset. This URI should
 - * not be a relative URI unless the document will be accessed in a
 - * context from which that relative URI makes sense.
 - * @param internalSubset Optional; this holds XML text which will
 - * be put into the internal subset. This must be legal syntax,
 - * and it is not tested by this document.
 - */
 - public DocumentType setDoctype (
 - String dtdPublicId,
 - String dtdSystemId,
 - String internalSubset
 - ) {
 - Doctype retval = (Doctype) getDoctype ();
 - if (retval != null)
 - retval.setPrintInfo (dtdPublicId, dtdSystemId,
 - internalSubset);
 - else {
 - retval = new Doctype (dtdPublicId, dtdSystemId,
 - internalSubset);
 - retval.setOwnerDocument (this);
 - insertBefore (retval, getFirstChild ());
 - }
 - return retval;
 - }
 - /**
 - * DOM: Returns the content root element.
 - */
 - public Element getDocumentElement ()
 - {
 - // We ignore comments, PIs, whitespace, etc
 - // and return the first (only!) element.
 - for (int i = 0; true; i++) {
 - Node n = item (i);
 - if (n == null)
 - return null;
 - if (n instanceof Element) {
 - return (Element)n;
 - }
 - }
 - }
 - /**
 - * Assigns the element factory to be used by this document.
 - *
 - * @param factory the element factory to be used; if this is null,
 - * all elements will be implemented by <em>ElementNode</em>.
 - * @deprecated
 - */
 - final public void setElementFactory (ElementFactory factory)
 - {
 - this.factory = factory;
 - }
 - /**
 - * Returns the element factory to be used by this document.
 - * @deprecated
 - */
 - final public ElementFactory getElementFactory ()
 - {
 - return factory;
 - }
 - /**
 - * DOM: Create a new element, associated with this document, with
 - * no children, attributes, or parent, by calling createElementEx.
 - *
 - * @param tagName the tag of the element, used to determine what
 - * type element to create as well as what tag to assign the new node.
 - * @exception IllegalArgumentException if a mapping is defined,
 - * but is invalid because the element can't be instantiated or
 - * does not subclass <em>ElementNode</em>.
 - */
 - public Element createElement(String tagName)
 - throws DOMException
 - {
 - return createElementEx (tagName);
 - }
 - /**
 - * <b>DOM2:</b>
 - * @since DOM Level 2
 - * Warning: Does not work with the deprecated ElementFactory
 - */
 - public Element createElementNS(String namespaceURI, String qualifiedName)
 - throws DOMException
 - {
 - // Check arguments and throw appropriate exceptions
 - ElementNode2.checkArguments(namespaceURI, qualifiedName);
 - ElementNode2 retval = new ElementNode2(namespaceURI, qualifiedName);
 - retval.setOwnerDocument(this);
 - return retval;
 - }
 - /**
 - * Create a new element, associated with this document, with no
 - * children, attributes, or parent. This uses the element factory,
 - * or else directly constructs an ElementNode.
 - *
 - * @param tagName the tag of the element, used to determine what
 - * type element to create as well as what tag to assign the new node.
 - * @exception IllegalArgumentException if a mapping is defined,
 - * but is invalid because the element can't be instantiated or
 - * does not subclass <em>ElementNode</em>.
 - * @deprecated Use the standard method createElement instead
 - */
 - final public ElementEx createElementEx(String tagName)
 - throws DOMException
 - {
 - ElementNode retval;
 - if (!XmlNames.isName (tagName)) {
 - throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
 - }
 - if (factory != null) {
 - // Ask factory to create appropriate ElementNode subtype
 - retval = (ElementNode) factory.createElementEx(tagName);
 - // Set the name of the ElementNode
 - retval.setTag(tagName);
 - } else {
 - retval = new ElementNode(tagName);
 - }
 - retval.setOwnerDocument(this);
 - return retval;
 - }
 - /**
 - * Create a new element, associated with this document, with no
 - * children, attributes, or parent. This uses the element factory,
 - * or else directly constructs an ElementNode.
 - *
 - * @param uri The namespace used to determine what type of element to
 - * create. This is not stored with the element; the element must be
 - * inserted into a DOM tree in a location where namespace declarations
 - * cause its tag to be interpreted correctly.
 - * @param tagName The tag of the element, which should not contain
 - * any namespace prefix.
 - * @exception IllegalArgumentException When a mapping is defined,
 - * but is invalid because the element can't be instantiated or
 - * does not subclass <em>ElementNode</em>.
 - * @deprecated Use the standard method createElementNS instead
 - */
 - final public ElementEx createElementEx(String uri, String tagName)
 - throws DOMException
 - {
 - ElementNode retval;
 - if (!XmlNames.isName(tagName)) {
 - throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
 - }
 - if (factory != null) {
 - // Ask factory to create appropriate ElementNode subtype
 - retval = (ElementNode) factory.createElementEx(uri, tagName);
 - // Set the name of the ElementNode
 - retval.setTag(tagName);
 - } else {
 - retval = new ElementNode(tagName);
 - }
 - retval.setOwnerDocument(this);
 - return retval;
 - }
 - /**
 - * DOM: returns a Text node initialized with the given text.
 - *
 - * @param text The contents of the text node being created, which
 - * should never contain "<em>]]></em>".
 - */
 - public Text createTextNode (String text)
 - {
 - TextNode retval;
 - retval = new TextNode ();
 - retval.setOwnerDocument (this);
 - if (text != null)
 - retval.setText (text.toCharArray ());
 - return retval;
 - }
 - /**
 - * DOM: Returns a CDATA section initialized with the given text.
 - *
 - * @param text the text which the CDATA section will hold, which
 - * should never contain "<em>]]></em>".
 - */
 - public CDATASection createCDATASection (String text)
 - {
 - CDataNode retval = new CDataNode ();
 - if (text != null)
 - retval.setText (text.toCharArray ());
 - retval.setOwnerDocument (this);
 - return retval;
 - }
 - // package private ... convenience rtn, reduced mallocation
 - TextNode newText (char buf [], int offset, int len)
 - throws SAXException
 - {
 - TextNode retval = (TextNode) createTextNode (null);
 - char data [] = new char [len];
 - System.arraycopy (buf, offset, data, 0, len);
 - retval.setText (data);
 - return retval;
 - }
 - /**
 - * DOM: Returns a Processing Instruction node for the specified
 - * processing target, with the given instructions.
 - *
 - * @param target the target of the processing instruction
 - * @param instructions the processing instruction, which should
 - * never contain "<em>?></em>".
 - */
 - public ProcessingInstruction createProcessingInstruction (
 - String target,
 - String instructions
 - ) throws DOMException
 - {
 - if (!XmlNames.isName (target))
 - throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
 - PINode retval = new PINode (target, instructions);
 - retval.setOwnerDocument (this);
 - return retval;
 - }
 - /**
 - * DOM: Returns a valueless attribute node with no default value.
 - *
 - * @param name the name of the attribute.
 - */
 - public Attr createAttribute(String name) throws DOMException {
 - if (!XmlNames.isName(name)) {
 - throw new DomEx(DOMException.INVALID_CHARACTER_ERR);
 - }
 - AttributeNode1 retval = new AttributeNode1(name, "", true, null);
 - retval.setOwnerDocument(this);
 - return retval;
 - }
 - /**
 - * <b>DOM2:</b>
 - * @since DOM Level 2
 - */
 - public Attr createAttributeNS(String namespaceURI, String qualifiedName)
 - throws DOMException
 - {
 - AttributeNode.checkArguments(namespaceURI, qualifiedName);
 - AttributeNode retval = new AttributeNode(namespaceURI, qualifiedName,
 - "", true, null);
 - retval.setOwnerDocument(this);
 - return retval;
 - }
 - /**
 - * DOM: creates a comment node.
 - *
 - * @param data The characters which will be in the comment.
 - * This should not include the "<em>--</em>" characters.
 - */
 - public Comment createComment (String data)
 - {
 - CommentNode retval = new CommentNode (data);
 - retval.setOwnerDocument (this);
 - return retval;
 - }
 - /** DOM: returns null. */
 - public Document getOwnerDoc ()
 - {
 - return null;
 - }
 - /**
 - * The <code>DOMImplementation</code> object that handles this document.
 - */
 - public DOMImplementation getImplementation() {
 - return DOMImplementationImpl.getDOMImplementation();
 - }
 - /**
 - * DOM: Creates a new document fragment.
 - */
 - public DocumentFragment createDocumentFragment ()
 - {
 - DocFragNode retval = new DocFragNode ();
 - retval.setOwnerDocument (this);
 - return retval;
 - }
 - /**
 - * DOM: Creates an entity reference to the named entity.
 - * Note that the entity must already be defined in the document
 - * type, and that the name must be a legal entity name.
 - *
 - * @param name the name of the the parsed entity
 - */
 - public EntityReference createEntityReference (String name)
 - throws DOMException
 - {
 - if (!XmlNames.isName (name))
 - throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
 - EntityRefNode retval = new EntityRefNode (name);
 - retval.setOwnerDocument (this);
 - return retval;
 - }
 - /** DOM: Returns the string "#document". */
 - final public String getNodeName () { return "#document"; }
 - /**
 - * DOM: Returns a copy of this document.
 - *
 - * <P> <em>Note:</em> At this time, any element factory or document
 - * type associated with this document will not be cloned.
 - *
 - * @param deep if true, child nodes are also cloned.
 - */
 - public Node cloneNode (boolean deep)
 - {
 - XmlDocument retval = new XmlDocument ();
 - retval.systemId = systemId;
 - // XXX clone the element factory ...
 - if (deep) {
 - Node node;
 - for (int i = 0; (node = item (i)) != null; i++) {
 - if (node instanceof DocumentType) {
 - // XXX recreate
 - continue;
 - }
 - node = node.cloneNode (true);
 - retval.changeNodeOwner (node);
 - retval.appendChild (node);
 - }
 - }
 - return retval;
 - }
 - /**
 - * Changes the "owner document" of the given node, and all child
 - * and associated attribute nodes, to be this document. If the
 - * node has a parent, it is first removed from that parent.
 - * <b>Obsolete</b> Use importNode method instead. Still useful for
 - * internal implementation.
 - *
 - * @param node
 - * @exception DOMException WRONG_DOCUMENT_ERROR when attempting
 - * to change the owner for some other DOM implementation<P>
 - * HIERARCHY_REQUEST_ERROR when the node is a document, document
 - * type, entity, or notation; or when it is an attribute associated
 - * with an element whose owner is not being (recursively) changed.
 - */
 - final public void changeNodeOwner (Node node)
 - throws DOMException
 - {
 - TreeWalker walker;
 - NodeBase n;
 - if (node.getOwnerDocument () == this)
 - return;
 - if (!(node instanceof NodeBase))
 - throw new DomEx (DomEx.WRONG_DOCUMENT_ERR);
 - switch (node.getNodeType ()) {
 - // Documents _are_ owners; can't switch identities
 - case Node.DOCUMENT_NODE:
 - // Entities, Notations only live in doctypes ... we
 - // don't support changing their ownership at this time
 - case Node.ENTITY_NODE:
 - case Node.NOTATION_NODE:
 - case Node.DOCUMENT_TYPE_NODE:
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - }
 - //
 - // If node is an attribute, its "scoped" by one element...
 - // and if that scope hasn't been changed (i.e. if this isn't
 - // a recursive call) we can't really fix anything!
 - //
 - if (node instanceof AttributeNode) {
 - AttributeNode attr = (AttributeNode) node;
 - Element scope = attr.getOwnerElement();
 - if (scope != null && scope.getOwnerDocument () != this)
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - }
 - // unparent node if needed
 - n = (NodeBase) node.getParentNode ();
 - if (n != null)
 - n.removeChild (node);
 - // change any children (including self)
 - for (walker = new TreeWalker (node),
 - n = (NodeBase) walker.getCurrent ();
 - n != null;
 - n = (NodeBase) walker.getNext ()) {
 - n.setOwnerDocument (this);
 - // Elements have associated attributes, which must
 - // also have owners changed.
 - if (n instanceof Element) {
 - NamedNodeMap list = n.getAttributes ();
 - int length = list.getLength ();
 - for (int i = 0; i < length; i++)
 - changeNodeOwner (list.item (i));
 - }
 - }
 - }
 - /**
 - * Returns the <code>Element</code> whose <code>ID</code> is given by
 - * <code>elementId</code>.
 - *
 - * @since DOM Level 2
 - */
 - public Element getElementById(String elementId) {
 - return getElementExById(elementId);
 - }
 - /**
 - * Returns the element whose ID is given by the parameter; or null
 - * if no such element exists. This relies on elements to know the
 - * name of their ID attribute, as will be currently be true only if
 - * the document has been parsed from XML text with a DTD using the
 - * <em>XmlDocumentBuilder</em> class, or if it has been constructed
 - * using specialized DOM implementation classes which know the name
 - * of their ID attribute. (XML allows only one ID attribute per
 - * element, and different elements may use different names for their
 - * ID attributes.)
 - *
 - * <P> This may be used to implement internal IDREF linkage, as well
 - * as some kinds of <em>XPointer</em> linkage as used in current
 - * drafts of <em>XLink</em>.
 - *
 - * @param id The value of the ID attribute which will be matched
 - * by any element which is returned.
 - * @deprecated As of DOM level 2, replaced by the method
 - * Document.getElementById
 - */
 - // Note: HTML DOM has getElementById() with "Element" return type
 - public ElementEx getElementExById (String id)
 - {
 - if (id == null)
 - throw new IllegalArgumentException (getMessage ("XD-000"));
 - TreeWalker w = new TreeWalker (this);
 - ElementEx element;
 - while ((element = (ElementEx) w.getNextElement (null)) != null) {
 - String idAttr = element.getIdAttributeName ();
 - String value;
 - if (idAttr == null)
 - continue;
 - value = element.getAttribute (idAttr);
 - if (value.equals (id))
 - return element;
 - }
 - return null;
 - }
 - /**
 - * @since DOM Level 2
 - */
 - public Node importNode(Node importedNode, boolean deep)
 - throws DOMException
 - {
 - // First make a copy of the subtree, then change the ownerDocument
 - // of the subtree.
 - Node node = null;
 - switch (importedNode.getNodeType()) {
 - case ATTRIBUTE_NODE:
 - node = importedNode.cloneNode(true);
 - break;
 - case DOCUMENT_FRAGMENT_NODE:
 - if (deep) {
 - node = importedNode.cloneNode(true);
 - } else {
 - node = new DocFragNode();
 - }
 - break;
 - case DOCUMENT_NODE:
 - case DOCUMENT_TYPE_NODE:
 - throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
 - case ELEMENT_NODE:
 - node = ((ElementNode2) importedNode).createCopyForImportNode(deep);
 - break;
 - case ENTITY_NODE:
 - node = importedNode.cloneNode(deep);
 - break;
 - case ENTITY_REFERENCE_NODE:
 - node = importedNode.cloneNode(false);
 - break;
 - case NOTATION_NODE:
 - case PROCESSING_INSTRUCTION_NODE:
 - case TEXT_NODE:
 - case CDATA_SECTION_NODE:
 - case COMMENT_NODE:
 - default:
 - node = importedNode.cloneNode(false);
 - break;
 - }
 - // Change the ownerDocument of subtree root and any children
 - TreeWalker walker;
 - NodeBase n;
 - for (walker = new TreeWalker (node),
 - n = (NodeBase) walker.getCurrent ();
 - n != null;
 - n = (NodeBase) walker.getNext ()) {
 - n.setOwnerDocument (this);
 - // Elements have associated attributes, which must
 - // also have owners changed.
 - if (n instanceof Element) {
 - NamedNodeMap list = n.getAttributes ();
 - int length = list.getLength ();
 - for (int i = 0; i < length; i++)
 - changeNodeOwner (list.item (i));
 - }
 - }
 - return node;
 - }
 - //
 - // Represent document fragments other than the document itself.
 - // (This class is primarily motivated for use with editors.)
 - //
 - static final class DocFragNode extends ParentNode
 - implements DocumentFragment
 - {
 - // package private -- overrides base class method
 - void checkChildType (int type)
 - throws DOMException
 - {
 - switch (type) {
 - case ELEMENT_NODE:
 - case PROCESSING_INSTRUCTION_NODE:
 - case COMMENT_NODE:
 - case TEXT_NODE:
 - case CDATA_SECTION_NODE:
 - case ENTITY_REFERENCE_NODE:
 - return;
 - default:
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - }
 - }
 - public void writeXml (XmlWriteContext context) throws IOException
 - {
 - this.writeChildrenXml (context);
 - }
 - public Node getParentNode ()
 - { return null; }
 - public void setParentNode (Node p)
 - { if (p != null) throw new IllegalArgumentException (); }
 - public short getNodeType ()
 - { return DOCUMENT_FRAGMENT_NODE; }
 - public String getNodeName () {
 - return ("#document-fragment");
 - }
 - public Node cloneNode (boolean deep)
 - {
 - DocFragNode retval = new DocFragNode ();
 - ((NodeBase)retval).setOwnerDocument
 - ((XmlDocument)this.getOwnerDocument ());
 - if (deep) {
 - Node node;
 - for (int i = 0; (node = item (i)) != null; i++) {
 - node = node.cloneNode (true);
 - retval.appendChild (node);
 - }
 - }
 - return retval;
 - }
 - }
 - //
 - // Represent entity references.
 - //
 - final static class EntityRefNode extends ParentNode
 - implements EntityReference
 - {
 - private String entity;
 - EntityRefNode (String name)
 - {
 - if (name == null)
 - throw new IllegalArgumentException (getMessage ("XD-002"));
 - entity = name;
 - }
 - // package private -- overrides base class method
 - void checkChildType (int type)
 - throws DOMException
 - {
 - switch (type) {
 - case ELEMENT_NODE:
 - case PROCESSING_INSTRUCTION_NODE:
 - case COMMENT_NODE:
 - case TEXT_NODE:
 - case CDATA_SECTION_NODE:
 - case ENTITY_REFERENCE_NODE:
 - return;
 - default:
 - throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
 - }
 - }
 - public void writeXml (XmlWriteContext context)
 - throws IOException
 - {
 - if (!context.isEntityDeclared (entity))
 - throw new IOException (getMessage ("XD-003", new Object[]
 - { entity }));
 - Writer out = context.getWriter ();
 - out.write ('&');
 - out.write (entity);
 - out.write (';');
 - }
 - public short getNodeType ()
 - { return ENTITY_REFERENCE_NODE; }
 - public String getNodeName ()
 - { return entity; }
 - public Node cloneNode (boolean deep) {
 - EntityRefNode retval = new EntityRefNode (entity);
 - ((NodeBase)retval).setOwnerDocument((
 - XmlDocument)this.getOwnerDocument ());
 - if (deep) {
 - Node node;
 - for (int i = 0; (node = item (i)) != null; i++) {
 - node = node.cloneNode (true);
 - retval.appendChild (node);
 - }
 - // XXX
 - //throw new RuntimeException (getMessage ("XD-001"));
 - }
 - return retval;
 - }
 - }
 - class ExtWriteContext extends XmlWriteContext
 - {
 - ExtWriteContext (Writer out) { super (out); }
 - ExtWriteContext (Writer out, int level) { super (out, level); }
 - public boolean isEntityDeclared (String name)
 - {
 - if (super.isEntityDeclared (name))
 - return true;
 - DocumentType doctype = getDoctype ();
 - if (doctype == null)
 - return false;
 - else
 - return doctype.getEntities ().getNamedItem (name) != null;
 - }
 - }
 - static class Catalog extends MessageCatalog
 - {
 - Catalog () { super (Catalog.class); }
 - }
 - }