/*
* 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 org.apache.xerces.dom;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.xerces.util.URI;
import org.apache.xerces.util.XML11Char;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.xni.NamespaceContext;
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;
import at.ac.tuwien.dsg.scaledom.dom.ScaleDomDocument;
/**
* 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.
* <P>
* 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.
* <p>
* The CoreDocumentImpl class only implements the DOM Core. Additional modules are supported by the more complete
* DocumentImpl subclass.
* <p>
* <b>Note:</b> 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
* @version $Id: CoreDocumentImpl.java 950601 2010-06-02 15:38:55Z mrglavas $
* @since PR-DOM-Level-1-19980818.
*/
@SuppressWarnings("all")
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;
/** Table for user data attached to this document nodes. */
protected Map userData; // serialized as Hashtable
/** Identifiers. */
protected Hashtable identifiers;
// DOM Level 3: normalizeDocument
transient DOMNormalizer domNormalizer = null;
transient DOMConfigurationImpl fConfiguration = null;
// support of XPath API
transient Object fXPathEvaluator = null;
/** Table for quick check of child insertion. */
private final static int[] kidOK;
/**
* Number of alterations made to this document since its creation. Serves as a "dirty bit" so that live objects such
* as NodeList can recognize when an alteration has been made and discard its cached state information.
* <p>
* 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.
* <p>
* (Required because NodeList is simultaneously "live" and integer- indexed -- a bad decision in the DOM's design.)
* <p>
* Note that changes which do not affect the tree's structure -- changing the node's name, for example -- do _not_
* have to call changed().
* <p>
* 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;
// 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 nodeTable; // serialized as Hashtable
private boolean xml11Version = false; // by default 1.0
//
// Static initialization
//
static {
kidOK = new int[13];
kidOK[DOCUMENT_NODE] = 1 << ELEMENT_NODE | 1 << PROCESSING_INSTRUCTION_NODE | 1 << COMMENT_NODE
| 1 << DOCUMENT_TYPE_NODE;
kidOK[DOCUMENT_FRAGMENT_NODE] = kidOK[ENTITY_NODE] = kidOK[ENTITY_REFERENCE_NODE] = kidOK[ELEMENT_NODE] = 1 << ELEMENT_NODE
| 1 << PROCESSING_INSTRUCTION_NODE
| 1 << COMMENT_NODE
| 1 << TEXT_NODE
| 1 << CDATA_SECTION_NODE
| 1 << ENTITY_REFERENCE_NODE;
kidOK[ATTRIBUTE_NODE] = 1 << TEXT_NODE | 1 << ENTITY_REFERENCE_NODE;
kidOK[DOCUMENT_TYPE_NODE] = kidOK[PROCESSING_INSTRUCTION_NODE] = kidOK[COMMENT_NODE] = kidOK[TEXT_NODE] = kidOK[CDATA_SECTION_NODE] = kidOK[NOTATION_NODE] = 0;
} // static
// <ScaleDOM>
protected boolean isScaleDomEnabled() {
return this instanceof ScaleDomDocument;
}
// </ScaleDOM>
//
// Constructors
//
/**
* NON-DOM: Actually creating a Document is outside the DOM's spec, since it has to operate in terms of a particular
* implementation.
*/
public CoreDocumentImpl() {
this(false);
}
/** Constructor. */
public CoreDocumentImpl(boolean grammarAccess) {
super(null);
ownerDocument = this;
allowGrammarAccess = grammarAccess;
}
/**
* For DOM2 support. The createDocument factory method is in DOMImplementation.
*/
public CoreDocumentImpl(DocumentType doctype) {
this(doctype, false);
}
/** For DOM2 support. */
public CoreDocumentImpl(DocumentType doctype, boolean grammarAccess) {
this(grammarAccess);
if (doctype != null) {
DocumentTypeImpl doctypeImpl;
try {
doctypeImpl = (DocumentTypeImpl) doctype;
} catch (ClassCastException e) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR",
null);
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
}
doctypeImpl.ownerDocument = this;
appendChild(doctype);
}
}
//
// Node methods
//
// even though ownerDocument refers to this in this implementation
// the DOM Level 2 spec says it must be null, so make it appear so
final public Document getOwnerDocument() {
return null;
}
/** Returns the node type. */
public short getNodeType() {
return Node.DOCUMENT_NODE;
}
/** Returns the node name. */
public String getNodeName() {
return "#document";
}
/**
* Deep-clone a document, including fixing ownerDoc for the cloned children. Note that this requires bypassing the
* WRONG_DOCUMENT_ERR protection. I've chosen to implement it by calling importNode which is DOM Level 2.
*
* @return org.w3c.dom.Node
* @param deep boolean, iff true replicate children
*/
public Node cloneNode(boolean deep) {
CoreDocumentImpl newdoc = new CoreDocumentImpl();
callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
cloneNode(newdoc, deep);
return newdoc;
} // cloneNode(boolean):Node
/**
* internal method to share code with subclass
**/
protected void cloneNode(CoreDocumentImpl newdoc, boolean deep) {
// clone the children by importing them
if (needsSyncChildren()) {
synchronizeChildren();
}
if (deep) {
HashMap reversedIdentifiers = null;
if (identifiers != null) {
// Build a reverse mapping from element to identifier.
reversedIdentifiers = new HashMap();
Iterator entries = identifiers.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Object elementId = entry.getKey();
Object elementNode = entry.getValue();
reversedIdentifiers.put(elementNode, elementId);
}
}
// Copy children into new document.
// <ScaleDOM>
ChildNode firstChild = null;
if(isScaleDomEnabled()) {
firstChild = getFirstLoadedChildNode();
} else {
firstChild = this.firstChild;
}
// </ScaleDOM>
for (ChildNode kid = firstChild; kid != null; kid = kid.nextSibling) {
newdoc.appendChild(newdoc.importNode(kid, true, true, reversedIdentifiers));
}
}
// experimental
newdoc.allowGrammarAccess = allowGrammarAccess;
newdoc.errorChecking = errorChecking;
} // cloneNode(CoreDocumentImpl,boolean):void
/**
* Since a Document may contain at most one top-level Element child, and at most one DocumentType declaraction, we
* need to subclass our add-children methods to implement this constraint. Since appendChild() is implemented as
* insertBefore(,null), altering the latter fixes both.
* <p>
* 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 (needsSyncChildren()) {
synchronizeChildren();
}
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("org.apache.xpath.domapi.XPathEvaluatorImpl",
ObjectFactory.findClassLoader(), 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 <em>live</em> 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:
* <ul>
* <li>Checking strings to make sure that all characters are legal XML characters
* <li>Hierarchy checking such as allowed children, checks for cycles, etc.
* </ul>
* <p>
* Turning off error checking does <em>not</em> turn off the following checks:
* <ul>
* <li>Read only checks
* <li>Checks related to DOM events
* </ul>
*/
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 <code>null</code>
* when it is not known, such as when the <code>Document</code> 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 <code>null</code> otherwise. <br>
* 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 <code>null</code> if undefined. <br>
* Beware that when the <code>Document</code> 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;
}
/*
* NON-DOM Used by DOM Level 3 WD remameNode.
*
* Some DOM implementations do not allow nodes to be renamed and require creating new elements. In this case this
* method should be overwritten.
*
* @return true if the given element can be renamed, false, if it must be replaced.
*/
protected boolean canRenameElements(String newNamespaceURI, String newNodeName, ElementImpl el) {
return true;
}
/**
* 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) {
if (canRenameElements(namespaceURI, name, el)) {
((ElementNSImpl) el).rename(namespaceURI, name);
// fire user data NODE_RENAMED event
callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
} else {
el = replaceRenameElement(el, namespaceURI, name);
}
} else {
if (namespaceURI == null && canRenameElements(null, name, el)) {
el.rename(name);
// fire user data NODE_RENAMED event
callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
} else {
el = replaceRenameElement(el, namespaceURI, name);
}
}
// fire ElementNameChanged event
renamedElement((Element) n, el);
return el;
}
case ATTRIBUTE_NODE: {
AttrImpl at = (AttrImpl) n;
// detach attr from element
Element el = at.getOwnerElement();
if (el != null) {
el.removeAttributeNode(at);
}
if (n instanceof AttrNSImpl) {
((AttrNSImpl) at).rename(namespaceURI, name);
// reattach attr to element
if (el != null) {
el.setAttributeNodeNS(at);
}
// fire user data NODE_RENAMED event
callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
} else {
if (namespaceURI == null) {
at.rename(name);
// reattach attr to element
if (el != null) {
el.setAttributeNode(at);
}
// fire user data NODE_RENAMED event
callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
} else {
// we need to create a new object
AttrNSImpl nat = (AttrNSImpl) createAttributeNS(namespaceURI, name);
// register event listeners on new node
copyEventListeners(at, nat);
// remove user data from old node
Hashtable data = removeUserDataTable(at);
// move children to new node
Node child = at.getFirstChild();
while (child != null) {
at.removeChild(child);
nat.appendChild(child);
child = at.getFirstChild();
}
// attach user data to new node
setUserDataTable(nat, data);
// and fire user data NODE_RENAMED event
callUserDataHandlers(at, nat, UserDataHandler.NODE_RENAMED);
// reattach attr to element
if (el != null) {
el.setAttributeNode(nat);
}
at = nat;
}
}
// fire AttributeNameChanged event
renamedAttrNode((Attr) n, at);
return at;
}
default: {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
}
}
private ElementImpl replaceRenameElement(ElementImpl el, String namespaceURI, String name) {
// we need to create a new object
ElementNSImpl nel = (ElementNSImpl) createElementNS(namespaceURI, name);
// register event listeners on new node
copyEventListeners(el, nel);
// remove user data from old node
Hashtable data = removeUserDataTable(el);
// remove old node from parent if any
Node parent = el.getParentNode();
Node nextSib = el.getNextSibling();
if (parent != null) {
parent.removeChild(el);
}
// move children to new node
Node child = el.getFirstChild();
while (child != null) {
el.removeChild(child);
nel.appendChild(child);
child = el.getFirstChild();
}
// move specified attributes to new node
nel.moveSpecifiedAttributes(el);
// attach user data to new node
setUserDataTable(nel, data);
// and fire user data NODE_RENAMED event
callUserDataHandlers(el, nel, UserDataHandler.NODE_RENAMED);
// insert new node where old one was
if (parent != null) {
parent.insertBefore(nel, nextSib);
}
return nel;
}
/**
* DOM Level 3 WD - Experimental Normalize document.
*/
public void normalizeDocument() {
// No need to normalize if already normalized.
if (isNormalized() && !isNormalizeDocRequired()) {
return;
}
if (needsSyncChildren()) {
synchronizeChildren();
}
if (domNormalizer == null) {
domNormalizer = new DOMNormalizer();
}
if (fConfiguration == null) {
fConfiguration = new DOMConfigurationImpl();
} else {
fConfiguration.reset();
}
domNormalizer.normalizeDocument(this, fConfiguration);
isNormalized(true);
// set the XMLversion changed value to false -- once we have finished
// doing normalization
xmlVersionChanged = false;
}
/**
* DOM Level 3 CR - Experimental
*
* The configuration used when <code>Document.normalizeDocument</code> 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 (org.apache.xerces.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 <code>true</code> the load method returns control to the caller before the document has
* completed loading. The default value of this property is <code>false</code>. <br>
* 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 <code>false</code> 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 <code>true</code> the load method returns control to the caller before the document has
* completed loading. The default value of this property is <code>false</code>. <br>
* 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 <code>false</code> 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
* <code>load</code> 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 [<a
* href='http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331'>DOM Level 3 Events</a>] , except that the
* <code>Event.targetNode</code> 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. <br>
* When invoking this method the parameters used in the <code>DOMParser</code> interface are assumed to have their
* default values with the exception that the parameters <code>"entities"</code> ,
* <code>"normalize-characters"</code>, <code>"check-character-normalization"</code> are set to <code>"false"</code>
* . <br>
* The result of a call to this method is the same the result of a call to <code>DOMParser.parseWithContext</code>
* with an input stream referencing the URI that was passed to this call, the document as the context node, and the
* action <code>ACTION_REPLACE_CHILDREN</code>.
*
* @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 <code>true</code> <code>load</code> returns <code>true</code> if the document load was
* successfully initiated. If an error occurred when initiating the document load, <code>load</code> returns
* <code>false</code>.If async is set to <code>false</code> <code>load</code> returns <code>true</code> if
* the document was successfully loaded and parsed. If an error occurred when either loading or parsing the
* URI, <code>load</code> returns <code>false</code>.
*/
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 <code>true</code> if parsing the input string succeeded without errors, otherwise <code>false</code>.
*/
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). <br>
* The parameters used in the <code>LSSerializer</code> interface are assumed to have their default values when
* invoking this method. <br>
* The result of a call to this method is the same the result of a call to <code>LSSerializer.writeToString</code>
* with the document as the node to write.
*
* @param node Specifies what to serialize, if this parameter is <code>null</code> the whole document is serialized,
* if it's non-null the given node is serialized.
* @return The serialized document or <code>null</code> 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 qualifiedName
* @param publicID
* @param systemID
*
* @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 WeakHashMap();
num = --nodeCounter;
nodeTable.put(node, new Integer(num));
} else {
Integer n = (Integer) nodeTable.get(node);
if (n == null) {
num = --nodeCounter;
nodeTable.put(node, new Integer(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.
* <p>
* 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 HashMap
* 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, HashMap reversedIdentifiers)
throws DOMException {
Node newnode = null;
Hashtable userData = null;
// Sigh. This doesn't work; too many nodes have private data that
// would have to be manually tweaked. May be able to add local
// shortcuts to each nodetype. Consider ?????
// if(source instanceof NodeImpl &&
// !(source instanceof DocumentImpl))
// {
// // Can't clone DocumentImpl since it invokes us...
// newnode=(NodeImpl)source.cloneNode(false);
// newnode.ownerDocument=this;
// }
// else
if (source instanceof NodeImpl)
userData = ((NodeImpl) source).getUserDataRecord();
int type = source.getNodeType();
switch (type) {
case ELEMENT_NODE: {
Element newElement;
boolean domLevel20 = source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0");
// Create element according to namespace support/qualification.
if (domLevel20 == false || source.getLocalName() == null)
newElement = createElement(source.getNodeName());
else
newElement = createElementNS(source.getNamespaceURI(), source.getNodeName());
// Copy element's attributes, if any.
NamedNodeMap sourceAttrs = source.getAttributes();
if (sourceAttrs != null) {
int length = sourceAttrs.getLength();
for (int index = 0; index < length; index++) {
Attr attr = (Attr) sourceAttrs.item(index);
// NOTE: this methods is used for both importingNode
// and cloning the document node. In case of the
// clonning default attributes should be copied.
// But for importNode defaults should be ignored.
if (attr.getSpecified() || cloningDoc) {
Attr newAttr = (Attr) importNode(attr, true, cloningDoc, reversedIdentifiers);
// Attach attribute according to namespace
// support/qualification.
if (domLevel20 == false || attr.getLocalName() == null)
newElement.setAttributeNode(newAttr);
else
newElement.setAttributeNodeNS(newAttr);
}
}
}
// Register element identifier.
if (reversedIdentifiers != null) {
// Does element have an associated identifier?
Object elementId = reversedIdentifiers.get(source);
if (elementId != null) {
if (identifiers == null)
identifiers = new Hashtable();
identifiers.put(elementId, newElement);
}
}
newnode = newElement;
break;
}
case ATTRIBUTE_NODE: {
if (source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0")) {
if (source.getLocalName() == null) {
newnode = createAttribute(source.getNodeName());
} else {
newnode = createAttributeNS(source.getNamespaceURI(), source.getNodeName());
}
} else {
newnode = createAttribute(source.getNodeName());
}
// if source is an AttrImpl from this very same implementation
// avoid creating the child nodes if possible
if (source instanceof AttrImpl) {
AttrImpl attr = (AttrImpl) source;
if (attr.hasStringValue()) {
AttrImpl newattr = (AttrImpl) newnode;
newattr.setValue(attr.getValue());
deep = false;
} else {
deep = true;
}
} else {
// According to the DOM spec the kids carry the value.
// However, there are non compliant implementations out
// there that fail to do so. To avoid ending up with no
// value at all, in this case we simply copy the text value
// directly.
if (source.getFirstChild() == null) {
newnode.setNodeValue(source.getNodeValue());
deep = false;
} else {
deep = true;
}
}
break;
}
case TEXT_NODE: {
newnode = createTextNode(source.getNodeValue());
break;
}
case CDATA_SECTION_NODE: {
newnode = createCDATASection(source.getNodeValue());
break;
}
case ENTITY_REFERENCE_NODE: {
newnode = createEntityReference(source.getNodeName());
// the subtree is created according to this doc by the method
// above, so avoid carrying over original subtree
deep = false;
break;
}
case ENTITY_NODE: {
Entity srcentity = (Entity) source;
EntityImpl newentity = (EntityImpl) createEntity(source.getNodeName());
newentity.setPublicId(srcentity.getPublicId());
newentity.setSystemId(srcentity.getSystemId());
newentity.setNotationName(srcentity.getNotationName());
// Kids carry additional value,
// allow deep import temporarily
newentity.isReadOnly(false);
newnode = newentity;
break;
}
case PROCESSING_INSTRUCTION_NODE: {
newnode = createProcessingInstruction(source.getNodeName(), source.getNodeValue());
break;
}
case COMMENT_NODE: {
newnode = createComment(source.getNodeValue());
break;
}
case DOCUMENT_TYPE_NODE: {
// unless this is used as part of cloning a Document
// forbid it for the sake of being compliant to the DOM spec
if (!cloningDoc) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR",
null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
DocumentType srcdoctype = (DocumentType) source;
DocumentTypeImpl newdoctype = (DocumentTypeImpl) createDocumentType(srcdoctype.getNodeName(),
srcdoctype.getPublicId(), srcdoctype.getSystemId());
newdoctype.setInternalSubset(srcdoctype.getInternalSubset());
// Values are on NamedNodeMaps
NamedNodeMap smap = srcdoctype.getEntities();
NamedNodeMap tmap = newdoctype.getEntities();
if (smap != null) {
for (int i = 0; i < smap.getLength(); i++) {
tmap.setNamedItem(importNode(smap.item(i), true, true, reversedIdentifiers));
}
}
smap = srcdoctype.getNotations();
tmap = newdoctype.getNotations();
if (smap != null) {
for (int i = 0; i < smap.getLength(); i++) {
tmap.setNamedItem(importNode(smap.item(i), true, true, reversedIdentifiers));
}
}
// NOTE: At this time, the DOM definition of DocumentType
// doesn't cover Elements and their Attributes. domimpl's
// extentions in that area will not be preserved, even if
// copying from domimpl to domimpl. We could special-case
// that here. Arguably we should. Consider. ?????
newnode = newdoctype;
break;
}
case DOCUMENT_FRAGMENT_NODE: {
newnode = createDocumentFragment();
// No name, kids carry value
break;
}
case NOTATION_NODE: {
Notation srcnotation = (Notation) source;
NotationImpl newnotation = (NotationImpl) createNotation(source.getNodeName());
newnotation.setPublicId(srcnotation.getPublicId());
newnotation.setSystemId(srcnotation.getSystemId());
// Kids carry additional value
newnode = newnotation;
// No name, no value
break;
}
case DOCUMENT_NODE: // Can't import document nodes
default: { // Unknown node type
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
}
if (userData != null)
callUserDataHandlers(source, newnode, UserDataHandler.NODE_IMPORTED, userData);
// If deep, replicate and attach the kids.
if (deep) {
for (Node srckid = source.getFirstChild(); srckid != null; srckid = srckid.getNextSibling()) {
newnode.appendChild(importNode(srckid, true, cloningDoc, reversedIdentifiers));
}
}
if (newnode.getNodeType() == Node.ENTITY_NODE) {
((NodeImpl) newnode).setReadOnly(true, true);
}
return newnode;
} // importNode(Node,boolean,boolean,Hashtable):Node
/**
* DOM Level 3 WD - Experimental Change the node's ownerDocument, and its subtree, to this Document
*
* @param source The node to adopt.
* @see #importNode
**/
public Node adoptNode(Node source) {
NodeImpl node;
Hashtable userData = null;
try {
node = (NodeImpl) source;
} catch (ClassCastException e) {
// source node comes from a different DOMImplementation
return null;
}
// Return null if the source is null
if (source == null) {
return null;
} else if (source != null && source.getOwnerDocument() != null) {
DOMImplementation thisImpl = this.getImplementation();
DOMImplementation otherImpl = source.getOwnerDocument().getImplementation();
// when the source node comes from a different implementation.
if (thisImpl != otherImpl) {
// Adopting from a deferred DOM to a non-deferred DOM
if (thisImpl instanceof org.apache.xerces.dom.DOMImplementationImpl
&& otherImpl instanceof org.apache.xerces.dom.DeferredDOMImplementationImpl) {
// traverse the DOM and expand deferred nodes and then allow adoption
undeferChildren(node);
} else if (thisImpl instanceof org.apache.xerces.dom.DeferredDOMImplementationImpl
&& otherImpl instanceof org.apache.xerces.dom.DOMImplementationImpl) {
// Adopting from a non-deferred DOM into a deferred DOM, this should be okay
} else {
// Adopting between two dissimilar DOMs is not allowed
return null;
}
}
// Adopting from a deferred DOM into another deferred DOM
else if (otherImpl instanceof org.apache.xerces.dom.DeferredDOMImplementationImpl) {
// traverse the DOM and expand deferred nodes and then allow adoption
undeferChildren(node);
}
}
switch (node.getNodeType()) {
case ATTRIBUTE_NODE: {
AttrImpl attr = (AttrImpl) node;
// remove node from wherever it is
if (attr.getOwnerElement() != null) {
// 1. owner element attribute is set to null
attr.getOwnerElement().removeAttributeNode(attr);
}
// 2. specified flag is set to true
attr.isSpecified(true);
userData = node.getUserDataRecord();
// 3. change ownership
attr.setOwnerDocument(this);
if (userData != null)
setUserDataTable(node, userData);
break;
}
// entity, notation nodes are read only nodes.. so they can't be adopted.
// runtime will fall through to NOTATION_NODE
case ENTITY_NODE:
case NOTATION_NODE: {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"NO_MODIFICATION_ALLOWED_ERR", null);
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
}
// document, documentype nodes can't be adopted.
// runtime will fall through to DocumentTypeNode
case DOCUMENT_NODE:
case DOCUMENT_TYPE_NODE: {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
case ENTITY_REFERENCE_NODE: {
userData = node.getUserDataRecord();
// remove node from wherever it is
Node parent = node.getParentNode();
if (parent != null) {
parent.removeChild(source);
}
// discard its replacement value
Node child;
while ((child = node.getFirstChild()) != null) {
node.removeChild(child);
}
// change ownership
node.setOwnerDocument(this);
if (userData != null)
setUserDataTable(node, userData);
// set its new replacement value if any
if (docType == null) {
break;
}
NamedNodeMap entities = docType.getEntities();
Node entityNode = entities.getNamedItem(node.getNodeName());
if (entityNode == null) {
break;
}
for (child = entityNode.getFirstChild(); child != null; child = child.getNextSibling()) {
Node childClone = child.cloneNode(true);
node.appendChild(childClone);
}
break;
}
case ELEMENT_NODE: {
userData = node.getUserDataRecord();
// remove node from wherever it is
Node parent = node.getParentNode();
if (parent != null) {
parent.removeChild(source);
}
// change ownership
node.setOwnerDocument(this);
if (userData != null)
setUserDataTable(node, userData);
// reconcile default attributes
((ElementImpl) node).reconcileDefaultAttributes();
break;
}
default: {
userData = node.getUserDataRecord();
// remove node from wherever it is
Node parent = node.getParentNode();
if (parent != null) {
parent.removeChild(source);
}
// change ownership
node.setOwnerDocument(this);
if (userData != null)
setUserDataTable(node, userData);
}
}
// DOM L3 Core CR
// http://www.w3.org/TR/2003/CR-DOM-Level-3-Core-20031107/core.html#UserDataHandler-ADOPTED
if (userData != null)
callUserDataHandlers(source, null, UserDataHandler.NODE_ADOPTED, userData);
return node;
}
/**
* Traverses the DOM Tree and expands deferred nodes and their children.
*
*/
protected void undeferChildren(Node node) {
Node top = node;
while (null != node) {
if (((NodeImpl) node).needsSyncData()) {
((NodeImpl) node).synchronizeData();
}
NamedNodeMap attributes = node.getAttributes();
if (attributes != null) {
int length = attributes.getLength();
for (int i = 0; i < length; ++i) {
undeferChildren(attributes.item(i));
}
}
Node nextNode = null;
nextNode = node.getFirstChild();
while (null == nextNode) {
if (top.equals(node))
break;
nextNode = node.getNextSibling();
if (null == nextNode) {
node = node.getParentNode();
if ((null == node) || (top.equals(node))) {
nextNode = null;
break;
}
}
}
node = nextNode;
}
}
// identifier maintenence
/**
* Introduced in DOM Level 2 Returns the Element whose ID is given by elementId. If no such element exists, returns
* null. Behavior is not defined if more than one element has this ID.
* <p>
* 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 Hashtable();
}
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)
/** Returns an enumeration registered of identifier names. */
public Enumeration getIdentifiers() {
if (needsSyncData()) {
synchronizeData();
}
if (identifiers == null) {
identifiers = new Hashtable();
}
return identifiers.keys();
} // getIdentifiers():Enumeration
//
// DOM2: Namespace methods
//
/**
* Introduced in DOM Level 2.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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
* <code>getUserData</code> 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 <code>null</code> to remove any existing association to
* that key.
* @param handler The handler to associate to that key, or <code>null</code>.
* @return Returns the <code>DOMObject</code> previously associated to the given key on this node, or
* <code>null</code> 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 (userData != null) {
Hashtable t = (Hashtable) userData.get(n);
if (t != null) {
Object o = t.remove(key);
if (o != null) {
UserDataRecord r = (UserDataRecord) o;
return r.fData;
}
}
}
return null;
} else {
Hashtable t;
if (userData == null) {
userData = new WeakHashMap();
t = new Hashtable();
userData.put(n, t);
} else {
t = (Hashtable) userData.get(n);
if (t == null) {
t = new Hashtable();
userData.put(n, t);
}
}
Object o = t.put(key, new UserDataRecord(data, handler));
if (o != null) {
UserDataRecord r = (UserDataRecord) o;
return r.fData;
}
return null;
}
}
/**
* Retrieves the object associated to a key on a this node. The object must first have been set to this node by
* calling <code>setUserData</code> 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 <code>DOMObject</code> associated to the given key on this node, or <code>null</code> if
* there was none.
* @since DOM Level 3
*/
public Object getUserData(Node n, String key) {
if (userData == null) {
return null;
}
Hashtable t = (Hashtable) userData.get(n);
if (t == null) {
return null;
}
Object o = t.get(key);
if (o != null) {
UserDataRecord r = (UserDataRecord) o;
return r.fData;
}
return null;
}
protected Hashtable getUserDataRecord(Node n) {
if (userData == null) {
return null;
}
Hashtable t = (Hashtable) userData.get(n);
if (t == null) {
return null;
}
return t;
}
/**
* Remove user data table for the given node.
*
* @param n The node this operation applies to.
* @return The removed table.
*/
Hashtable removeUserDataTable(Node n) {
if (userData == null) {
return null;
}
return (Hashtable) userData.get(n);
}
/**
* Set user data table for the given node.
*
* @param n The node this operation applies to.
* @param data The user data table.
*/
void setUserDataTable(Node n, Hashtable data) {
if (userData == null) {
userData = new WeakHashMap();
}
if (data != null) {
userData.put(n, data);
}
}
/**
* Call user data handlers when a node is deleted (finalized)
*
* @param n The node this operation applies to.
* @param c The copy node or null.
* @param operation The operation - import, clone, or delete.
*/
protected void callUserDataHandlers(Node n, Node c, short operation) {
if (userData == null) {
return;
}
// Hashtable t = (Hashtable) userData.get(n);
if (n instanceof NodeImpl) {
Hashtable t = ((NodeImpl) n).getUserDataRecord();
if (t == null || t.isEmpty()) {
return;
}
callUserDataHandlers(n, c, operation, t);
}
}
/**
* Call user data handlers when a node is deleted (finalized)
*
* @param n The node this operation applies to.
* @param c The copy node or null.
* @param operation The operation - import, clone, or delete.
* @param handlers Data associated with n.
*/
void callUserDataHandlers(Node n, Node c, short operation, Hashtable userData) {
if (userData == null || userData.isEmpty()) {
return;
}
Iterator entries = userData.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
String key = (String) entry.getKey();
UserDataRecord r = (UserDataRecord) entry.getValue();
if (r.fHandler != null) {
r.fHandler.handle(operation, key, r.fData, n, c);
}
}
}
/**
* Call user data handlers to let them know the nodes they are related to are being deleted. The alternative would
* be to do that on Node but because the nodes are used as the keys we have a reference to them that prevents them
* from being gc'ed until the document is. At the same time, doing it here has the advantage of avoiding a
* finalize() method on Node, which would affect all nodes and not just the ones that have a user data.
*/
// Temporarily comment out this method, because
// 1. It seems that finalizers are not guaranteed to be called, so the
// functionality is not implemented.
// 2. It affects the performance greatly in multi-thread environment.
// -SG
/*
* public void finalize() { if (userData == null) { return; } Enumeration nodes = userData.keys(); while
* (nodes.hasMoreElements()) { Object node = nodes.nextElement(); Hashtable t = (Hashtable) userData.get(node); if
* (t != null && !t.isEmpty()) { Enumeration keys = t.keys(); while (keys.hasMoreElements()) { String key = (String)
* keys.nextElement(); UserDataRecord r = (UserDataRecord) t.get(key); if (r.fHandler != null) {
* r.fHandler.handle(UserDataHandler.NODE_DELETED, key, r.fData, null, null); } } } } }
*/
protected final void checkNamespaceWF(String qname, int colon1, int colon2) {
if (!errorChecking) {
return;
}
// it is an error for NCName to have more than one ':'
// check if it is valid QName [Namespace in XML production 6]
// :camera , nikon:camera:minolta, camera:
if (colon1 == 0 || colon1 == qname.length() - 1 || colon2 != colon1) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
throw new DOMException(DOMException.NAMESPACE_ERR, msg);
}
}
protected final void checkDOMNSErr(String prefix, String namespace) {
if (errorChecking) {
if (namespace == null) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
throw new DOMException(DOMException.NAMESPACE_ERR, msg);
} else if (prefix.equals("xml") && !namespace.equals(NamespaceContext.XML_URI)) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
throw new DOMException(DOMException.NAMESPACE_ERR, msg);
} else if (prefix.equals("xmlns") && !namespace.equals(NamespaceContext.XMLNS_URI)
|| (!prefix.equals("xmlns") && namespace.equals(NamespaceContext.XMLNS_URI))) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NAMESPACE_ERR", null);
throw new DOMException(DOMException.NAMESPACE_ERR, msg);
}
}
}
/**
* 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
*/
protected final void checkQName(String prefix, String local) {
if (!errorChecking) {
return;
}
// check that both prefix and local part match NCName
boolean validNCName = false;
if (!xml11Version) {
validNCName = (prefix == null || XMLChar.isValidNCName(prefix)) && XMLChar.isValidNCName(local);
} else {
validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
&& XML11Char.isXML11ValidNCName(local);
}
if (!validNCName) {
// REVISIT: add qname parameter to the message
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR",
null);
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
}
}
/**
* We could have more xml versions in future , but for now we could do with this to handle XML 1.0 and 1.1
*/
boolean isXML11Version() {
return xml11Version;
}
boolean isNormalizeDocRequired() {
// REVISIT: Implement to optimize when normalization
// is required
return true;
}
// we should be checking the (elements, attribute, entity etc.) names only when
// version of the document is changed.
boolean isXMLVersionChanged() {
return xmlVersionChanged;
}
/**
* NON-DOM: kept for backward compatibility Store user data related to a given node This is a place where we could
* use weak references! Indeed, the node here won't be GC'ed as long as some user data is attached to it, since the
* userData table will have a reference to the node.
*/
protected void setUserData(NodeImpl n, Object data) {
setUserData(n, "XERCES1DOMUSERDATA", data, null);
}
/**
* NON-DOM: kept for backward compatibility Retreive user data related to a given node
*/
protected Object getUserData(NodeImpl n) {
return getUserData(n, "XERCES1DOMUSERDATA");
}
// Event related methods overidden in subclass
protected void addEventListener(NodeImpl node, String type, EventListener listener, boolean useCapture) {
// does nothing by default - overidden in subclass
}
protected void removeEventListener(NodeImpl node, String type, EventListener listener, boolean useCapture) {
// does nothing by default - overidden in subclass
}
protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
// does nothing by default - overidden in subclass
}
protected boolean dispatchEvent(NodeImpl node, Event event) {
// does nothing by default - overidden in subclass
return false;
}
// Notification methods overidden in subclasses
/**
* A method to be called when some text was changed in a text node, so that live objects can be notified.
*/
void replacedText(CharacterDataImpl node) {
}
/**
* A method to be called when some text was deleted from a text node, so that live objects can be notified.
*/
void deletedText(CharacterDataImpl node, int offset, int count) {
}
/**
* A method to be called when some text was inserted into a text node, so that live objects can be notified.
*/
void insertedText(CharacterDataImpl node, int offset, int count) {
}
/**
* A method to be called when a character data node is about to be modified
*/
void modifyingCharacterData(NodeImpl node, boolean replace) {
}
/**
* A method to be called when a character data node has been modified
*/
void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
}
/**
* A method to be called when a node is about to be inserted in the tree.
*/
void insertingNode(NodeImpl node, boolean replace) {
}
/**
* A method to be called when a node has been inserted in the tree.
*/
void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
}
/**
* A method to be called when a node is about to be removed from the tree.
*/
void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
}
/**
* A method to be called when a node has been removed from the tree.
*/
void removedNode(NodeImpl node, boolean replace) {
}
/**
* A method to be called when a node is about to be replaced in the tree.
*/
void replacingNode(NodeImpl node) {
}
/**
* A method to be called when a node has been replaced in the tree.
*/
void replacedNode(NodeImpl node) {
}
/**
* A method to be called when a character data node is about to be replaced
*/
void replacingData(NodeImpl node) {
}
/**
* method to be called when a character data node has been replaced.
*/
void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
}
/**
* A method to be called when an attribute value has been modified
*/
void modifiedAttrValue(AttrImpl attr, String oldvalue) {
}
/**
* A method to be called when an attribute node has been set
*/
void setAttrNode(AttrImpl attr, AttrImpl previous) {
}
/**
* A method to be called when an attribute node has been removed
*/
void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
}
/**
* A method to be called when an attribute node has been renamed
*/
void renamedAttrNode(Attr oldAt, Attr newAt) {
}
/**
* A method to be called when an element has been renamed
*/
void renamedElement(Element oldEl, Element newEl) {
}
/**
* The serialized forms of the user data and node table maps are Hashtables. Convert them into WeakHashMaps on load.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (userData != null) {
userData = new WeakHashMap(userData);
}
if (nodeTable != null) {
nodeTable = new WeakHashMap(nodeTable);
}
}
/**
* To allow DOM trees serialized by newer versions of Xerces to be read by older versions briefly move the user data
* and node table into Hashtables.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// Keep references to the original objects for restoration after serialization
final Map oldUserData = this.userData;
final Map oldNodeTable = this.nodeTable;
try {
if (oldUserData != null) {
this.userData = new Hashtable(oldUserData);
}
if (oldNodeTable != null) {
nodeTable = new Hashtable(oldNodeTable);
}
out.defaultWriteObject();
}
// If the write fails for some reason ensure
// that we restore the original objects.
finally {
this.userData = oldUserData;
this.nodeTable = oldNodeTable;
}
}
} // class CoreDocumentImpl