| /* |
| * Copyright (c) 2009, 2017, 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. |
| * <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 |
| * @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<Node, Map<String, UserDataRecord>> nodeUserData; |
| |
| /** Identifiers. */ |
| protected Map<String, Node> 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; |
| /** 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<Node, Integer> nodeTable; |
| 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 |
| |
| /** |
| * @serialField docType DocumentTypeImpl document type |
| * @serialField docElement ElementImpl document element |
| * @serialField fFreeNLCache NodeListCache NodeListCache free list |
| * @serialField encoding String Document encoding |
| * @serialField actualEncoding String Document actualEncoding |
| * @serialField version String Document version |
| * @serialField standalone boolean Document standalone |
| * @serialField fDocumentURI String Document URI |
| * @serialField userData Hashtable user data attached to the nodes. Note that |
| * it was original called "userData". It has been changed to nodeUserData to |
| * avoid confusion with those that are actually values of the map. |
| * @serialField identifiers Hashtable identifiers |
| * @serialField changes int flag indicates whether the node has changed |
| * @serialField allowGrammarAccess boolean Allow grammar access |
| * @serialField errorChecking boolean Bypass error checking |
| * @serialField ancestorChecking boolean Ancestor checking |
| * @serialField xmlVersionChanged boolean Indicate whether the version has changed |
| * @serialField documentNumber int Document number |
| * @serialField nodeCounter int Node counter |
| * @serialField nodeTable Hashtable Node table |
| * @serialField xml11Version boolean XML version |
| */ |
| private static final ObjectStreamField[] serialPersistentFields = |
| new ObjectStreamField[] { |
| new ObjectStreamField("docType", DocumentTypeImpl.class), |
| new ObjectStreamField("docElement", ElementImpl.class), |
| new ObjectStreamField("fFreeNLCache", NodeListCache.class), |
| new ObjectStreamField("encoding", String.class), |
| new ObjectStreamField("actualEncoding", String.class), |
| new ObjectStreamField("version", String.class), |
| new ObjectStreamField("standalone", boolean.class), |
| new ObjectStreamField("fDocumentURI", String.class), |
| new ObjectStreamField("userData", Hashtable.class), |
| new ObjectStreamField("identifiers", Hashtable.class), |
| new ObjectStreamField("changes", int.class), |
| new ObjectStreamField("allowGrammarAccess", boolean.class), |
| new ObjectStreamField("errorChecking", boolean.class), |
| new ObjectStreamField("ancestorChecking", boolean.class), |
| new ObjectStreamField("xmlVersionChanged", boolean.class), |
| new ObjectStreamField("documentNumber", int.class), |
| new ObjectStreamField("nodeCounter", int.class), |
| new ObjectStreamField("nodeTable", Hashtable.class), |
| new ObjectStreamField("xml11Version", boolean.class), |
| }; |
| |
| // |
| // 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; |
| String systemProp = SecuritySupport.getSystemProperty(Constants.SUN_DOM_PROPERTY_PREFIX+Constants.SUN_DOM_ANCESTOR_CHECCK); |
| if (systemProp != null) { |
| if (systemProp.equalsIgnoreCase("false")) { |
| ancestorChecking = false; |
| } |
| } |
| } |
| |
| /** |
| * 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) { |
| Map<Node, String> reversedIdentifiers = null; |
| |
| if (identifiers != null) { |
| // Build a reverse mapping from element to identifier. |
| reversedIdentifiers = new HashMap<>(identifiers.size()); |
| for (String elementId : identifiers.keySet()) { |
| reversedIdentifiers.put(identifiers.get(elementId), elementId); |
| } |
| } |
| |
| // Copy children into new document. |
| 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((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) { |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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; |
| } |
| |
| |
| /** |
| * 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 |
| Map<String, UserDataRecord> 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); |
| } |
| el = nel; |
| } |
| } |
| // fire ElementNameChanged event |
| renamedElement((Element) n, el); |
| return el; |
| } |
| case ATTRIBUTE_NODE: { |
| AttrImpl at = (AttrImpl) n; |
| |
| // dettach 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 = new AttrNSImpl(this, namespaceURI, name); |
| |
| // register event listeners on new node |
| copyEventListeners(at, nat); |
| |
| // remove user data from old node |
| Map<String, UserDataRecord> 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); |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * 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 (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 <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 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, num); |
| } else { |
| Integer n = (Integer) 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. |
| * <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 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<Node, String> reversedIdentifiers) |
| throws DOMException { |
| Node newnode = null; |
| Map<String, UserDataRecord> 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? |
| String elementId = reversedIdentifiers.get(source); |
| if (elementId != null) { |
| if (identifiers == null) { |
| identifiers = new HashMap<>(); |
| } |
| |
| 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()); |
| // 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,Map):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; |
| Map<String, UserDataRecord> userData; |
| 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.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 DefferedDOM to DOM |
| if (thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl && |
| otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl) { |
| // traverse the DOM and expand deffered nodes and then allow adoption |
| undeferChildren (node); |
| } else if ( thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl |
| && otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl) { |
| // Adopting from a DOM into a DefferedDOM, this should be okay |
| } else { |
| // Adopting between two dissimilar DOM's is not allowed |
| return null; |
| } |
| } |
| } |
| |
| 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 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. <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 (nodeUserData != null) { |
| Map<String, UserDataRecord> t = nodeUserData.get(n); |
| if (t != null) { |
| UserDataRecord r = t.remove(key); |
| if (r != null) { |
| return r.fData; |
| } |
| } |
| } |
| return null; |
| } else { |
| Map<String, UserDataRecord> t; |
| if (nodeUserData == null) { |
| nodeUserData = new HashMap<>(); |
| t = new HashMap<>(); |
| nodeUserData.put(n, t); |
| } else { |
| t = nodeUserData.get(n); |
| if (t == null) { |
| t = new HashMap<>(); |
| nodeUserData.put(n, t); |
| } |
| } |
| UserDataRecord r = t.put(key, new UserDataRecord(data, handler)); |
| if (r != null) { |
| 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 (nodeUserData == null) { |
| return null; |
| } |
| Map<String, UserDataRecord> t = nodeUserData.get(n); |
| if (t == null) { |
| return null; |
| } |
| UserDataRecord r = t.get(key); |
| if (r != null) { |
| return r.fData; |
| } |
| return null; |
| } |
| |
| protected Map<String, UserDataRecord> getUserDataRecord(Node n) { |
| if (nodeUserData == null) { |
| return null; |
| } |
| Map<String, UserDataRecord> t = nodeUserData.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. |
| */ |
| Map<String, UserDataRecord> removeUserDataTable(Node n) { |
| if (nodeUserData == null) { |
| return null; |
| } |
| return nodeUserData.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, Map<String, UserDataRecord> data) { |
| if (nodeUserData == null) { |
| nodeUserData = new HashMap<>(); |
| } |
| |
| if (data != null) { |
| nodeUserData.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. |
| */ |
| void callUserDataHandlers(Node n, Node c, short operation) { |
| if (nodeUserData == null) { |
| return; |
| } |
| |
| if (n instanceof NodeImpl) { |
| Map<String, UserDataRecord> 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, Map<String, UserDataRecord> userData) { |
| if (userData == null || userData.isEmpty()) { |
| return; |
| } |
| |
| userData.keySet().stream().forEach((key) -> { |
| UserDataRecord r = userData.get(key); |
| 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(NodeImpl node) { |
| } |
| |
| /** |
| * A method to be called when some text was deleted from a text node, |
| * so that live objects can be notified. |
| */ |
| void deletedText(NodeImpl 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(NodeImpl 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) { |
| } |
| |
| /** |
| * @serialData Serialized fields. Convert Maps to Hashtables for backward |
| * compatibility. |
| */ |
| private void writeObject(ObjectOutputStream out) throws IOException { |
| // Convert Maps to Hashtables |
| Hashtable<Node, Hashtable<String, UserDataRecord>> nud = null; |
| if (nodeUserData != null) { |
| nud = new Hashtable<>(); |
| for (Map.Entry<Node, Map<String, UserDataRecord>> e : nodeUserData.entrySet()) { |
| //e.getValue() will not be null since an entry is always put with a non-null value |
| nud.put(e.getKey(), new Hashtable<>(e.getValue())); |
| } |
| } |
| |
| Hashtable<String, Node> ids = (identifiers == null)? null : new Hashtable<>(identifiers); |
| Hashtable<Node, Integer> nt = (nodeTable == null)? null : new Hashtable<>(nodeTable); |
| |
| // Write serialized fields |
| ObjectOutputStream.PutField pf = out.putFields(); |
| pf.put("docType", docType); |
| pf.put("docElement", docElement); |
| pf.put("fFreeNLCache", fFreeNLCache); |
| pf.put("encoding", encoding); |
| pf.put("actualEncoding", actualEncoding); |
| pf.put("version", version); |
| pf.put("standalone", standalone); |
| pf.put("fDocumentURI", fDocumentURI); |
| |
| //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField |
| pf.put("userData", nud); |
| pf.put("identifiers", ids); |
| pf.put("changes", changes); |
| pf.put("allowGrammarAccess", allowGrammarAccess); |
| pf.put("errorChecking", errorChecking); |
| pf.put("ancestorChecking", ancestorChecking); |
| pf.put("xmlVersionChanged", xmlVersionChanged); |
| pf.put("documentNumber", documentNumber); |
| pf.put("nodeCounter", nodeCounter); |
| pf.put("nodeTable", nt); |
| pf.put("xml11Version", xml11Version); |
| out.writeFields(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void readObject(ObjectInputStream in) |
| throws IOException, ClassNotFoundException { |
| // We have to read serialized fields first. |
| ObjectInputStream.GetField gf = in.readFields(); |
| docType = (DocumentTypeImpl)gf.get("docType", null); |
| docElement = (ElementImpl)gf.get("docElement", null); |
| fFreeNLCache = (NodeListCache)gf.get("fFreeNLCache", null); |
| encoding = (String)gf.get("encoding", null); |
| actualEncoding = (String)gf.get("actualEncoding", null); |
| version = (String)gf.get("version", null); |
| standalone = gf.get("standalone", false); |
| fDocumentURI = (String)gf.get("fDocumentURI", null); |
| |
| //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField |
| Hashtable<Node, Hashtable<String, UserDataRecord>> nud = |
| (Hashtable<Node, Hashtable<String, UserDataRecord>>)gf.get("userData", null); |
| |
| Hashtable<String, Node> ids = (Hashtable<String, Node>)gf.get("identifiers", null); |
| |
| changes = gf.get("changes", 0); |
| allowGrammarAccess = gf.get("allowGrammarAccess", false); |
| errorChecking = gf.get("errorChecking", true); |
| ancestorChecking = gf.get("ancestorChecking", true); |
| xmlVersionChanged = gf.get("xmlVersionChanged", false); |
| documentNumber = gf.get("documentNumber", 0); |
| nodeCounter = gf.get("nodeCounter", 0); |
| |
| Hashtable<Node, Integer> nt = (Hashtable<Node, Integer>)gf.get("nodeTable", null); |
| |
| xml11Version = gf.get("xml11Version", false); |
| |
| //convert Hashtables back to HashMaps |
| if (nud != null) { |
| nodeUserData = new HashMap<>(); |
| for (Map.Entry<Node, Hashtable<String, UserDataRecord>> e : nud.entrySet()) { |
| nodeUserData.put(e.getKey(), new HashMap<>(e.getValue())); |
| } |
| } |
| |
| if (ids != null) identifiers = new HashMap<>(ids); |
| if (nt != null) nodeTable = new HashMap<>(nt); |
| } |
| } // class CoreDocumentImpl |