/* * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xerces.internal.dom; import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.util.URI; import com.sun.org.apache.xerces.internal.util.XML11Char; import com.sun.org.apache.xerces.internal.util.XMLChar; import com.sun.org.apache.xerces.internal.utils.ObjectFactory; import com.sun.org.apache.xerces.internal.utils.SecuritySupport; import com.sun.org.apache.xerces.internal.xni.NamespaceContext; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.Entity; import org.w3c.dom.EntityReference; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Notation; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; import org.w3c.dom.UserDataHandler; import org.w3c.dom.events.Event; import org.w3c.dom.events.EventListener; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; /** * The Document interface represents the entire HTML or XML document. * Conceptually, it is the root of the document tree, and provides the * primary access to the document's data. *
* Since elements, text nodes, comments, processing instructions, * etc. cannot exist outside the context of a Document, the Document * interface also contains the factory methods needed to create these * objects. The Node objects created have a ownerDocument attribute * which associates them with the Document within whose context they * were created. *
* The CoreDocumentImpl class only implements the DOM Core. Additional modules * are supported by the more complete DocumentImpl subclass. *
* Note: When any node in the document is serialized, the
* entire document is serialized along with it.
*
* @xerces.internal
*
* @author Arnaud Le Hors, IBM
* @author Joe Kesselman, IBM
* @author Andy Clark, IBM
* @author Ralf Pfeiffer, IBM
* @since PR-DOM-Level-1-19980818.
*/
public class CoreDocumentImpl
extends ParentNode implements Document {
/**
* TODO:: 1. Change XML11Char method names similar to XMLChar. That will
* prevent lot of dirty version checking code.
*
* 2. IMO during cloneNode qname/isXMLName check should not be made.
*/
//
// Constants
//
/** Serialization version. */
static final long serialVersionUID = 0;
//
// Data
//
// document information
/** Document type. */
protected DocumentTypeImpl docType;
/** Document element. */
protected ElementImpl docElement;
/** NodeListCache free list */
transient NodeListCache fFreeNLCache;
/**Experimental DOM Level 3 feature: Document encoding */
protected String encoding;
/**Experimental DOM Level 3 feature: Document actualEncoding */
protected String actualEncoding;
/**Experimental DOM Level 3 feature: Document version */
protected String version;
/**Experimental DOM Level 3 feature: Document standalone */
protected boolean standalone;
/**Experimental DOM Level 3 feature: documentURI */
protected String fDocumentURI;
//Revisit :: change to a better data structure.
/** Table for user data attached to this document nodes. */
private Map
* Any method that alters the tree structure MUST cause or be
* accompanied by a call to changed(), to inform it that any outstanding
* NodeLists may have to be updated.
*
* (Required because NodeList is simultaneously "live" and integer-
* indexed -- a bad decision in the DOM's design.)
*
* Note that changes which do not affect the tree's structure -- changing
* the node's name, for example -- do _not_ have to call changed().
*
* Alternative implementation would be to use a cryptographic
* Digest value rather than a count. This would have the advantage that
* "harmless" changes (those producing equal() trees) would not force
* NodeList to resynchronize. Disadvantage is that it's slightly more prone
* to "false negatives", though that's the difference between "wildly
* unlikely" and "absurdly unlikely". IF we start maintaining digests,
* we should consider taking advantage of them.
*
* Note: This used to be done a node basis, so that we knew what
* subtree changed. But since only DeepNodeList really use this today,
* the gain appears to be really small compared to the cost of having
* an int on every (parent) node plus having to walk up the tree all the
* way to the root to mark the branch as changed everytime a node is
* changed.
* So we now have a single counter global to the document. It means that
* some objects may flush their cache more often than necessary, but this
* makes nodes smaller and only the document needs to be marked as changed.
*/
protected int changes = 0;
// experimental
/** Allow grammar access. */
protected boolean allowGrammarAccess;
/** Bypass error checking. */
protected boolean errorChecking = true;
/** Ancestor checking */
protected boolean ancestorChecking = true;
//Did version change at any point when the document was created ?
//this field helps us to optimize when normalizingDocument.
protected boolean xmlVersionChanged = false ;
/** The following are required for compareDocumentPosition
*/
// Document number. Documents are ordered across the implementation using
// positive integer values. Documents are assigned numbers on demand.
private int documentNumber=0;
// Node counter and table. Used to assign numbers to nodes for this
// document. Node number values are negative integers. Nodes are
// assigned numbers on demand.
private int nodeCounter = 0;
private Map
* While I'm doing so, I've taken advantage of the opportunity to
* cache documentElement and docType so we don't have to
* search for them.
*
* REVISIT: According to the spec it is not allowed to alter neither the
* document element nor the document type in any way
*/
public Node insertBefore(Node newChild, Node refChild)
throws DOMException {
// Only one such child permitted
int type = newChild.getNodeType();
if (errorChecking) {
if((type == Node.ELEMENT_NODE && docElement != null) ||
(type == Node.DOCUMENT_TYPE_NODE && docType != null)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
}
}
// Adopt orphan doctypes
if (newChild.getOwnerDocument() == null &&
newChild instanceof DocumentTypeImpl) {
((DocumentTypeImpl) newChild).ownerDocument = this;
}
super.insertBefore(newChild,refChild);
// If insert succeeded, cache the kid appropriately
if (type == Node.ELEMENT_NODE) {
docElement = (ElementImpl)newChild;
}
else if (type == Node.DOCUMENT_TYPE_NODE) {
docType = (DocumentTypeImpl)newChild;
}
return newChild;
} // insertBefore(Node,Node):Node
/**
* Since insertBefore caches the docElement (and, currently, docType),
* removeChild has to know how to undo the cache
*
* REVISIT: According to the spec it is not allowed to alter neither the
* document element nor the document type in any way
*/
public Node removeChild(Node oldChild) throws DOMException {
super.removeChild(oldChild);
// If remove succeeded, un-cache the kid appropriately
int type = oldChild.getNodeType();
if(type == Node.ELEMENT_NODE) {
docElement = null;
}
else if (type == Node.DOCUMENT_TYPE_NODE) {
docType = null;
}
return oldChild;
} // removeChild(Node):Node
/**
* Since we cache the docElement (and, currently, docType),
* replaceChild has to update the cache
*
* REVISIT: According to the spec it is not allowed to alter neither the
* document element nor the document type in any way
*/
public Node replaceChild(Node newChild, Node oldChild)
throws DOMException {
// Adopt orphan doctypes
if (newChild.getOwnerDocument() == null &&
newChild instanceof DocumentTypeImpl) {
((DocumentTypeImpl) newChild).ownerDocument = this;
}
if (errorChecking &&((docType != null &&
oldChild.getNodeType() != Node.DOCUMENT_TYPE_NODE &&
newChild.getNodeType() == Node.DOCUMENT_TYPE_NODE)
|| (docElement != null &&
oldChild.getNodeType() != Node.ELEMENT_NODE &&
newChild.getNodeType() == Node.ELEMENT_NODE))) {
throw new DOMException(
DOMException.HIERARCHY_REQUEST_ERR,
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
}
super.replaceChild(newChild, oldChild);
int type = oldChild.getNodeType();
if(type == Node.ELEMENT_NODE) {
docElement = (ElementImpl)newChild;
}
else if (type == Node.DOCUMENT_TYPE_NODE) {
docType = (DocumentTypeImpl)newChild;
}
return oldChild;
} // replaceChild(Node,Node):Node
/*
* Get Node text content
* @since DOM Level 3
*/
public String getTextContent() throws DOMException {
return null;
}
/*
* Set Node text content
* @since DOM Level 3
*/
public void setTextContent(String textContent)
throws DOMException {
// no-op
}
/**
* @since DOM Level 3
*/
public Object getFeature(String feature, String version) {
boolean anyVersion = version == null || version.length() == 0;
// if a plus sign "+" is prepended to any feature name, implementations
// are considered in which the specified feature may not be directly
// castable DOMImplementation.getFeature(feature, version). Without a
// plus, only features whose interfaces are directly castable are
// considered.
if ((feature.equalsIgnoreCase("+XPath"))
&& (anyVersion || version.equals("3.0"))) {
// If an XPathEvaluator was created previously
// return it otherwise create a new one.
if (fXPathEvaluator != null) {
return fXPathEvaluator;
}
try {
Class xpathClass = ObjectFactory.findProviderClass (
"com.sun.org.apache.xpath.internal.domapi.XPathEvaluatorImpl", true);
Constructor xpathClassConstr =
xpathClass.getConstructor(new Class[] { Document.class });
// Check if the DOM XPath implementation implements
// the interface org.w3c.dom.XPathEvaluator
Class interfaces[] = xpathClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].getName().equals(
"org.w3c.dom.xpath.XPathEvaluator")) {
fXPathEvaluator = xpathClassConstr.newInstance(new Object[] { this });
return fXPathEvaluator;
}
}
return null;
} catch (Exception e) {
return null;
}
}
return super.getFeature(feature, version);
}
//
// Document methods
//
// factory methods
/**
* Factory method; creates an Attribute having this Document as its
* OwnerDoc.
*
* @param name The name of the attribute. Note that the attribute's value is
* _not_ established at the factory; remember to set it!
*
* @throws DOMException(INVALID_NAME_ERR)
* if the attribute name is not acceptable.
*/
public Attr createAttribute(String name)
throws DOMException {
if (errorChecking && !isXMLName(name,xml11Version)) {
String msg =
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
"INVALID_CHARACTER_ERR",
null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new AttrImpl(this, name);
} // createAttribute(String):Attr
/**
* Factory method; creates a CDATASection having this Document as
* its OwnerDoc.
*
* @param data The initial contents of the CDATA
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
* not yet implemented.)
*/
public CDATASection createCDATASection(String data)
throws DOMException {
return new CDATASectionImpl(this, data);
}
/**
* Factory method; creates a Comment having this Document as its
* OwnerDoc.
*
* @param data The initial contents of the Comment. */
public Comment createComment(String data) {
return new CommentImpl(this, data);
}
/**
* Factory method; creates a DocumentFragment having this Document
* as its OwnerDoc.
*/
public DocumentFragment createDocumentFragment() {
return new DocumentFragmentImpl(this);
}
/**
* Factory method; creates an Element having this Document
* as its OwnerDoc.
*
* @param tagName The name of the element type to instantiate. For
* XML, this is case-sensitive. For HTML, the tagName parameter may
* be provided in any case, but it must be mapped to the canonical
* uppercase form by the DOM implementation.
*
* @throws DOMException(INVALID_NAME_ERR) if the tag name is not
* acceptable.
*/
public Element createElement(String tagName)
throws DOMException {
if (errorChecking && !isXMLName(tagName,xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new ElementImpl(this, tagName);
} // createElement(String):Element
/**
* Factory method; creates an EntityReference having this Document
* as its OwnerDoc.
*
* @param name The name of the Entity we wish to refer to
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
* nonstandard entities are not permitted. (HTML not yet
* implemented.)
*/
public EntityReference createEntityReference(String name)
throws DOMException {
if (errorChecking && !isXMLName(name,xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new EntityReferenceImpl(this, name);
} // createEntityReference(String):EntityReference
/**
* Factory method; creates a ProcessingInstruction having this Document
* as its OwnerDoc.
*
* @param target The target "processor channel"
* @param data Parameter string to be passed to the target.
*
* @throws DOMException(INVALID_NAME_ERR) if the target name is not
* acceptable.
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
* not yet implemented.)
*/
public ProcessingInstruction createProcessingInstruction(String target,
String data)
throws DOMException {
if (errorChecking && !isXMLName(target,xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new ProcessingInstructionImpl(this, target, data);
} // createProcessingInstruction(String,String):ProcessingInstruction
/**
* Factory method; creates a Text node having this Document as its
* OwnerDoc.
*
* @param data The initial contents of the Text.
*/
public Text createTextNode(String data) {
return new TextImpl(this, data);
}
// other document methods
/**
* For XML, this provides access to the Document Type Definition.
* For HTML documents, and XML documents which don't specify a DTD,
* it will be null.
*/
public DocumentType getDoctype() {
if (needsSyncChildren()) {
synchronizeChildren();
}
return docType;
}
/**
* Convenience method, allowing direct access to the child node
* which is considered the root of the actual document content. For
* HTML, where it is legal to have more than one Element at the top
* level of the document, we pick the one with the tagName
* "HTML". For XML there should be only one top-level
*
* (HTML not yet supported.)
*/
public Element getDocumentElement() {
if (needsSyncChildren()) {
synchronizeChildren();
}
return docElement;
}
/**
* Return a live collection of all descendent Elements (not just
* immediate children) having the specified tag name.
*
* @param tagname The type of Element we want to gather. "*" will be
* taken as a wildcard, meaning "all elements in the document."
*
* @see DeepNodeListImpl
*/
public NodeList getElementsByTagName(String tagname) {
return new DeepNodeListImpl(this,tagname);
}
/**
* Retrieve information describing the abilities of this particular
* DOM implementation. Intended to support applications that may be
* using DOMs retrieved from several different sources, potentially
* with different underlying representations.
*/
public DOMImplementation getImplementation() {
// Currently implemented as a singleton, since it's hardcoded
// information anyway.
return CoreDOMImplementationImpl.getDOMImplementation();
}
//
// Public methods
//
// properties
/**
* Sets whether the DOM implementation performs error checking
* upon operations. Turning off error checking only affects
* the following DOM checks:
*
* Turning off error checking does not turn off the
* following checks:
*
* According to the DOM specifications, document nodes cannot be imported
* and a NOT_SUPPORTED_ERR exception is thrown if attempted.
*/
public Node importNode(Node source, boolean deep)
throws DOMException {
return importNode(source, deep, false, null);
} // importNode(Node,boolean):Node
/**
* Overloaded implementation of DOM's importNode method. This method
* provides the core functionality for the public importNode and cloneNode
* methods.
*
* The reversedIdentifiers parameter is provided for cloneNode to preserve
* the document's identifiers. The Map has Elements as the keys and
* their identifiers as the values. When an element is being imported, a
* check is done for an associated identifier. If one exists, the identifier
* is registered with the new, imported element. If reversedIdentifiers is
* null, the parameter is not applied.
*/
private Node importNode(Node source, boolean deep, boolean cloningDoc,
Map
* Note: The DOM implementation must have information that says which
* attributes are of type ID. Attributes with the name "ID" are not of type
* ID unless so defined. Implementations that do not know whether
* attributes are of type ID or not are expected to return null.
* @see #getIdentifier
*/
public Element getElementById(String elementId) {
return getIdentifier(elementId);
}
/**
* Remove all identifiers from the ID table
*/
protected final void clearIdentifiers(){
if (identifiers != null){
identifiers.clear();
}
}
/**
* Registers an identifier name with a specified element node.
* If the identifier is already registered, the new element
* node replaces the previous node. If the specified element
* node is null, removeIdentifier() is called.
*
* @see #getIdentifier
* @see #removeIdentifier
*/
public void putIdentifier(String idName, Element element) {
if (element == null) {
removeIdentifier(idName);
return;
}
if (needsSyncData()) {
synchronizeData();
}
if (identifiers == null) {
identifiers = new HashMap<>();
}
identifiers.put(idName, element);
} // putIdentifier(String,Element)
/**
* Returns a previously registered element with the specified
* identifier name, or null if no element is registered.
*
* @see #putIdentifier
* @see #removeIdentifier
*/
public Element getIdentifier(String idName) {
if (needsSyncData()) {
synchronizeData();
}
if (identifiers == null) {
return null;
}
Element elem = (Element) identifiers.get(idName);
if (elem != null) {
// check that the element is in the tree
Node parent = elem.getParentNode();
while (parent != null) {
if (parent == this) {
return elem;
}
parent = parent.getParentNode();
}
}
return null;
} // getIdentifier(String):Element
/**
* Removes a previously registered element with the specified
* identifier name.
*
* @see #putIdentifier
* @see #getIdentifier
*/
public void removeIdentifier(String idName) {
if (needsSyncData()) {
synchronizeData();
}
if (identifiers == null) {
return;
}
identifiers.remove(idName);
} // removeIdentifier(String)
//
// DOM2: Namespace methods
//
/**
* Introduced in DOM Level 2.
* Creates an element of the given qualified name and namespace URI.
* If the given namespaceURI is null or an empty string and the
* qualifiedName has a prefix that is "xml", the created element
* is bound to the predefined namespace
* "http://www.w3.org/XML/1998/namespace" [Namespaces].
* @param namespaceURI The namespace URI of the element to
* create.
* @param qualifiedName The qualified name of the element type to
* instantiate.
* @return Element A new Element object with the following attributes:
* @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
* name contains an invalid character.
* @throws DOMException NAMESPACE_ERR: Raised if the qualifiedName has a
* prefix that is "xml" and the namespaceURI is
* neither null nor an empty string nor
* "http://www.w3.org/XML/1998/namespace", or
* if the qualifiedName has a prefix different
* from "xml" and the namespaceURI is null or an
* empty string.
* @since WD-DOM-Level-2-19990923
*/
public Element createElementNS(String namespaceURI, String qualifiedName)
throws DOMException {
return new ElementNSImpl(this, namespaceURI, qualifiedName);
}
/**
* NON-DOM: a factory method used by the Xerces DOM parser
* to create an element.
*
* @param namespaceURI The namespace URI of the element to
* create.
* @param qualifiedName The qualified name of the element type to
* instantiate.
* @param localpart The local name of the attribute to instantiate.
*
* @return Element A new Element object with the following attributes:
* @exception DOMException INVALID_CHARACTER_ERR: Raised if the specified
* name contains an invalid character.
*/
public Element createElementNS(String namespaceURI, String qualifiedName,
String localpart)
throws DOMException {
return new ElementNSImpl(this, namespaceURI, qualifiedName, localpart);
}
/**
* Introduced in DOM Level 2.
* Creates an attribute of the given qualified name and namespace URI.
* If the given namespaceURI is null or an empty string and the
* qualifiedName has a prefix that is "xml", the created element
* is bound to the predefined namespace
* "http://www.w3.org/XML/1998/namespace" [Namespaces].
*
* @param namespaceURI The namespace URI of the attribute to
* create. When it is null or an empty string,
* this method behaves like createAttribute.
* @param qualifiedName The qualified name of the attribute to
* instantiate.
* @return Attr A new Attr object.
* @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
* name contains an invalid character.
* @since WD-DOM-Level-2-19990923
*/
public Attr createAttributeNS(String namespaceURI, String qualifiedName)
throws DOMException {
return new AttrNSImpl(this, namespaceURI, qualifiedName);
}
/**
* NON-DOM: a factory method used by the Xerces DOM parser
* to create an element.
*
* @param namespaceURI The namespace URI of the attribute to
* create. When it is null or an empty string,
* this method behaves like createAttribute.
* @param qualifiedName The qualified name of the attribute to
* instantiate.
* @param localpart The local name of the attribute to instantiate.
*
* @return Attr A new Attr object.
* @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
* name contains an invalid character.
*/
public Attr createAttributeNS(String namespaceURI, String qualifiedName,
String localpart)
throws DOMException {
return new AttrNSImpl(this, namespaceURI, qualifiedName, localpart);
}
/**
* Introduced in DOM Level 2.
* Returns a NodeList of all the Elements with a given local name and
* namespace URI in the order in which they would be encountered in a
* preorder traversal of the Document tree.
* @param namespaceURI The namespace URI of the elements to match
* on. The special value "*" matches all
* namespaces. When it is null or an empty
* string, this method behaves like
* getElementsByTagName.
* @param localName The local name of the elements to match on.
* The special value "*" matches all local names.
* @return NodeList A new NodeList object containing all the matched
* Elements.
* @since WD-DOM-Level-2-19990923
*/
public NodeList getElementsByTagNameNS(String namespaceURI,
String localName) {
return new DeepNodeListImpl(this, namespaceURI, localName);
}
//
// Object methods
//
/** Clone. */
public Object clone() throws CloneNotSupportedException {
CoreDocumentImpl newdoc = (CoreDocumentImpl) super.clone();
newdoc.docType = null;
newdoc.docElement = null;
return newdoc;
}
//
// Public static methods
//
/**
* Check the string against XML's definition of acceptable names for
* elements and attributes and so on using the XMLCharacterProperties
* utility class
*/
public static final boolean isXMLName(String s, boolean xml11Version) {
if (s == null) {
return false;
}
if(!xml11Version)
return XMLChar.isValidName(s);
else
return XML11Char.isXML11ValidName(s);
} // isXMLName(String):boolean
/**
* Checks if the given qualified name is legal with respect
* to the version of XML to which this document must conform.
*
* @param prefix prefix of qualified name
* @param local local part of qualified name
*/
public static final boolean isValidQName(String prefix, String local, boolean xml11Version) {
// check that both prefix and local part match NCName
if (local == null) return false;
boolean validNCName = false;
if (!xml11Version) {
validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
&& XMLChar.isValidNCName(local);
}
else {
validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
&& XML11Char.isXML11ValidNCName(local);
}
return validNCName;
}
//
// Protected methods
//
/**
* Uses the kidOK lookup table to check whether the proposed
* tree structure is legal.
*/
protected boolean isKidOK(Node parent, Node child) {
if (allowGrammarAccess &&
parent.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
return child.getNodeType() == Node.ELEMENT_NODE;
}
return 0 != (kidOK[parent.getNodeType()] & 1 << child.getNodeType());
}
/**
* Denotes that this node has changed.
*/
protected void changed() {
changes++;
}
/**
* Returns the number of changes to this node.
*/
protected int changes() {
return changes;
}
// NodeListCache pool
/**
* Returns a NodeListCache for the given node.
*/
NodeListCache getNodeListCache(ParentNode owner) {
if (fFreeNLCache == null) {
return new NodeListCache(owner);
}
NodeListCache c = fFreeNLCache;
fFreeNLCache = fFreeNLCache.next;
c.fChild = null;
c.fChildIndex = -1;
c.fLength = -1;
// revoke previous ownership
if (c.fOwner != null) {
c.fOwner.fNodeListCache = null;
}
c.fOwner = owner;
// c.next = null; not necessary, except for confused people...
return c;
}
/**
* Puts the given NodeListCache in the free list.
* Note: The owner node can keep using it until we reuse it
*/
void freeNodeListCache(NodeListCache c) {
c.next = fFreeNLCache;
fFreeNLCache = c;
}
/**
* Associate an object to a key on this node. The object can later be
* retrieved from this node by calling
*
*
*
*/
public void setErrorChecking(boolean check) {
errorChecking = check;
}
/*
* DOM Level 3 WD - Experimental.
*/
public void setStrictErrorChecking(boolean check) {
errorChecking = check;
}
/**
* Returns true if the DOM implementation performs error checking.
*/
public boolean getErrorChecking() {
return errorChecking;
}
/*
* DOM Level 3 WD - Experimental.
*/
public boolean getStrictErrorChecking() {
return errorChecking;
}
/**
* DOM Level 3 CR - Experimental. (Was getActualEncoding)
*
* An attribute specifying the encoding used for this document
* at the time of the parsing. This is null
when
* it is not known, such as when the Document
was
* created in memory.
* @since DOM Level 3
*/
public String getInputEncoding() {
return actualEncoding;
}
/**
* DOM Internal
* (Was a DOM L3 Core WD public interface method setActualEncoding )
*
* An attribute specifying the actual encoding of this document. This is
* null
otherwise.
*
This attribute represents the property [character encoding scheme]
* defined in .
*/
public void setInputEncoding(String value) {
actualEncoding = value;
}
/**
* DOM Internal
* (Was a DOM L3 Core WD public interface method setXMLEncoding )
*
* An attribute specifying, as part of the XML declaration,
* the encoding of this document. This is null when unspecified.
*/
public void setXmlEncoding(String value) {
encoding = value;
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public void setEncoding(String value) {
setXmlEncoding(value);
}
/**
* DOM Level 3 WD - Experimental.
* The encoding of this document (part of XML Declaration)
*/
public String getXmlEncoding() {
return encoding;
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public String getEncoding() {
return getXmlEncoding();
}
/**
* DOM Level 3 CR - Experimental.
* version - An attribute specifying, as part of the XML declaration,
* the version number of this document.
*/
public void setXmlVersion(String value) {
if(value.equals("1.0") || value.equals("1.1")){
//we need to change the flag value only --
// when the version set is different than already set.
if(!getXmlVersion().equals(value)){
xmlVersionChanged = true ;
//change the normalization value back to false
isNormalized(false);
version = value;
}
}
else{
//NOT_SUPPORTED_ERR: Raised if the vesion is set to a value that is not supported by
//this document
//we dont support any other XML version
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
if((getXmlVersion()).equals("1.1")){
xml11Version = true;
}
else{
xml11Version = false;
}
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public void setVersion(String value) {
setXmlVersion(value);
}
/**
* DOM Level 3 WD - Experimental.
* The version of this document (part of XML Declaration)
*/
public String getXmlVersion() {
return (version == null)?"1.0":version;
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public String getVersion() {
return getXmlVersion();
}
/**
* DOM Level 3 CR - Experimental.
*
* Xmlstandalone - An attribute specifying, as part of the XML declaration,
* whether this document is standalone
* @exception DOMException
* NOT_SUPPORTED_ERR: Raised if this document does not support the
* "XML" feature.
* @since DOM Level 3
*/
public void setXmlStandalone(boolean value)
throws DOMException {
standalone = value;
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public void setStandalone(boolean value) {
setXmlStandalone(value);
}
/**
* DOM Level 3 WD - Experimental.
* standalone that specifies whether this document is standalone
* (part of XML Declaration)
*/
public boolean getXmlStandalone() {
return standalone;
}
/**
* @deprecated This method is internal and only exists for
* compatibility with older applications. New applications
* should never call this method.
*/
public boolean getStandalone() {
return getXmlStandalone();
}
/**
* DOM Level 3 WD - Experimental.
* The location of the document or null
if undefined.
*
Beware that when the Document
supports the feature
* "HTML" , the href attribute of the HTML BASE element takes precedence
* over this attribute.
* @since DOM Level 3
*/
public String getDocumentURI(){
return fDocumentURI;
}
/**
* DOM Level 3 WD - Experimental.
* Renaming node
*/
public Node renameNode(Node n,String namespaceURI,String name)
throws DOMException{
if (errorChecking && n.getOwnerDocument() != this && n != this) {
String msg = DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
}
switch (n.getNodeType()) {
case ELEMENT_NODE: {
ElementImpl el = (ElementImpl) n;
if (el instanceof ElementNSImpl) {
((ElementNSImpl) el).rename(namespaceURI, name);
// fire user data NODE_RENAMED event
callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
}
else {
if (namespaceURI == null) {
if (errorChecking) {
int colon1 = name.indexOf(':');
if(colon1 != -1){
String msg =
DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
"NAMESPACE_ERR",
null);
throw new DOMException(DOMException.NAMESPACE_ERR, msg);
}
if (!isXMLName(name,xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(
DOMMessageFormatter.DOM_DOMAIN,
"INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
msg);
}
}
el.rename(name);
// fire user data NODE_RENAMED event
callUserDataHandlers(el, null,
UserDataHandler.NODE_RENAMED);
}
else {
// we need to create a new object
ElementNSImpl nel =
new ElementNSImpl(this, namespaceURI, name);
// register event listeners on new node
copyEventListeners(el, nel);
// remove user data from old node
MapDocument.normalizeDocument
is
* invoked.
* @since DOM Level 3
*/
public DOMConfiguration getDomConfig(){
if (fConfiguration == null) {
fConfiguration = new DOMConfigurationImpl();
}
return fConfiguration;
}
/**
* Returns the absolute base URI of this node or null if the implementation
* wasn't able to obtain an absolute URI. Note: If the URI is malformed, a
* null is returned.
*
* @return The absolute base URI of this node or null.
* @since DOM Level 3
*/
public String getBaseURI() {
if (fDocumentURI != null && fDocumentURI.length() != 0 ) {// attribute value is always empty string
try {
return new URI(fDocumentURI).toString();
}
catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
// REVISIT: what should happen in this case?
return null;
}
}
return fDocumentURI;
}
/**
* DOM Level 3 WD - Experimental.
*/
public void setDocumentURI(String documentURI){
fDocumentURI = documentURI;
}
//
// DOM L3 LS
//
/**
* DOM Level 3 WD - Experimental.
* Indicates whether the method load should be synchronous or
* asynchronous. When the async attribute is set to true
* the load method returns control to the caller before the document has
* completed loading. The default value of this property is
* false
.
*
Setting the value of this attribute might throw NOT_SUPPORTED_ERR
* if the implementation doesn't support the mode the attribute is being
* set to. Should the DOM spec define the default value of this
* property? What if implementing both async and sync IO is impractical
* in some systems? 2001-09-14. default is false
but we
* need to check with Mozilla and IE.
*/
public boolean getAsync() {
return false;
}
/**
* DOM Level 3 WD - Experimental.
* Indicates whether the method load should be synchronous or
* asynchronous. When the async attribute is set to true
* the load method returns control to the caller before the document has
* completed loading. The default value of this property is
* false
.
*
Setting the value of this attribute might throw NOT_SUPPORTED_ERR
* if the implementation doesn't support the mode the attribute is being
* set to. Should the DOM spec define the default value of this
* property? What if implementing both async and sync IO is impractical
* in some systems? 2001-09-14. default is false
but we
* need to check with Mozilla and IE.
*/
public void setAsync(boolean async) {
if (async) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
}
/**
* DOM Level 3 WD - Experimental.
* If the document is currently being loaded as a result of the method
* load
being invoked the loading and parsing is
* immediately aborted. The possibly partial result of parsing the
* document is discarded and the document is cleared.
*/
public void abort() {
}
/**
* DOM Level 3 WD - Experimental.
*
* Replaces the content of the document with the result of parsing the
* given URI. Invoking this method will either block the caller or
* return to the caller immediately depending on the value of the async
* attribute. Once the document is fully loaded a "load" event (as
* defined in [DOM Level 3 Events]
* , except that the Event.targetNode
will be the document,
* not an element) will be dispatched on the document. If an error
* occurs, an implementation dependent "error" event will be dispatched
* on the document. If this method is called on a document that is
* currently loading, the current load is interrupted and the new URI
* load is initiated.
*
When invoking this method the parameters used in the
* DOMParser
interface are assumed to have their default
* values with the exception that the parameters "entities"
* , "normalize-characters"
,
* "check-character-normalization"
are set to
* "false"
.
*
The result of a call to this method is the same the result of a
* call to DOMParser.parseWithContext
with an input stream
* referencing the URI that was passed to this call, the document as the
* context node, and the action ACTION_REPLACE_CHILDREN
.
* @param uri The URI reference for the XML file to be loaded. If this is
* a relative URI, the base URI used by the implementation is
* implementation dependent.
* @return If async is set to true
load
returns
* true
if the document load was successfully initiated.
* If an error occurred when initiating the document load,
* load
returns false
.If async is set to
* false
load
returns true
if
* the document was successfully loaded and parsed. If an error
* occurred when either loading or parsing the URI, load
* returns false
.
*/
public boolean load(String uri) {
return false;
}
/**
* DOM Level 3 WD - Experimental.
* Replace the content of the document with the result of parsing the
* input string, this method is always synchronous.
* @param source A string containing an XML document.
* @return true
if parsing the input string succeeded
* without errors, otherwise false
.
*/
public boolean loadXML(String source) {
return false;
}
/**
* DOM Level 3 WD - Experimental.
* Save the document or the given node and all its descendants to a string
* (i.e. serialize the document or node).
*
The parameters used in the LSSerializer
interface are
* assumed to have their default values when invoking this method.
*
The result of a call to this method is the same the result of a
* call to LSSerializer.writeToString
with the document as
* the node to write.
* @param node Specifies what to serialize, if this parameter is
* null
the whole document is serialized, if it's
* non-null the given node is serialized.
* @return The serialized document or null
in case an error
* occurred.
* @exception DOMException
* WRONG_DOCUMENT_ERR: Raised if the node passed in as the node
* parameter is from an other document.
*/
public String saveXML(Node node)
throws DOMException {
if (errorChecking && node != null
&& this != node.getOwnerDocument()) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
}
DOMImplementationLS domImplLS = (DOMImplementationLS) DOMImplementationImpl.getDOMImplementation();
LSSerializer xmlWriter = domImplLS.createLSSerializer();
if (node == null) {
node = this;
}
return xmlWriter.writeToString(node);
}
/**
* Sets whether the DOM implementation generates mutation events upon
* operations.
*/
void setMutationEvents(boolean set) {
// does nothing by default - overidden in subclass
}
/**
* Returns true if the DOM implementation generates mutation events.
*/
boolean getMutationEvents() {
// does nothing by default - overriden in subclass
return false;
}
// non-DOM factory methods
/**
* NON-DOM Factory method; creates a DocumentType having this Document as
* its OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
* information unspecified.)
*
* @param name The name of the Entity we wish to provide a value for.
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where DTDs
* are not permitted. (HTML not yet implemented.)
*/
public DocumentType createDocumentType(String qualifiedName,
String publicID,
String systemID)
throws DOMException {
return new DocumentTypeImpl(this, qualifiedName, publicID, systemID);
} // createDocumentType(String):DocumentType
/**
* NON-DOM Factory method; creates an Entity having this Document as its
* OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
* information unspecified.)
*
* @param name The name of the Entity we wish to provide a value for.
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
* nonstandard entities are not permitted. (HTML not yet implemented.)
*/
public Entity createEntity(String name)
throws DOMException {
if (errorChecking && !isXMLName(name, xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new EntityImpl(this, name);
} // createEntity(String):Entity
/**
* NON-DOM Factory method; creates a Notation having this Document as its
* OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
* information unspecified.)
*
* @param name The name of the Notation we wish to describe
*
* @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
* notations are not permitted. (HTML not yet implemented.)
*/
public Notation createNotation(String name)
throws DOMException {
if (errorChecking && !isXMLName(name, xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new NotationImpl(this, name);
} // createNotation(String):Notation
/**
* NON-DOM Factory method: creates an element definition. Element
* definitions hold default attribute values.
*/
public ElementDefinitionImpl createElementDefinition(String name)
throws DOMException {
if (errorChecking && !isXMLName(name, xml11Version)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
return new ElementDefinitionImpl(this, name);
} // createElementDefinition(String):ElementDefinitionImpl
// other non-DOM methods
/**
* NON-DOM: Get the number associated with this document. Used to order
* documents in the implementation.
*/
protected int getNodeNumber() {
if (documentNumber == 0) {
CoreDOMImplementationImpl cd = (CoreDOMImplementationImpl) CoreDOMImplementationImpl.getDOMImplementation();
documentNumber = cd.assignDocumentNumber();
}
return documentNumber;
}
/**
* NON-DOM: Get a number associated with a node created with respect to this
* document. Needed for compareDocumentPosition when nodes are disconnected.
* This is only used on demand.
*/
protected int getNodeNumber(Node node) {
// Check if the node is already in the hash
// If so, retrieve the node number
// If not, assign a number to the node
// Node numbers are negative, from -1 to -n
int num;
if (nodeTable == null) {
nodeTable = new HashMap<>();
num = --nodeCounter;
nodeTable.put(node, new Integer(num));
} else {
Integer n = nodeTable.get(node);
if (n == null) {
num = --nodeCounter;
nodeTable.put(node, num);
} else {
num = n.intValue();
}
}
return num;
}
/**
* Copies a node from another document to this document. The new nodes are
* created using this document's factory methods and are populated with the
* data from the source's accessor methods defined by the DOM interfaces.
* Its behavior is otherwise similar to that of cloneNode.
* getUserData
with the
* same key.
* @param n The node to associate the object to.
* @param key The key to associate the object to.
* @param data The object to associate to the given key, or
* null
to remove any existing association to that key.
* @param handler The handler to associate to that key, or
* null
.
* @return Returns the DOMObject
previously associated to
* the given key on this node, or null
if there was none.
* @since DOM Level 3
*
* REVISIT: we could use a free list of UserDataRecord here
*/
public Object setUserData(Node n, String key,
Object data, UserDataHandler handler) {
if (data == null) {
if (nodeUserData != null) {
MapsetUserData
with the same key.
* @param n The node the object is associated to.
* @param key The key the object is associated to.
* @return Returns the DOMObject
associated to the given key
* on this node, or null
if there was none.
* @since DOM Level 3
*/
public Object getUserData(Node n, String key) {
if (nodeUserData == null) {
return null;
}
Map