1 /*
   2  * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.xerces.internal.dom;
  21 
  22 import com.sun.org.apache.xerces.internal.impl.Constants;
  23 import com.sun.org.apache.xerces.internal.util.URI;
  24 import com.sun.org.apache.xerces.internal.util.XML11Char;
  25 import com.sun.org.apache.xerces.internal.util.XMLChar;
  26 import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
  27 import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
  28 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  29 import java.io.IOException;
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.io.ObjectStreamField;
  33 import java.lang.reflect.Constructor;
  34 import java.util.HashMap;
  35 import java.util.Hashtable;
  36 import java.util.Map;
  37 import org.w3c.dom.Attr;
  38 import org.w3c.dom.CDATASection;
  39 import org.w3c.dom.Comment;
  40 import org.w3c.dom.DOMConfiguration;
  41 import org.w3c.dom.DOMException;
  42 import org.w3c.dom.DOMImplementation;
  43 import org.w3c.dom.Document;
  44 import org.w3c.dom.DocumentFragment;
  45 import org.w3c.dom.DocumentType;
  46 import org.w3c.dom.Element;
  47 import org.w3c.dom.Entity;
  48 import org.w3c.dom.EntityReference;
  49 import org.w3c.dom.NamedNodeMap;
  50 import org.w3c.dom.Node;
  51 import org.w3c.dom.NodeList;
  52 import org.w3c.dom.Notation;
  53 import org.w3c.dom.ProcessingInstruction;
  54 import org.w3c.dom.Text;
  55 import org.w3c.dom.UserDataHandler;
  56 import org.w3c.dom.events.Event;
  57 import org.w3c.dom.events.EventListener;
  58 import org.w3c.dom.ls.DOMImplementationLS;
  59 import org.w3c.dom.ls.LSSerializer;
  60 
  61 /**
  62  * The Document interface represents the entire HTML or XML document.
  63  * Conceptually, it is the root of the document tree, and provides the
  64  * primary access to the document's data.
  65  * <P>
  66  * Since elements, text nodes, comments, processing instructions,
  67  * etc. cannot exist outside the context of a Document, the Document
  68  * interface also contains the factory methods needed to create these
  69  * objects. The Node objects created have a ownerDocument attribute
  70  * which associates them with the Document within whose context they
  71  * were created.
  72  * <p>
  73  * The CoreDocumentImpl class only implements the DOM Core. Additional modules
  74  * are supported by the more complete DocumentImpl subclass.
  75  * <p>
  76  * <b>Note:</b> When any node in the document is serialized, the
  77  * entire document is serialized along with it.
  78  *
  79  * @xerces.internal
  80  *
  81  * @author Arnaud  Le Hors, IBM
  82  * @author Joe Kesselman, IBM
  83  * @author Andy Clark, IBM
  84  * @author Ralf Pfeiffer, IBM
  85  * @since  PR-DOM-Level-1-19980818.
  86  */
  87 public class CoreDocumentImpl
  88         extends ParentNode implements Document {
  89 
  90     /**
  91      * TODO:: 1. Change XML11Char method names similar to XMLChar. That will
  92      * prevent lot of dirty version checking code.
  93      *
  94      * 2. IMO during cloneNode qname/isXMLName check should not be made.
  95      */
  96     //
  97     // Constants
  98     //
  99 
 100     /** Serialization version. */
 101     static final long serialVersionUID = 0;
 102 
 103     //
 104     // Data
 105     //
 106 
 107     // document information
 108 
 109     /** Document type. */
 110     protected DocumentTypeImpl docType;
 111 
 112     /** Document element. */
 113     protected ElementImpl docElement;
 114 
 115     /** NodeListCache free list */
 116     transient NodeListCache fFreeNLCache;
 117 
 118     /**Experimental DOM Level 3 feature: Document encoding */
 119     protected String encoding;
 120 
 121     /**Experimental DOM Level 3 feature: Document actualEncoding */
 122     protected String actualEncoding;
 123 
 124     /**Experimental DOM Level 3 feature: Document version */
 125     protected String version;
 126 
 127     /**Experimental DOM Level 3 feature: Document standalone */
 128     protected boolean standalone;
 129 
 130     /**Experimental DOM Level 3 feature: documentURI */
 131     protected String fDocumentURI;
 132 
 133     //Revisit :: change to a better data structure.
 134     /** Table for user data attached to this document nodes. */
 135     private Map<Node, Map<String, UserDataRecord>> nodeUserData;
 136 
 137     /** Identifiers. */
 138     protected Map<String, Node> identifiers;
 139 
 140     // DOM Level 3: normalizeDocument
 141     transient DOMNormalizer domNormalizer = null;
 142     transient DOMConfigurationImpl fConfiguration = null;
 143 
 144     // support of XPath API
 145     transient Object fXPathEvaluator = null;
 146 
 147     /** Table for quick check of child insertion. */
 148     private final static int[] kidOK;
 149 
 150     /**
 151      * Number of alterations made to this document since its creation.
 152      * Serves as a "dirty bit" so that live objects such as NodeList can
 153      * recognize when an alteration has been made and discard its cached
 154      * state information.
 155      * <p>
 156      * Any method that alters the tree structure MUST cause or be
 157      * accompanied by a call to changed(), to inform it that any outstanding
 158      * NodeLists may have to be updated.
 159      * <p>
 160      * (Required because NodeList is simultaneously "live" and integer-
 161      * indexed -- a bad decision in the DOM's design.)
 162      * <p>
 163      * Note that changes which do not affect the tree's structure -- changing
 164      * the node's name, for example -- do _not_ have to call changed().
 165      * <p>
 166      * Alternative implementation would be to use a cryptographic
 167      * Digest value rather than a count. This would have the advantage that
 168      * "harmless" changes (those producing equal() trees) would not force
 169      * NodeList to resynchronize. Disadvantage is that it's slightly more prone
 170      * to "false negatives", though that's the difference between "wildly
 171      * unlikely" and "absurdly unlikely". IF we start maintaining digests,
 172      * we should consider taking advantage of them.
 173      *
 174      * Note: This used to be done a node basis, so that we knew what
 175      * subtree changed. But since only DeepNodeList really use this today,
 176      * the gain appears to be really small compared to the cost of having
 177      * an int on every (parent) node plus having to walk up the tree all the
 178      * way to the root to mark the branch as changed everytime a node is
 179      * changed.
 180      * So we now have a single counter global to the document. It means that
 181      * some objects may flush their cache more often than necessary, but this
 182      * makes nodes smaller and only the document needs to be marked as changed.
 183      */
 184     protected int changes = 0;
 185 
 186     // experimental
 187 
 188     /** Allow grammar access. */
 189     protected boolean allowGrammarAccess;
 190 
 191     /** Bypass error checking. */
 192     protected boolean errorChecking = true;
 193     /** Ancestor checking */
 194     protected boolean ancestorChecking = true;
 195 
 196     //Did version change at any point when the document was created ?
 197     //this field helps us to optimize when normalizingDocument.
 198     protected boolean xmlVersionChanged = false ;
 199 
 200     /** The following are required for compareDocumentPosition
 201      */
 202     // Document number.   Documents are ordered across the implementation using
 203     // positive integer values.  Documents are assigned numbers on demand.
 204     private int documentNumber=0;
 205     // Node counter and table.  Used to assign numbers to nodes for this
 206     // document.  Node number values are negative integers.  Nodes are
 207     // assigned numbers on demand.
 208     private int nodeCounter = 0;
 209     private Map<Node, Integer> nodeTable;
 210     private boolean xml11Version = false; //by default 1.0
 211     //
 212     // Static initialization
 213     //
 214 
 215     static {
 216 
 217         kidOK = new int[13];
 218 
 219         kidOK[DOCUMENT_NODE] =
 220         1 << ELEMENT_NODE | 1 << PROCESSING_INSTRUCTION_NODE |
 221         1 << COMMENT_NODE | 1 << DOCUMENT_TYPE_NODE;
 222 
 223         kidOK[DOCUMENT_FRAGMENT_NODE] =
 224         kidOK[ENTITY_NODE] =
 225         kidOK[ENTITY_REFERENCE_NODE] =
 226         kidOK[ELEMENT_NODE] =
 227         1 << ELEMENT_NODE | 1 << PROCESSING_INSTRUCTION_NODE |
 228         1 << COMMENT_NODE | 1 << TEXT_NODE |
 229         1 << CDATA_SECTION_NODE | 1 << ENTITY_REFERENCE_NODE ;
 230 
 231 
 232         kidOK[ATTRIBUTE_NODE] =
 233         1 << TEXT_NODE | 1 << ENTITY_REFERENCE_NODE;
 234 
 235         kidOK[DOCUMENT_TYPE_NODE] =
 236         kidOK[PROCESSING_INSTRUCTION_NODE] =
 237         kidOK[COMMENT_NODE] =
 238         kidOK[TEXT_NODE] =
 239         kidOK[CDATA_SECTION_NODE] =
 240         kidOK[NOTATION_NODE] =
 241         0;
 242 
 243     } // static
 244 
 245     /**
 246      * @serialField docType DocumentTypeImpl document type
 247      * @serialField docElement ElementImpl document element
 248      * @serialField fFreeNLCache NodeListCache NodeListCache free list
 249      * @serialField encoding String Document encoding
 250      * @serialField actualEncoding String Document actualEncoding
 251      * @serialField version String Document version
 252      * @serialField standalone boolean Document standalone
 253      * @serialField fDocumentURI String Document URI
 254      * @serialField userData Hashtable user data attached to the nodes. Note that
 255      * it was original called "userData". It has been changed to nodeUserData to
 256      * avoid confusion with those that are actually values of the map.
 257      * @serialField identifiers Hashtable identifiers
 258      * @serialField changes int flag indicates whether the node has changed
 259      * @serialField allowGrammarAccess boolean Allow grammar access
 260      * @serialField errorChecking boolean Bypass error checking
 261      * @serialField ancestorChecking boolean Ancestor checking
 262      * @serialField xmlVersionChanged boolean Indicate whether the version has changed
 263      * @serialField documentNumber int Document number
 264      * @serialField nodeCounter int Node counter
 265      * @serialField nodeTable Hashtable Node table
 266      * @serialField xml11Version boolean XML version
 267      */
 268     private static final ObjectStreamField[] serialPersistentFields =
 269         new ObjectStreamField[] {
 270             new ObjectStreamField("docType", DocumentTypeImpl.class),
 271             new ObjectStreamField("docElement", ElementImpl.class),
 272             new ObjectStreamField("fFreeNLCache", NodeListCache.class),
 273             new ObjectStreamField("encoding", String.class),
 274             new ObjectStreamField("actualEncoding", String.class),
 275             new ObjectStreamField("version", String.class),
 276             new ObjectStreamField("standalone", boolean.class),
 277             new ObjectStreamField("fDocumentURI", String.class),
 278             new ObjectStreamField("userData", Hashtable.class),
 279             new ObjectStreamField("identifiers", Hashtable.class),
 280             new ObjectStreamField("changes", int.class),
 281             new ObjectStreamField("allowGrammarAccess", boolean.class),
 282             new ObjectStreamField("errorChecking", boolean.class),
 283             new ObjectStreamField("ancestorChecking", boolean.class),
 284             new ObjectStreamField("xmlVersionChanged", boolean.class),
 285             new ObjectStreamField("documentNumber", int.class),
 286             new ObjectStreamField("nodeCounter", int.class),
 287             new ObjectStreamField("nodeTable", Hashtable.class),
 288             new ObjectStreamField("xml11Version", boolean.class),
 289         };
 290 
 291     //
 292     // Constructors
 293     //
 294 
 295     /**
 296      * NON-DOM: Actually creating a Document is outside the DOM's spec,
 297      * since it has to operate in terms of a particular implementation.
 298      */
 299     public CoreDocumentImpl() {
 300         this(false);
 301     }
 302 
 303     /** Constructor. */
 304     public CoreDocumentImpl(boolean grammarAccess) {
 305         super(null);
 306         ownerDocument = this;
 307         allowGrammarAccess = grammarAccess;
 308         String systemProp = SecuritySupport.getSystemProperty(Constants.SUN_DOM_PROPERTY_PREFIX+Constants.SUN_DOM_ANCESTOR_CHECCK);
 309         if (systemProp != null) {
 310             if (systemProp.equalsIgnoreCase("false")) {
 311                 ancestorChecking = false;
 312             }
 313         }
 314     }
 315 
 316     /**
 317      * For DOM2 support.
 318      * The createDocument factory method is in DOMImplementation.
 319      */
 320     public CoreDocumentImpl(DocumentType doctype) {
 321         this(doctype, false);
 322     }
 323 
 324     /** For DOM2 support. */
 325     public CoreDocumentImpl(DocumentType doctype, boolean grammarAccess) {
 326         this(grammarAccess);
 327         if (doctype != null) {
 328             DocumentTypeImpl doctypeImpl;
 329             try {
 330                 doctypeImpl = (DocumentTypeImpl) doctype;
 331             } catch (ClassCastException e) {
 332                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
 333                 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
 334             }
 335             doctypeImpl.ownerDocument = this;
 336             appendChild(doctype);
 337         }
 338     }
 339 
 340     //
 341     // Node methods
 342     //
 343 
 344     // even though ownerDocument refers to this in this implementation
 345     // the DOM Level 2 spec says it must be null, so make it appear so
 346     final public Document getOwnerDocument() {
 347         return null;
 348     }
 349 
 350     /** Returns the node type. */
 351     public short getNodeType() {
 352         return Node.DOCUMENT_NODE;
 353     }
 354 
 355     /** Returns the node name. */
 356     public String getNodeName() {
 357         return "#document";
 358     }
 359 
 360     /**
 361      * Deep-clone a document, including fixing ownerDoc for the cloned
 362      * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
 363      * protection. I've chosen to implement it by calling importNode
 364      * which is DOM Level 2.
 365      *
 366      * @return org.w3c.dom.Node
 367      * @param deep boolean, iff true replicate children
 368      */
 369     public Node cloneNode(boolean deep) {
 370 
 371         CoreDocumentImpl newdoc = new CoreDocumentImpl();
 372         callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
 373         cloneNode(newdoc, deep);
 374 
 375         return newdoc;
 376 
 377     } // cloneNode(boolean):Node
 378 
 379 
 380     /**
 381      * internal method to share code with subclass
 382      **/
 383     protected void cloneNode(CoreDocumentImpl newdoc, boolean deep) {
 384 
 385         // clone the children by importing them
 386         if (needsSyncChildren()) {
 387             synchronizeChildren();
 388         }
 389 
 390         if (deep) {
 391             Map<Node, String> reversedIdentifiers = null;
 392 
 393             if (identifiers != null) {
 394                 // Build a reverse mapping from element to identifier.
 395                 reversedIdentifiers = new HashMap<>(identifiers.size());
 396                 for (String elementId : identifiers.keySet()) {
 397                     reversedIdentifiers.put(identifiers.get(elementId), elementId);
 398                 }
 399             }
 400 
 401             // Copy children into new document.
 402             for (ChildNode kid = firstChild; kid != null;
 403                     kid = kid.nextSibling) {
 404                 newdoc.appendChild(newdoc.importNode(kid, true, true,
 405                         reversedIdentifiers));
 406             }
 407         }
 408 
 409         // experimental
 410         newdoc.allowGrammarAccess = allowGrammarAccess;
 411         newdoc.errorChecking = errorChecking;
 412 
 413     } // cloneNode(CoreDocumentImpl,boolean):void
 414 
 415     /**
 416      * Since a Document may contain at most one top-level Element child,
 417      * and at most one DocumentType declaraction, we need to subclass our
 418      * add-children methods to implement this constraint.
 419      * Since appendChild() is implemented as insertBefore(,null),
 420      * altering the latter fixes both.
 421      * <p>
 422      * While I'm doing so, I've taken advantage of the opportunity to
 423      * cache documentElement and docType so we don't have to
 424      * search for them.
 425      *
 426      * REVISIT: According to the spec it is not allowed to alter neither the
 427      * document element nor the document type in any way
 428      */
 429     public Node insertBefore(Node newChild, Node refChild)
 430             throws DOMException {
 431 
 432         // Only one such child permitted
 433         int type = newChild.getNodeType();
 434         if (errorChecking) {
 435             if((type == Node.ELEMENT_NODE && docElement != null) ||
 436             (type == Node.DOCUMENT_TYPE_NODE && docType != null)) {
 437                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
 438                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
 439             }
 440         }
 441         // Adopt orphan doctypes
 442         if (newChild.getOwnerDocument() == null &&
 443         newChild instanceof DocumentTypeImpl) {
 444             ((DocumentTypeImpl) newChild).ownerDocument = this;
 445         }
 446         super.insertBefore(newChild,refChild);
 447 
 448         // If insert succeeded, cache the kid appropriately
 449         if (type == Node.ELEMENT_NODE) {
 450             docElement = (ElementImpl)newChild;
 451         }
 452         else if (type == Node.DOCUMENT_TYPE_NODE) {
 453             docType = (DocumentTypeImpl)newChild;
 454         }
 455 
 456         return newChild;
 457 
 458     } // insertBefore(Node,Node):Node
 459 
 460     /**
 461      * Since insertBefore caches the docElement (and, currently, docType),
 462      * removeChild has to know how to undo the cache
 463      *
 464      * REVISIT: According to the spec it is not allowed to alter neither the
 465      * document element nor the document type in any way
 466      */
 467     public Node removeChild(Node oldChild) throws DOMException {
 468 
 469         super.removeChild(oldChild);
 470 
 471         // If remove succeeded, un-cache the kid appropriately
 472         int type = oldChild.getNodeType();
 473         if(type == Node.ELEMENT_NODE) {
 474             docElement = null;
 475         }
 476         else if (type == Node.DOCUMENT_TYPE_NODE) {
 477             docType = null;
 478         }
 479 
 480         return oldChild;
 481 
 482     }   // removeChild(Node):Node
 483 
 484     /**
 485      * Since we cache the docElement (and, currently, docType),
 486      * replaceChild has to update the cache
 487      *
 488      * REVISIT: According to the spec it is not allowed to alter neither the
 489      * document element nor the document type in any way
 490      */
 491     public Node replaceChild(Node newChild, Node oldChild)
 492             throws DOMException {
 493 
 494         // Adopt orphan doctypes
 495         if (newChild.getOwnerDocument() == null &&
 496         newChild instanceof DocumentTypeImpl) {
 497             ((DocumentTypeImpl) newChild).ownerDocument = this;
 498         }
 499 
 500         if (errorChecking &&((docType != null &&
 501             oldChild.getNodeType() != Node.DOCUMENT_TYPE_NODE &&
 502             newChild.getNodeType() == Node.DOCUMENT_TYPE_NODE)
 503             || (docElement != null &&
 504             oldChild.getNodeType() != Node.ELEMENT_NODE &&
 505             newChild.getNodeType() == Node.ELEMENT_NODE))) {
 506 
 507             throw new DOMException(
 508                     DOMException.HIERARCHY_REQUEST_ERR,
 509                     DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
 510         }
 511         super.replaceChild(newChild, oldChild);
 512 
 513         int type = oldChild.getNodeType();
 514         if(type == Node.ELEMENT_NODE) {
 515             docElement = (ElementImpl)newChild;
 516         }
 517         else if (type == Node.DOCUMENT_TYPE_NODE) {
 518             docType = (DocumentTypeImpl)newChild;
 519         }
 520         return oldChild;
 521     }   // replaceChild(Node,Node):Node
 522 
 523     /*
 524      * Get Node text content
 525      * @since DOM Level 3
 526      */
 527     public String getTextContent() throws DOMException {
 528         return null;
 529     }
 530 
 531     /*
 532      * Set Node text content
 533      * @since DOM Level 3
 534      */
 535     public void setTextContent(String textContent)
 536             throws DOMException {
 537         // no-op
 538     }
 539 
 540     /**
 541      * @since DOM Level 3
 542      */
 543     public Object getFeature(String feature, String version) {
 544         return super.getFeature(feature, version);
 545     }
 546 
 547     //
 548     // Document methods
 549     //
 550 
 551     // factory methods
 552 
 553     /**
 554      * Factory method; creates an Attribute having this Document as its
 555      * OwnerDoc.
 556      *
 557      * @param name The name of the attribute. Note that the attribute's value is
 558      * _not_ established at the factory; remember to set it!
 559      *
 560      * @throws DOMException(INVALID_NAME_ERR)
 561      * if the attribute name is not acceptable.
 562      */
 563     public Attr createAttribute(String name)
 564             throws DOMException {
 565 
 566         if (errorChecking && !isXMLName(name,xml11Version)) {
 567             String msg =
 568                 DOMMessageFormatter.formatMessage(
 569                             DOMMessageFormatter.DOM_DOMAIN,
 570                             "INVALID_CHARACTER_ERR",
 571                             null);
 572             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
 573         }
 574         return new AttrImpl(this, name);
 575 
 576     } // createAttribute(String):Attr
 577 
 578     /**
 579      * Factory method; creates a CDATASection having this Document as
 580      * its OwnerDoc.
 581      *
 582      * @param data The initial contents of the CDATA
 583      *
 584      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
 585      * not yet implemented.)
 586      */
 587     public CDATASection createCDATASection(String data)
 588             throws DOMException {
 589         return new CDATASectionImpl(this, data);
 590     }
 591 
 592     /**
 593      * Factory method; creates a Comment having this Document as its
 594      * OwnerDoc.
 595      *
 596      * @param data The initial contents of the Comment. */
 597     public Comment createComment(String data) {
 598         return new CommentImpl(this, data);
 599     }
 600 
 601     /**
 602      * Factory method; creates a DocumentFragment having this Document
 603      * as its OwnerDoc.
 604      */
 605     public DocumentFragment createDocumentFragment() {
 606         return new DocumentFragmentImpl(this);
 607     }
 608 
 609     /**
 610      * Factory method; creates an Element having this Document
 611      * as its OwnerDoc.
 612      *
 613      * @param tagName The name of the element type to instantiate. For
 614      * XML, this is case-sensitive. For HTML, the tagName parameter may
 615      * be provided in any case, but it must be mapped to the canonical
 616      * uppercase form by the DOM implementation.
 617      *
 618      * @throws DOMException(INVALID_NAME_ERR) if the tag name is not
 619      * acceptable.
 620      */
 621     public Element createElement(String tagName)
 622             throws DOMException {
 623 
 624         if (errorChecking && !isXMLName(tagName,xml11Version)) {
 625             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
 626             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
 627         }
 628         return new ElementImpl(this, tagName);
 629 
 630     } // createElement(String):Element
 631 
 632     /**
 633      * Factory method; creates an EntityReference having this Document
 634      * as its OwnerDoc.
 635      *
 636      * @param name The name of the Entity we wish to refer to
 637      *
 638      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
 639      * nonstandard entities are not permitted. (HTML not yet
 640      * implemented.)
 641      */
 642     public EntityReference createEntityReference(String name)
 643             throws DOMException {
 644 
 645         if (errorChecking && !isXMLName(name,xml11Version)) {
 646             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
 647             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
 648         }
 649         return new EntityReferenceImpl(this, name);
 650 
 651     } // createEntityReference(String):EntityReference
 652 
 653     /**
 654      * Factory method; creates a ProcessingInstruction having this Document
 655      * as its OwnerDoc.
 656      *
 657      * @param target The target "processor channel"
 658      * @param data Parameter string to be passed to the target.
 659      *
 660      * @throws DOMException(INVALID_NAME_ERR) if the target name is not
 661      * acceptable.
 662      *
 663      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents. (HTML
 664      * not yet implemented.)
 665      */
 666     public ProcessingInstruction createProcessingInstruction(String target,
 667             String data)
 668             throws DOMException {
 669 
 670         if (errorChecking && !isXMLName(target,xml11Version)) {
 671             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
 672             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
 673         }
 674         return new ProcessingInstructionImpl(this, target, data);
 675 
 676     } // createProcessingInstruction(String,String):ProcessingInstruction
 677 
 678     /**
 679      * Factory method; creates a Text node having this Document as its
 680      * OwnerDoc.
 681      *
 682      * @param data The initial contents of the Text.
 683      */
 684     public Text createTextNode(String data) {
 685         return new TextImpl(this, data);
 686     }
 687 
 688     // other document methods
 689 
 690     /**
 691      * For XML, this provides access to the Document Type Definition.
 692      * For HTML documents, and XML documents which don't specify a DTD,
 693      * it will be null.
 694      */
 695     public DocumentType getDoctype() {
 696         if (needsSyncChildren()) {
 697             synchronizeChildren();
 698         }
 699         return docType;
 700     }
 701 
 702     /**
 703      * Convenience method, allowing direct access to the child node
 704      * which is considered the root of the actual document content. For
 705      * HTML, where it is legal to have more than one Element at the top
 706      * level of the document, we pick the one with the tagName
 707      * "HTML". For XML there should be only one top-level
 708      *
 709      * (HTML not yet supported.)
 710      */
 711     public Element getDocumentElement() {
 712         if (needsSyncChildren()) {
 713             synchronizeChildren();
 714         }
 715         return docElement;
 716     }
 717 
 718     /**
 719      * Return a <em>live</em> collection of all descendent Elements (not just
 720      * immediate children) having the specified tag name.
 721      *
 722      * @param tagname The type of Element we want to gather. "*" will be
 723      * taken as a wildcard, meaning "all elements in the document."
 724      *
 725      * @see DeepNodeListImpl
 726      */
 727     public NodeList getElementsByTagName(String tagname) {
 728         return new DeepNodeListImpl(this,tagname);
 729     }
 730 
 731     /**
 732      * Retrieve information describing the abilities of this particular
 733      * DOM implementation. Intended to support applications that may be
 734      * using DOMs retrieved from several different sources, potentially
 735      * with different underlying representations.
 736      */
 737     public DOMImplementation getImplementation() {
 738         // Currently implemented as a singleton, since it's hardcoded
 739         // information anyway.
 740         return CoreDOMImplementationImpl.getDOMImplementation();
 741     }
 742 
 743     //
 744     // Public methods
 745     //
 746 
 747     // properties
 748 
 749     /**
 750      * Sets whether the DOM implementation performs error checking
 751      * upon operations. Turning off error checking only affects
 752      * the following DOM checks:
 753      * <ul>
 754      * <li>Checking strings to make sure that all characters are
 755      *     legal XML characters
 756      * <li>Hierarchy checking such as allowed children, checks for
 757      *     cycles, etc.
 758      * </ul>
 759      * <p>
 760      * Turning off error checking does <em>not</em> turn off the
 761      * following checks:
 762      * <ul>
 763      * <li>Read only checks
 764      * <li>Checks related to DOM events
 765      * </ul>
 766      */
 767 
 768     public void setErrorChecking(boolean check) {
 769         errorChecking = check;
 770     }
 771 
 772     /*
 773      * DOM Level 3 WD - Experimental.
 774      */
 775     public void setStrictErrorChecking(boolean check) {
 776         errorChecking = check;
 777     }
 778 
 779     /**
 780      * Returns true if the DOM implementation performs error checking.
 781      */
 782     public boolean getErrorChecking() {
 783         return errorChecking;
 784     }
 785 
 786     /*
 787      * DOM Level 3 WD - Experimental.
 788      */
 789     public boolean getStrictErrorChecking() {
 790         return errorChecking;
 791     }
 792 
 793     /**
 794      * DOM Level 3 CR - Experimental. (Was getActualEncoding)
 795      *
 796      * An attribute specifying the encoding used for this document
 797      * at the time of the parsing. This is <code>null</code> when
 798      * it is not known, such as when the <code>Document</code> was
 799      * created in memory.
 800      * @since DOM Level 3
 801      */
 802     public String getInputEncoding() {
 803         return actualEncoding;
 804     }
 805 
 806     /**
 807      * DOM Internal
 808      * (Was a DOM L3 Core WD public interface method setActualEncoding )
 809      *
 810      * An attribute specifying the actual encoding of this document. This is
 811      * <code>null</code> otherwise.
 812      * <br> This attribute represents the property [character encoding scheme]
 813      * defined in .
 814      */
 815     public void setInputEncoding(String value) {
 816         actualEncoding = value;
 817     }
 818 
 819     /**
 820      * DOM Internal
 821      * (Was a DOM L3 Core WD public interface method setXMLEncoding )
 822      *
 823      * An attribute specifying, as part of the XML declaration,
 824      * the encoding of this document. This is null when unspecified.
 825      */
 826     public void setXmlEncoding(String value) {
 827         encoding = value;
 828     }
 829 
 830     /**
 831      * @deprecated This method is internal and only exists for
 832      * compatibility with older applications. New applications
 833      * should never call this method.
 834      */
 835     @Deprecated
 836     public void setEncoding(String value) {
 837         setXmlEncoding(value);
 838     }
 839 
 840     /**
 841      * DOM Level 3 WD - Experimental.
 842      * The encoding of this document (part of XML Declaration)
 843      */
 844     public String getXmlEncoding() {
 845         return encoding;
 846     }
 847 
 848     /**
 849      * @deprecated This method is internal and only exists for
 850      * compatibility with older applications. New applications
 851      * should never call this method.
 852      */
 853     @Deprecated
 854     public String getEncoding() {
 855         return getXmlEncoding();
 856     }
 857 
 858     /**
 859      * DOM Level 3 CR - Experimental.
 860      * version - An attribute specifying, as part of the XML declaration,
 861      * the version number of this document.
 862      */
 863     public void setXmlVersion(String value) {
 864         if(value.equals("1.0") || value.equals("1.1")){
 865             //we need to change the flag value only --
 866             // when the version set is different than already set.
 867             if(!getXmlVersion().equals(value)){
 868                 xmlVersionChanged = true ;
 869                 //change the normalization value back to false
 870                 isNormalized(false);
 871                 version = value;
 872             }
 873         }
 874         else{
 875             //NOT_SUPPORTED_ERR: Raised if the vesion is set to a value that is not supported by
 876             //this document
 877             //we dont support any other XML version
 878             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
 879             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
 880 
 881         }
 882         if((getXmlVersion()).equals("1.1")){
 883             xml11Version = true;
 884         }
 885         else{
 886             xml11Version = false;
 887         }
 888     }
 889 
 890     /**
 891      * @deprecated This method is internal and only exists for
 892      * compatibility with older applications. New applications
 893      * should never call this method.
 894      */
 895     @Deprecated
 896     public void setVersion(String value) {
 897         setXmlVersion(value);
 898     }
 899 
 900     /**
 901      * DOM Level 3 WD - Experimental.
 902      * The version of this document (part of XML Declaration)
 903      */
 904 
 905     public String getXmlVersion() {
 906         return (version == null)?"1.0":version;
 907     }
 908 
 909     /**
 910      * @deprecated This method is internal and only exists for
 911      * compatibility with older applications. New applications
 912      * should never call this method.
 913      */
 914     @Deprecated
 915     public String getVersion() {
 916         return getXmlVersion();
 917     }
 918 
 919     /**
 920      * DOM Level 3 CR - Experimental.
 921      *
 922      * Xmlstandalone - An attribute specifying, as part of the XML declaration,
 923      * whether this document is standalone
 924      * @exception DOMException
 925      *    NOT_SUPPORTED_ERR: Raised if this document does not support the
 926      *   "XML" feature.
 927      * @since DOM Level 3
 928      */
 929     public void setXmlStandalone(boolean value)
 930             throws DOMException {
 931         standalone = value;
 932     }
 933 
 934     /**
 935      * @deprecated This method is internal and only exists for
 936      * compatibility with older applications. New applications
 937      * should never call this method.
 938      */
 939     @Deprecated
 940     public void setStandalone(boolean value) {
 941         setXmlStandalone(value);
 942     }
 943 
 944     /**
 945      * DOM Level 3 WD - Experimental.
 946      * standalone that specifies whether this document is standalone
 947      * (part of XML Declaration)
 948      */
 949     public boolean getXmlStandalone() {
 950         return standalone;
 951     }
 952 
 953     /**
 954      * @deprecated This method is internal and only exists for
 955      * compatibility with older applications. New applications
 956      * should never call this method.
 957      */
 958     @Deprecated
 959     public boolean getStandalone() {
 960         return getXmlStandalone();
 961     }
 962 
 963     /**
 964      * DOM Level 3 WD - Experimental.
 965      * The location of the document or <code>null</code> if undefined.
 966      * <br>Beware that when the <code>Document</code> supports the feature
 967      * "HTML" , the href attribute of the HTML BASE element takes precedence
 968      * over this attribute.
 969      * @since DOM Level 3
 970      */
 971     public String getDocumentURI(){
 972         return fDocumentURI;
 973     }
 974 
 975 
 976     /**
 977      * DOM Level 3 WD - Experimental.
 978      * Renaming node
 979      */
 980     public Node renameNode(Node n,String namespaceURI,String name)
 981     throws DOMException{
 982 
 983         if (errorChecking && n.getOwnerDocument() != this && n != this) {
 984             String msg = DOMMessageFormatter.formatMessage(
 985                     DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
 986             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
 987         }
 988         switch (n.getNodeType()) {
 989             case ELEMENT_NODE: {
 990                 ElementImpl el = (ElementImpl) n;
 991                 if (el instanceof ElementNSImpl) {
 992                     ((ElementNSImpl) el).rename(namespaceURI, name);
 993 
 994                     // fire user data NODE_RENAMED event
 995                     callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
 996                 }
 997                 else {
 998                     if (namespaceURI == null) {
 999                         if (errorChecking) {
1000                             int colon1 = name.indexOf(':');
1001                             if(colon1 != -1){
1002                                 String msg =
1003                                     DOMMessageFormatter.formatMessage(
1004                                                 DOMMessageFormatter.DOM_DOMAIN,
1005                                                 "NAMESPACE_ERR",
1006                                                 null);
1007                                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
1008                             }
1009                             if (!isXMLName(name,xml11Version)) {
1010                                 String msg = DOMMessageFormatter.formatMessage(
1011                                         DOMMessageFormatter.DOM_DOMAIN,
1012                                         "INVALID_CHARACTER_ERR", null);
1013                                 throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
1014                                         msg);
1015                             }
1016                         }
1017                         el.rename(name);
1018 
1019                         // fire user data NODE_RENAMED event
1020                         callUserDataHandlers(el, null,
1021                                 UserDataHandler.NODE_RENAMED);
1022                     }
1023                     else {
1024                         // we need to create a new object
1025                         ElementNSImpl nel =
1026                             new ElementNSImpl(this, namespaceURI, name);
1027 
1028                         // register event listeners on new node
1029                         copyEventListeners(el, nel);
1030 
1031                         // remove user data from old node
1032                         Map<String, UserDataRecord> data = removeUserDataTable(el);
1033 
1034                         // remove old node from parent if any
1035                         Node parent = el.getParentNode();
1036                         Node nextSib = el.getNextSibling();
1037                         if (parent != null) {
1038                             parent.removeChild(el);
1039                         }
1040                         // move children to new node
1041                         Node child = el.getFirstChild();
1042                         while (child != null) {
1043                             el.removeChild(child);
1044                             nel.appendChild(child);
1045                             child = el.getFirstChild();
1046                         }
1047                         // move specified attributes to new node
1048                         nel.moveSpecifiedAttributes(el);
1049 
1050                         // attach user data to new node
1051                         setUserDataTable(nel, data);
1052 
1053                         // and fire user data NODE_RENAMED event
1054                         callUserDataHandlers(el, nel,
1055                                 UserDataHandler.NODE_RENAMED);
1056 
1057                         // insert new node where old one was
1058                         if (parent != null) {
1059                             parent.insertBefore(nel, nextSib);
1060                         }
1061                         el = nel;
1062                     }
1063                 }
1064                 // fire ElementNameChanged event
1065                 renamedElement((Element) n, el);
1066                 return el;
1067             }
1068             case ATTRIBUTE_NODE: {
1069                 AttrImpl at = (AttrImpl) n;
1070 
1071                 // dettach attr from element
1072                 Element el = at.getOwnerElement();
1073                 if (el != null) {
1074                     el.removeAttributeNode(at);
1075                 }
1076                 if (n instanceof AttrNSImpl) {
1077                     ((AttrNSImpl) at).rename(namespaceURI, name);
1078                     // reattach attr to element
1079                     if (el != null) {
1080                         el.setAttributeNodeNS(at);
1081                     }
1082 
1083                     // fire user data NODE_RENAMED event
1084                     callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1085                 }
1086                 else {
1087                     if (namespaceURI == null) {
1088                         at.rename(name);
1089                         // reattach attr to element
1090                         if (el != null) {
1091                             el.setAttributeNode(at);
1092                         }
1093 
1094                         // fire user data NODE_RENAMED event
1095                         callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1096                     }
1097                     else {
1098                         // we need to create a new object
1099                         AttrNSImpl nat = new AttrNSImpl(this, namespaceURI, name);
1100 
1101                         // register event listeners on new node
1102                         copyEventListeners(at, nat);
1103 
1104                         // remove user data from old node
1105                         Map<String, UserDataRecord> data = removeUserDataTable(at);
1106 
1107                         // move children to new node
1108                         Node child = at.getFirstChild();
1109                         while (child != null) {
1110                             at.removeChild(child);
1111                             nat.appendChild(child);
1112                             child = at.getFirstChild();
1113                         }
1114 
1115                         // attach user data to new node
1116                         setUserDataTable(nat, data);
1117 
1118                         // and fire user data NODE_RENAMED event
1119                         callUserDataHandlers(at, nat, UserDataHandler.NODE_RENAMED);
1120 
1121                         // reattach attr to element
1122                         if (el != null) {
1123                             el.setAttributeNode(nat);
1124                         }
1125                         at = nat;
1126                     }
1127                 }
1128                 // fire AttributeNameChanged event
1129                 renamedAttrNode((Attr) n, at);
1130 
1131                 return at;
1132             }
1133             default: {
1134                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1135                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1136             }
1137         }
1138 
1139     }
1140 
1141 
1142     /**
1143      *  DOM Level 3 WD - Experimental
1144      *  Normalize document.
1145      */
1146     public void normalizeDocument(){
1147         // No need to normalize if already normalized.
1148         if (isNormalized() && !isNormalizeDocRequired()) {
1149             return;
1150         }
1151         if (needsSyncChildren()) {
1152             synchronizeChildren();
1153         }
1154 
1155         if (domNormalizer == null) {
1156             domNormalizer = new DOMNormalizer();
1157         }
1158 
1159         if (fConfiguration == null) {
1160             fConfiguration =  new DOMConfigurationImpl();
1161         }
1162         else {
1163             fConfiguration.reset();
1164         }
1165 
1166         domNormalizer.normalizeDocument(this, fConfiguration);
1167         isNormalized(true);
1168         //set the XMLversion changed value to false -- once we have finished
1169         //doing normalization
1170         xmlVersionChanged = false ;
1171     }
1172 
1173 
1174     /**
1175      * DOM Level 3 CR - Experimental
1176      *
1177      *  The configuration used when <code>Document.normalizeDocument</code> is
1178      * invoked.
1179      * @since DOM Level 3
1180      */
1181     public DOMConfiguration getDomConfig(){
1182         if (fConfiguration == null) {
1183             fConfiguration = new DOMConfigurationImpl();
1184         }
1185         return fConfiguration;
1186     }
1187 
1188 
1189     /**
1190      * Returns the absolute base URI of this node or null if the implementation
1191      * wasn't able to obtain an absolute URI. Note: If the URI is malformed, a
1192      * null is returned.
1193      *
1194      * @return The absolute base URI of this node or null.
1195      * @since DOM Level 3
1196      */
1197     public String getBaseURI() {
1198         if (fDocumentURI != null && fDocumentURI.length() != 0 ) {// attribute value is always empty string
1199             try {
1200                 return new URI(fDocumentURI).toString();
1201             }
1202             catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
1203                 // REVISIT: what should happen in this case?
1204                 return null;
1205             }
1206         }
1207         return fDocumentURI;
1208     }
1209 
1210     /**
1211      * DOM Level 3 WD - Experimental.
1212      */
1213     public void setDocumentURI(String documentURI){
1214         fDocumentURI = documentURI;
1215     }
1216 
1217 
1218     //
1219     // DOM L3 LS
1220     //
1221     /**
1222      * DOM Level 3 WD - Experimental.
1223      * Indicates whether the method load should be synchronous or
1224      * asynchronous. When the async attribute is set to <code>true</code>
1225      * the load method returns control to the caller before the document has
1226      * completed loading. The default value of this property is
1227      * <code>false</code>.
1228      * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1229      * if the implementation doesn't support the mode the attribute is being
1230      * set to. Should the DOM spec define the default value of this
1231      * property? What if implementing both async and sync IO is impractical
1232      * in some systems?  2001-09-14. default is <code>false</code> but we
1233      * need to check with Mozilla and IE.
1234      */
1235     public boolean getAsync() {
1236         return false;
1237     }
1238 
1239     /**
1240      * DOM Level 3 WD - Experimental.
1241      * Indicates whether the method load should be synchronous or
1242      * asynchronous. When the async attribute is set to <code>true</code>
1243      * the load method returns control to the caller before the document has
1244      * completed loading. The default value of this property is
1245      * <code>false</code>.
1246      * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1247      * if the implementation doesn't support the mode the attribute is being
1248      * set to. Should the DOM spec define the default value of this
1249      * property? What if implementing both async and sync IO is impractical
1250      * in some systems?  2001-09-14. default is <code>false</code> but we
1251      * need to check with Mozilla and IE.
1252      */
1253     public void setAsync(boolean async) {
1254         if (async) {
1255             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1256             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1257         }
1258     }
1259     /**
1260      * DOM Level 3 WD - Experimental.
1261      * If the document is currently being loaded as a result of the method
1262      * <code>load</code> being invoked the loading and parsing is
1263      * immediately aborted. The possibly partial result of parsing the
1264      * document is discarded and the document is cleared.
1265      */
1266     public void abort() {
1267     }
1268 
1269     /**
1270      * DOM Level 3 WD - Experimental.
1271      *
1272      * Replaces the content of the document with the result of parsing the
1273      * given URI. Invoking this method will either block the caller or
1274      * return to the caller immediately depending on the value of the async
1275      * attribute. Once the document is fully loaded a "load" event (as
1276      * defined in [<a href='http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331'>DOM Level 3 Events</a>]
1277      * , except that the <code>Event.targetNode</code> will be the document,
1278      * not an element) will be dispatched on the document. If an error
1279      * occurs, an implementation dependent "error" event will be dispatched
1280      * on the document. If this method is called on a document that is
1281      * currently loading, the current load is interrupted and the new URI
1282      * load is initiated.
1283      * <br> When invoking this method the parameters used in the
1284      * <code>DOMParser</code> interface are assumed to have their default
1285      * values with the exception that the parameters <code>"entities"</code>
1286      * , <code>"normalize-characters"</code>,
1287      * <code>"check-character-normalization"</code> are set to
1288      * <code>"false"</code>.
1289      * <br> The result of a call to this method is the same the result of a
1290      * call to <code>DOMParser.parseWithContext</code> with an input stream
1291      * referencing the URI that was passed to this call, the document as the
1292      * context node, and the action <code>ACTION_REPLACE_CHILDREN</code>.
1293      * @param uri The URI reference for the XML file to be loaded. If this is
1294      *  a relative URI, the base URI used by the implementation is
1295      *  implementation dependent.
1296      * @return If async is set to <code>true</code> <code>load</code> returns
1297      *   <code>true</code> if the document load was successfully initiated.
1298      *   If an error occurred when initiating the document load,
1299      *   <code>load</code> returns <code>false</code>.If async is set to
1300      *   <code>false</code> <code>load</code> returns <code>true</code> if
1301      *   the document was successfully loaded and parsed. If an error
1302      *   occurred when either loading or parsing the URI, <code>load</code>
1303      *   returns <code>false</code>.
1304      */
1305     public boolean load(String uri) {
1306         return false;
1307     }
1308 
1309     /**
1310      * DOM Level 3 WD - Experimental.
1311      * Replace the content of the document with the result of parsing the
1312      * input string, this method is always synchronous.
1313      * @param source A string containing an XML document.
1314      * @return <code>true</code> if parsing the input string succeeded
1315      *   without errors, otherwise <code>false</code>.
1316      */
1317     public boolean loadXML(String source) {
1318         return false;
1319     }
1320 
1321     /**
1322      * DOM Level 3 WD - Experimental.
1323      * Save the document or the given node and all its descendants to a string
1324      * (i.e. serialize the document or node).
1325      * <br>The parameters used in the <code>LSSerializer</code> interface are
1326      * assumed to have their default values when invoking this method.
1327      * <br> The result of a call to this method is the same the result of a
1328      * call to <code>LSSerializer.writeToString</code> with the document as
1329      * the node to write.
1330      * @param node Specifies what to serialize, if this parameter is
1331      *   <code>null</code> the whole document is serialized, if it's
1332      *   non-null the given node is serialized.
1333      * @return The serialized document or <code>null</code> in case an error
1334      *   occurred.
1335      * @exception DOMException
1336      *   WRONG_DOCUMENT_ERR: Raised if the node passed in as the node
1337      *   parameter is from an other document.
1338      */
1339     public String saveXML(Node node)
1340             throws DOMException {
1341         if (errorChecking && node != null
1342                 && this != node.getOwnerDocument()) {
1343             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
1344             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
1345         }
1346         DOMImplementationLS domImplLS = (DOMImplementationLS) DOMImplementationImpl.getDOMImplementation();
1347         LSSerializer xmlWriter = domImplLS.createLSSerializer();
1348         if (node == null) {
1349             node = this;
1350         }
1351         return xmlWriter.writeToString(node);
1352     }
1353 
1354     /**
1355      * Sets whether the DOM implementation generates mutation events upon
1356      * operations.
1357      */
1358     void setMutationEvents(boolean set) {
1359         // does nothing by default - overidden in subclass
1360     }
1361 
1362     /**
1363      * Returns true if the DOM implementation generates mutation events.
1364      */
1365     boolean getMutationEvents() {
1366         // does nothing by default - overriden in subclass
1367         return false;
1368     }
1369 
1370     // non-DOM factory methods
1371     /**
1372      * NON-DOM Factory method; creates a DocumentType having this Document as
1373      * its OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1374      * information unspecified.)
1375      *
1376      * @param name The name of the Entity we wish to provide a value for.
1377      *
1378      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where DTDs
1379      * are not permitted. (HTML not yet implemented.)
1380      */
1381     public DocumentType createDocumentType(String qualifiedName,
1382             String publicID,
1383             String systemID)
1384             throws DOMException {
1385 
1386         return new DocumentTypeImpl(this, qualifiedName, publicID, systemID);
1387 
1388     } // createDocumentType(String):DocumentType
1389 
1390     /**
1391      * NON-DOM Factory method; creates an Entity having this Document as its
1392      * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1393      * information unspecified.)
1394      *
1395      * @param name The name of the Entity we wish to provide a value for.
1396      *
1397      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1398      * nonstandard entities are not permitted. (HTML not yet implemented.)
1399      */
1400     public Entity createEntity(String name)
1401             throws DOMException {
1402 
1403         if (errorChecking && !isXMLName(name, xml11Version)) {
1404             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1405             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1406         }
1407         return new EntityImpl(this, name);
1408 
1409     } // createEntity(String):Entity
1410 
1411     /**
1412      * NON-DOM Factory method; creates a Notation having this Document as its
1413      * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1414      * information unspecified.)
1415      *
1416      * @param name The name of the Notation we wish to describe
1417      *
1418      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1419      * notations are not permitted. (HTML not yet implemented.)
1420      */
1421     public Notation createNotation(String name)
1422             throws DOMException {
1423 
1424         if (errorChecking && !isXMLName(name, xml11Version)) {
1425             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1426             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1427         }
1428         return new NotationImpl(this, name);
1429 
1430     } // createNotation(String):Notation
1431 
1432     /**
1433      * NON-DOM Factory method: creates an element definition. Element
1434      * definitions hold default attribute values.
1435      */
1436     public ElementDefinitionImpl createElementDefinition(String name)
1437             throws DOMException {
1438 
1439         if (errorChecking && !isXMLName(name, xml11Version)) {
1440             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1441             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1442         }
1443         return new ElementDefinitionImpl(this, name);
1444 
1445     } // createElementDefinition(String):ElementDefinitionImpl
1446 
1447     // other non-DOM methods
1448     /**
1449      * NON-DOM: Get the number associated with this document. Used to order
1450      * documents in the implementation.
1451      */
1452     protected int getNodeNumber() {
1453         if (documentNumber == 0) {
1454 
1455             CoreDOMImplementationImpl cd = (CoreDOMImplementationImpl) CoreDOMImplementationImpl.getDOMImplementation();
1456             documentNumber = cd.assignDocumentNumber();
1457         }
1458         return documentNumber;
1459     }
1460 
1461     /**
1462      * NON-DOM: Get a number associated with a node created with respect to this
1463      * document. Needed for compareDocumentPosition when nodes are disconnected.
1464      * This is only used on demand.
1465      */
1466     protected int getNodeNumber(Node node) {
1467 
1468         // Check if the node is already in the hash
1469         // If so, retrieve the node number
1470         // If not, assign a number to the node
1471         // Node numbers are negative, from -1 to -n
1472         int num;
1473         if (nodeTable == null) {
1474             nodeTable = new HashMap<>();
1475             num = --nodeCounter;
1476             nodeTable.put(node, new Integer(num));
1477         } else {
1478             Integer n = (Integer) nodeTable.get(node);
1479             if (n == null) {
1480                 num = --nodeCounter;
1481                 nodeTable.put(node, new Integer(num));
1482             } else {
1483                 num = n.intValue();
1484             }
1485         }
1486         return num;
1487     }
1488 
1489     /**
1490      * Copies a node from another document to this document. The new nodes are
1491      * created using this document's factory methods and are populated with the
1492      * data from the source's accessor methods defined by the DOM interfaces.
1493      * Its behavior is otherwise similar to that of cloneNode.
1494      * <p>
1495      * According to the DOM specifications, document nodes cannot be imported
1496      * and a NOT_SUPPORTED_ERR exception is thrown if attempted.
1497      */
1498     public Node importNode(Node source, boolean deep)
1499             throws DOMException {
1500         return importNode(source, deep, false, null);
1501     } // importNode(Node,boolean):Node
1502 
1503     /**
1504      * Overloaded implementation of DOM's importNode method. This method
1505      * provides the core functionality for the public importNode and cloneNode
1506      * methods.
1507      *
1508      * The reversedIdentifiers parameter is provided for cloneNode to preserve
1509      * the document's identifiers. The Map has Elements as the keys and
1510      * their identifiers as the values. When an element is being imported, a
1511      * check is done for an associated identifier. If one exists, the identifier
1512      * is registered with the new, imported element. If reversedIdentifiers is
1513      * null, the parameter is not applied.
1514      */
1515     private Node importNode(Node source, boolean deep, boolean cloningDoc,
1516             Map<Node, String> reversedIdentifiers)
1517             throws DOMException {
1518         Node newnode = null;
1519         Map<String, UserDataRecord> userData = null;
1520 
1521         // Sigh. This doesn't work; too many nodes have private data that
1522         // would have to be manually tweaked. May be able to add local
1523         // shortcuts to each nodetype. Consider ?????
1524         // if(source instanceof NodeImpl &&
1525         //  !(source instanceof DocumentImpl))
1526         // {
1527         //  // Can't clone DocumentImpl since it invokes us...
1528         //  newnode=(NodeImpl)source.cloneNode(false);
1529         //  newnode.ownerDocument=this;
1530         // }
1531         // else
1532         if (source instanceof NodeImpl) {
1533             userData = ((NodeImpl) source).getUserDataRecord();
1534         }
1535         int type = source.getNodeType();
1536         switch (type) {
1537             case ELEMENT_NODE: {
1538                 Element newElement;
1539                 boolean domLevel20 = source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0");
1540                 // Create element according to namespace support/qualification.
1541                 if(domLevel20 == false || source.getLocalName() == null)
1542                     newElement = createElement(source.getNodeName());
1543                 else
1544                     newElement = createElementNS(source.getNamespaceURI(),
1545                             source.getNodeName());
1546 
1547                 // Copy element's attributes, if any.
1548                 NamedNodeMap sourceAttrs = source.getAttributes();
1549                 if (sourceAttrs != null) {
1550                     int length = sourceAttrs.getLength();
1551                     for (int index = 0; index < length; index++) {
1552                         Attr attr = (Attr)sourceAttrs.item(index);
1553 
1554                         // NOTE: this methods is used for both importingNode
1555                         // and cloning the document node. In case of the
1556                         // clonning default attributes should be copied.
1557                         // But for importNode defaults should be ignored.
1558                         if (attr.getSpecified() || cloningDoc) {
1559                             Attr newAttr = (Attr)importNode(attr, true, cloningDoc,
1560                                     reversedIdentifiers);
1561 
1562                             // Attach attribute according to namespace
1563                             // support/qualification.
1564                             if (domLevel20 == false ||
1565                             attr.getLocalName() == null)
1566                                 newElement.setAttributeNode(newAttr);
1567                             else
1568                                 newElement.setAttributeNodeNS(newAttr);
1569                             }
1570                         }
1571                     }
1572 
1573                 // Register element identifier.
1574                 if (reversedIdentifiers != null) {
1575                     // Does element have an associated identifier?
1576                     String elementId = reversedIdentifiers.get(source);
1577                     if (elementId != null) {
1578                         if (identifiers == null) {
1579                             identifiers = new HashMap<>();
1580                         }
1581 
1582                         identifiers.put(elementId, newElement);
1583                     }
1584                 }
1585 
1586                 newnode = newElement;
1587                 break;
1588             }
1589 
1590             case ATTRIBUTE_NODE: {
1591 
1592                 if( source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0") ){
1593                     if (source.getLocalName() == null) {
1594                         newnode = createAttribute(source.getNodeName());
1595                     } else {
1596                         newnode = createAttributeNS(source.getNamespaceURI(),
1597                                 source.getNodeName());
1598                     }
1599                 }
1600                 else {
1601                     newnode = createAttribute(source.getNodeName());
1602                 }
1603                 // if source is an AttrImpl from this very same implementation
1604                 // avoid creating the child nodes if possible
1605                 if (source instanceof AttrImpl) {
1606                     AttrImpl attr = (AttrImpl) source;
1607                     if (attr.hasStringValue()) {
1608                         AttrImpl newattr = (AttrImpl) newnode;
1609                         newattr.setValue(attr.getValue());
1610                         deep = false;
1611                     }
1612                     else {
1613                         deep = true;
1614                     }
1615                 }
1616                 else {
1617                     // According to the DOM spec the kids carry the value.
1618                     // However, there are non compliant implementations out
1619                     // there that fail to do so. To avoid ending up with no
1620                     // value at all, in this case we simply copy the text value
1621                     // directly.
1622                     if (source.getFirstChild() == null) {
1623                         newnode.setNodeValue(source.getNodeValue());
1624                         deep = false;
1625                     } else {
1626                         deep = true;
1627                     }
1628                 }
1629                 break;
1630             }
1631 
1632             case TEXT_NODE: {
1633                 newnode = createTextNode(source.getNodeValue());
1634                 break;
1635             }
1636 
1637             case CDATA_SECTION_NODE: {
1638                 newnode = createCDATASection(source.getNodeValue());
1639                 break;
1640             }
1641 
1642             case ENTITY_REFERENCE_NODE: {
1643                 newnode = createEntityReference(source.getNodeName());
1644                 // the subtree is created according to this doc by the method
1645                 // above, so avoid carrying over original subtree
1646                 deep = false;
1647                 break;
1648             }
1649 
1650             case ENTITY_NODE: {
1651                 Entity srcentity = (Entity)source;
1652                 EntityImpl newentity =
1653                 (EntityImpl)createEntity(source.getNodeName());
1654                 newentity.setPublicId(srcentity.getPublicId());
1655                 newentity.setSystemId(srcentity.getSystemId());
1656                 newentity.setNotationName(srcentity.getNotationName());
1657                 // Kids carry additional value,
1658                 // allow deep import temporarily
1659                 newentity.isReadOnly(false);
1660                 newnode = newentity;
1661                 break;
1662             }
1663 
1664             case PROCESSING_INSTRUCTION_NODE: {
1665                 newnode = createProcessingInstruction(source.getNodeName(),
1666                         source.getNodeValue());
1667                 break;
1668             }
1669 
1670             case COMMENT_NODE: {
1671                 newnode = createComment(source.getNodeValue());
1672                 break;
1673             }
1674 
1675             case DOCUMENT_TYPE_NODE: {
1676                 // unless this is used as part of cloning a Document
1677                 // forbid it for the sake of being compliant to the DOM spec
1678                 if (!cloningDoc) {
1679                     String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1680                     throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1681                 }
1682                 DocumentType srcdoctype = (DocumentType)source;
1683                 DocumentTypeImpl newdoctype = (DocumentTypeImpl)
1684                 createDocumentType(srcdoctype.getNodeName(),
1685                         srcdoctype.getPublicId(),
1686                         srcdoctype.getSystemId());
1687                 // Values are on NamedNodeMaps
1688                 NamedNodeMap smap = srcdoctype.getEntities();
1689                 NamedNodeMap tmap = newdoctype.getEntities();
1690                 if(smap != null) {
1691                     for(int i = 0; i < smap.getLength(); i++) {
1692                         tmap.setNamedItem(importNode(smap.item(i), true, true,
1693                                 reversedIdentifiers));
1694                     }
1695                 }
1696                 smap = srcdoctype.getNotations();
1697                 tmap = newdoctype.getNotations();
1698                 if (smap != null) {
1699                     for(int i = 0; i < smap.getLength(); i++) {
1700                         tmap.setNamedItem(importNode(smap.item(i), true, true,
1701                                 reversedIdentifiers));
1702                     }
1703                 }
1704 
1705                 // NOTE: At this time, the DOM definition of DocumentType
1706                 // doesn't cover Elements and their Attributes. domimpl's
1707                 // extentions in that area will not be preserved, even if
1708                 // copying from domimpl to domimpl. We could special-case
1709                 // that here. Arguably we should. Consider. ?????
1710                 newnode = newdoctype;
1711                 break;
1712             }
1713 
1714             case DOCUMENT_FRAGMENT_NODE: {
1715                 newnode = createDocumentFragment();
1716                 // No name, kids carry value
1717                 break;
1718             }
1719 
1720             case NOTATION_NODE: {
1721                 Notation srcnotation = (Notation)source;
1722                 NotationImpl newnotation =
1723                 (NotationImpl)createNotation(source.getNodeName());
1724                 newnotation.setPublicId(srcnotation.getPublicId());
1725                 newnotation.setSystemId(srcnotation.getSystemId());
1726                 // Kids carry additional value
1727                 newnode = newnotation;
1728                 // No name, no value
1729                 break;
1730             }
1731             case DOCUMENT_NODE : // Can't import document nodes
1732             default: {           // Unknown node type
1733                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1734                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1735             }
1736         }
1737 
1738                 if(userData != null)
1739                         callUserDataHandlers(source, newnode, UserDataHandler.NODE_IMPORTED,userData);
1740 
1741         // If deep, replicate and attach the kids.
1742         if (deep) {
1743             for (Node srckid = source.getFirstChild();
1744                     srckid != null;
1745                     srckid = srckid.getNextSibling()) {
1746                 newnode.appendChild(importNode(srckid, true, cloningDoc,
1747                         reversedIdentifiers));
1748             }
1749         }
1750         if (newnode.getNodeType() == Node.ENTITY_NODE) {
1751             ((NodeImpl)newnode).setReadOnly(true, true);
1752         }
1753         return newnode;
1754 
1755     } // importNode(Node,boolean,boolean,Map):Node
1756 
1757     /**
1758      * DOM Level 3 WD - Experimental
1759      * Change the node's ownerDocument, and its subtree, to this Document
1760      *
1761      * @param source The node to adopt.
1762      * @see #importNode
1763      **/
1764     public Node adoptNode(Node source) {
1765         NodeImpl node;
1766         Map<String, UserDataRecord> userData;
1767         try {
1768             node = (NodeImpl) source;
1769         } catch (ClassCastException e) {
1770             // source node comes from a different DOMImplementation
1771             return null;
1772         }
1773 
1774         // Return null if the source is null
1775 
1776         if (source == null ) {
1777             return null;
1778         } else if (source.getOwnerDocument() != null) {
1779 
1780             DOMImplementation thisImpl = this.getImplementation();
1781             DOMImplementation otherImpl = source.getOwnerDocument().getImplementation();
1782 
1783             // when the source node comes from a different implementation.
1784             if (thisImpl != otherImpl) {
1785 
1786                 // Adopting from a DefferedDOM to DOM
1787                 if (thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl &&
1788                         otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl) {
1789                     // traverse the DOM and expand deffered nodes and then allow adoption
1790                     undeferChildren (node);
1791                 } else if ( thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl
1792                         && otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl) {
1793                     // Adopting from a DOM into a DefferedDOM, this should be okay
1794                 } else {
1795                     // Adopting between two dissimilar DOM's is not allowed
1796                     return null;
1797                 }
1798             }
1799         }
1800 
1801         switch (node.getNodeType()) {
1802             case ATTRIBUTE_NODE: {
1803                 AttrImpl attr = (AttrImpl) node;
1804                 // remove node from wherever it is
1805                 if( attr.getOwnerElement() != null){
1806                     //1. owner element attribute is set to null
1807                     attr.getOwnerElement().removeAttributeNode(attr);
1808                 }
1809                 //2. specified flag is set to true
1810                 attr.isSpecified(true);
1811                 userData = node.getUserDataRecord();
1812 
1813                 //3. change ownership
1814                 attr.setOwnerDocument(this);
1815                 if (userData != null) {
1816                     setUserDataTable(node, userData);
1817                 }
1818                 break;
1819             }
1820             //entity, notation nodes are read only nodes.. so they can't be adopted.
1821             //runtime will fall through to NOTATION_NODE
1822             case ENTITY_NODE:
1823             case NOTATION_NODE:{
1824                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
1825                 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
1826 
1827             }
1828             //document, documentype nodes can't be adopted.
1829             //runtime will fall through to DocumentTypeNode
1830             case DOCUMENT_NODE:
1831             case DOCUMENT_TYPE_NODE: {
1832                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1833                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1834             }
1835             case ENTITY_REFERENCE_NODE: {
1836                 userData = node.getUserDataRecord();
1837                 // remove node from wherever it is
1838                 Node parent = node.getParentNode();
1839                 if (parent != null) {
1840                     parent.removeChild(source);
1841                 }
1842                 // discard its replacement value
1843                 Node child;
1844                 while ((child = node.getFirstChild()) != null) {
1845                     node.removeChild(child);
1846                 }
1847                 // change ownership
1848                 node.setOwnerDocument(this);
1849                 if (userData != null) {
1850                     setUserDataTable(node, userData);
1851                 }
1852                 // set its new replacement value if any
1853                 if (docType == null) {
1854                     break;
1855                 }
1856                 NamedNodeMap entities = docType.getEntities();
1857                 Node entityNode = entities.getNamedItem(node.getNodeName());
1858                 if (entityNode == null) {
1859                     break;
1860                 }
1861                 for (child = entityNode.getFirstChild();
1862                         child != null; child = child.getNextSibling()) {
1863                     Node childClone = child.cloneNode(true);
1864                     node.appendChild(childClone);
1865                 }
1866                 break;
1867             }
1868             case ELEMENT_NODE: {
1869                 userData = node.getUserDataRecord();
1870                 // remove node from wherever it is
1871                 Node parent = node.getParentNode();
1872                 if (parent != null) {
1873                     parent.removeChild(source);
1874                 }
1875                 // change ownership
1876                 node.setOwnerDocument(this);
1877                 if (userData != null) {
1878                     setUserDataTable(node, userData);
1879                 }
1880                 // reconcile default attributes
1881                 ((ElementImpl)node).reconcileDefaultAttributes();
1882                 break;
1883             }
1884             default: {
1885                 userData = node.getUserDataRecord();
1886                 // remove node from wherever it is
1887                 Node parent = node.getParentNode();
1888                 if (parent != null) {
1889                     parent.removeChild(source);
1890                 }
1891                 // change ownership
1892                 node.setOwnerDocument(this);
1893                 if (userData != null) {
1894                     setUserDataTable(node, userData);
1895                 }
1896             }
1897         }
1898 
1899                 //DOM L3 Core CR
1900         //http://www.w3.org/TR/2003/CR-DOM-Level-3-Core-20031107/core.html#UserDataHandler-ADOPTED
1901         if (userData != null) {
1902             callUserDataHandlers(source, null, UserDataHandler.NODE_ADOPTED, userData);
1903         }
1904 
1905         return node;
1906     }
1907 
1908     /**
1909      * Traverses the DOM Tree and expands deferred nodes and their
1910      * children.
1911      *
1912      */
1913     protected void undeferChildren(Node node) {
1914 
1915         Node top = node;
1916 
1917         while (null != node) {
1918 
1919             if (((NodeImpl)node).needsSyncData()) {
1920                 ((NodeImpl)node).synchronizeData();
1921             }
1922 
1923             NamedNodeMap attributes = node.getAttributes();
1924             if (attributes != null) {
1925                 int length = attributes.getLength();
1926                 for (int i = 0; i < length; ++i) {
1927                     undeferChildren(attributes.item(i));
1928                 }
1929             }
1930 
1931             Node nextNode = null;
1932             nextNode = node.getFirstChild();
1933 
1934             while (null == nextNode) {
1935 
1936                 if (top.equals(node))
1937                     break;
1938 
1939                 nextNode = node.getNextSibling();
1940 
1941                 if (null == nextNode) {
1942                     node = node.getParentNode();
1943 
1944                     if ((null == node) || (top.equals(node))) {
1945                         nextNode = null;
1946                         break;
1947                     }
1948                 }
1949             }
1950 
1951             node = nextNode;
1952         }
1953     }
1954 
1955     // identifier maintenence
1956     /**
1957      * Introduced in DOM Level 2
1958      * Returns the Element whose ID is given by elementId. If no such element
1959      * exists, returns null. Behavior is not defined if more than one element
1960      * has this ID.
1961      * <p>
1962      * Note: The DOM implementation must have information that says which
1963      * attributes are of type ID. Attributes with the name "ID" are not of type
1964      * ID unless so defined. Implementations that do not know whether
1965      * attributes are of type ID or not are expected to return null.
1966      * @see #getIdentifier
1967      */
1968     public Element getElementById(String elementId) {
1969         return getIdentifier(elementId);
1970     }
1971 
1972     /**
1973      * Remove all identifiers from the ID table
1974      */
1975     protected final void clearIdentifiers(){
1976         if (identifiers != null){
1977             identifiers.clear();
1978         }
1979     }
1980 
1981     /**
1982      * Registers an identifier name with a specified element node.
1983      * If the identifier is already registered, the new element
1984      * node replaces the previous node. If the specified element
1985      * node is null, removeIdentifier() is called.
1986      *
1987      * @see #getIdentifier
1988      * @see #removeIdentifier
1989      */
1990     public void putIdentifier(String idName, Element element) {
1991 
1992         if (element == null) {
1993             removeIdentifier(idName);
1994             return;
1995         }
1996 
1997         if (needsSyncData()) {
1998             synchronizeData();
1999         }
2000 
2001         if (identifiers == null) {
2002             identifiers = new HashMap<>();
2003         }
2004 
2005         identifiers.put(idName, element);
2006 
2007     } // putIdentifier(String,Element)
2008 
2009     /**
2010      * Returns a previously registered element with the specified
2011      * identifier name, or null if no element is registered.
2012      *
2013      * @see #putIdentifier
2014      * @see #removeIdentifier
2015      */
2016     public Element getIdentifier(String idName) {
2017 
2018         if (needsSyncData()) {
2019             synchronizeData();
2020         }
2021 
2022         if (identifiers == null) {
2023             return null;
2024         }
2025         Element elem = (Element) identifiers.get(idName);
2026         if (elem != null) {
2027             // check that the element is in the tree
2028             Node parent = elem.getParentNode();
2029             while (parent != null) {
2030                 if (parent == this) {
2031                     return elem;
2032                 }
2033                 parent = parent.getParentNode();
2034             }
2035         }
2036         return null;
2037     } // getIdentifier(String):Element
2038 
2039     /**
2040      * Removes a previously registered element with the specified
2041      * identifier name.
2042      *
2043      * @see #putIdentifier
2044      * @see #getIdentifier
2045      */
2046     public void removeIdentifier(String idName) {
2047 
2048         if (needsSyncData()) {
2049             synchronizeData();
2050         }
2051 
2052         if (identifiers == null) {
2053             return;
2054         }
2055 
2056         identifiers.remove(idName);
2057 
2058     } // removeIdentifier(String)
2059 
2060     //
2061     // DOM2: Namespace methods
2062     //
2063     /**
2064      * Introduced in DOM Level 2. <p>
2065      * Creates an element of the given qualified name and namespace URI.
2066      * If the given namespaceURI is null or an empty string and the
2067      * qualifiedName has a prefix that is "xml", the created element
2068      * is bound to the predefined namespace
2069      * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2070      * @param namespaceURI The namespace URI of the element to
2071      *                     create.
2072      * @param qualifiedName The qualified name of the element type to
2073      *                      instantiate.
2074      * @return Element A new Element object with the following attributes:
2075      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2076      * name contains an invalid character.
2077      * @throws DOMException NAMESPACE_ERR: Raised if the qualifiedName has a
2078      *                      prefix that is "xml" and the namespaceURI is
2079      *                      neither null nor an empty string nor
2080      *                      "http://www.w3.org/XML/1998/namespace", or
2081      *                      if the qualifiedName has a prefix different
2082      *                      from "xml" and the namespaceURI is null or an
2083      *                      empty string.
2084      * @since WD-DOM-Level-2-19990923
2085      */
2086     public Element createElementNS(String namespaceURI, String qualifiedName)
2087             throws DOMException {
2088         return new ElementNSImpl(this, namespaceURI, qualifiedName);
2089     }
2090 
2091     /**
2092      * NON-DOM: a factory method used by the Xerces DOM parser
2093      * to create an element.
2094      *
2095      * @param namespaceURI The namespace URI of the element to
2096      *                     create.
2097      * @param qualifiedName The qualified name of the element type to
2098      *                      instantiate.
2099      * @param localpart  The local name of the attribute to instantiate.
2100      *
2101      * @return Element A new Element object with the following attributes:
2102      * @exception DOMException INVALID_CHARACTER_ERR: Raised if the specified
2103      *                   name contains an invalid character.
2104      */
2105     public Element createElementNS(String namespaceURI, String qualifiedName,
2106             String localpart)
2107             throws DOMException {
2108         return new ElementNSImpl(this, namespaceURI, qualifiedName, localpart);
2109     }
2110 
2111     /**
2112      * Introduced in DOM Level 2. <p>
2113      * Creates an attribute of the given qualified name and namespace URI.
2114      * If the given namespaceURI is null or an empty string and the
2115      * qualifiedName has a prefix that is "xml", the created element
2116      * is bound to the predefined namespace
2117      * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2118      *
2119      * @param namespaceURI  The namespace URI of the attribute to
2120      *                      create. When it is null or an empty string,
2121      *                      this method behaves like createAttribute.
2122      * @param qualifiedName The qualified name of the attribute to
2123      *                      instantiate.
2124      * @return Attr         A new Attr object.
2125      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2126      * name contains an invalid character.
2127      * @since WD-DOM-Level-2-19990923
2128      */
2129     public Attr createAttributeNS(String namespaceURI, String qualifiedName)
2130             throws DOMException {
2131         return new AttrNSImpl(this, namespaceURI, qualifiedName);
2132     }
2133 
2134     /**
2135      * NON-DOM: a factory method used by the Xerces DOM parser
2136      * to create an element.
2137      *
2138      * @param namespaceURI  The namespace URI of the attribute to
2139      *                      create. When it is null or an empty string,
2140      *                      this method behaves like createAttribute.
2141      * @param qualifiedName The qualified name of the attribute to
2142      *                      instantiate.
2143      * @param localpart     The local name of the attribute to instantiate.
2144      *
2145      * @return Attr         A new Attr object.
2146      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2147      * name contains an invalid character.
2148      */
2149     public Attr createAttributeNS(String namespaceURI, String qualifiedName,
2150             String localpart)
2151             throws DOMException {
2152         return new AttrNSImpl(this, namespaceURI, qualifiedName, localpart);
2153     }
2154 
2155     /**
2156      * Introduced in DOM Level 2. <p>
2157      * Returns a NodeList of all the Elements with a given local name and
2158      * namespace URI in the order in which they would be encountered in a
2159      * preorder traversal of the Document tree.
2160      * @param namespaceURI  The namespace URI of the elements to match
2161      *                      on. The special value "*" matches all
2162      *                      namespaces. When it is null or an empty
2163      *                      string, this method behaves like
2164      *                      getElementsByTagName.
2165      * @param localName     The local name of the elements to match on.
2166      *                      The special value "*" matches all local names.
2167      * @return NodeList     A new NodeList object containing all the matched
2168      *                      Elements.
2169      * @since WD-DOM-Level-2-19990923
2170      */
2171     public NodeList getElementsByTagNameNS(String namespaceURI,
2172             String localName) {
2173         return new DeepNodeListImpl(this, namespaceURI, localName);
2174     }
2175 
2176     //
2177     // Object methods
2178     //
2179 
2180     /** Clone. */
2181     public Object clone() throws CloneNotSupportedException {
2182         CoreDocumentImpl newdoc = (CoreDocumentImpl) super.clone();
2183         newdoc.docType = null;
2184         newdoc.docElement = null;
2185         return newdoc;
2186     }
2187 
2188     //
2189     // Public static methods
2190     //
2191 
2192     /**
2193      * Check the string against XML's definition of acceptable names for
2194      * elements and attributes and so on using the XMLCharacterProperties
2195      * utility class
2196      */
2197 
2198     public static final boolean isXMLName(String s, boolean xml11Version) {
2199 
2200         if (s == null) {
2201             return false;
2202         }
2203         if(!xml11Version)
2204             return XMLChar.isValidName(s);
2205         else
2206             return XML11Char.isXML11ValidName(s);
2207 
2208     } // isXMLName(String):boolean
2209 
2210     /**
2211      * Checks if the given qualified name is legal with respect
2212      * to the version of XML to which this document must conform.
2213      *
2214      * @param prefix prefix of qualified name
2215      * @param local local part of qualified name
2216      */
2217     public static final boolean isValidQName(String prefix, String local, boolean xml11Version) {
2218 
2219         // check that both prefix and local part match NCName
2220         if (local == null) return false;
2221         boolean validNCName = false;
2222 
2223         if (!xml11Version) {
2224             validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2225                     && XMLChar.isValidNCName(local);
2226         }
2227         else {
2228             validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2229                     && XML11Char.isXML11ValidNCName(local);
2230         }
2231 
2232         return validNCName;
2233     }
2234     //
2235     // Protected methods
2236     //
2237 
2238     /**
2239      * Uses the kidOK lookup table to check whether the proposed
2240      * tree structure is legal.
2241      */
2242     protected boolean isKidOK(Node parent, Node child) {
2243         if (allowGrammarAccess &&
2244         parent.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
2245             return child.getNodeType() == Node.ELEMENT_NODE;
2246         }
2247         return 0 != (kidOK[parent.getNodeType()] & 1 << child.getNodeType());
2248     }
2249 
2250     /**
2251      * Denotes that this node has changed.
2252      */
2253     protected void changed() {
2254         changes++;
2255     }
2256 
2257     /**
2258      * Returns the number of changes to this node.
2259      */
2260     protected int changes() {
2261         return changes;
2262     }
2263 
2264     //  NodeListCache pool
2265 
2266     /**
2267      * Returns a NodeListCache for the given node.
2268      */
2269     NodeListCache getNodeListCache(ParentNode owner) {
2270         if (fFreeNLCache == null) {
2271             return new NodeListCache(owner);
2272         }
2273         NodeListCache c = fFreeNLCache;
2274         fFreeNLCache = fFreeNLCache.next;
2275         c.fChild = null;
2276         c.fChildIndex = -1;
2277         c.fLength = -1;
2278         // revoke previous ownership
2279         if (c.fOwner != null) {
2280             c.fOwner.fNodeListCache = null;
2281         }
2282         c.fOwner = owner;
2283         // c.next = null; not necessary, except for confused people...
2284         return c;
2285     }
2286 
2287     /**
2288      * Puts the given NodeListCache in the free list.
2289      * Note: The owner node can keep using it until we reuse it
2290      */
2291     void freeNodeListCache(NodeListCache c) {
2292         c.next = fFreeNLCache;
2293         fFreeNLCache = c;
2294     }
2295 
2296 
2297 
2298     /**
2299      * Associate an object to a key on this node. The object can later be
2300      * retrieved from this node by calling <code>getUserData</code> with the
2301      * same key.
2302      * @param n The node to associate the object to.
2303      * @param key The key to associate the object to.
2304      * @param data The object to associate to the given key, or
2305      *   <code>null</code> to remove any existing association to that key.
2306      * @param handler The handler to associate to that key, or
2307      *   <code>null</code>.
2308      * @return Returns the <code>DOMObject</code> previously associated to
2309      *   the given key on this node, or <code>null</code> if there was none.
2310      * @since DOM Level 3
2311      *
2312      * REVISIT: we could use a free list of UserDataRecord here
2313      */
2314     public Object setUserData(Node n, String key,
2315             Object data, UserDataHandler handler) {
2316         if (data == null) {
2317             if (nodeUserData != null) {
2318                 Map<String, UserDataRecord> t = nodeUserData.get(n);
2319                 if (t != null) {
2320                     UserDataRecord r = t.remove(key);
2321                     if (r != null) {
2322                         return r.fData;
2323                     }
2324                 }
2325             }
2326             return null;
2327         } else {
2328             Map<String, UserDataRecord> t;
2329             if (nodeUserData == null) {
2330                 nodeUserData = new HashMap<>();
2331                 t = new HashMap<>();
2332                 nodeUserData.put(n, t);
2333             } else {
2334                 t = nodeUserData.get(n);
2335                 if (t == null) {
2336                     t = new HashMap<>();
2337                     nodeUserData.put(n, t);
2338                 }
2339             }
2340             UserDataRecord r = t.put(key, new UserDataRecord(data, handler));
2341             if (r != null) {
2342                 return r.fData;
2343             }
2344             return null;
2345         }
2346     }
2347 
2348 
2349     /**
2350      * Retrieves the object associated to a key on a this node. The object
2351      * must first have been set to this node by calling
2352      * <code>setUserData</code> with the same key.
2353      * @param n The node the object is associated to.
2354      * @param key The key the object is associated to.
2355      * @return Returns the <code>DOMObject</code> associated to the given key
2356      *   on this node, or <code>null</code> if there was none.
2357      * @since DOM Level 3
2358      */
2359     public Object getUserData(Node n, String key) {
2360         if (nodeUserData == null) {
2361             return null;
2362         }
2363         Map<String, UserDataRecord> t = nodeUserData.get(n);
2364         if (t == null) {
2365             return null;
2366         }
2367         UserDataRecord r = t.get(key);
2368         if (r != null) {
2369             return r.fData;
2370         }
2371         return null;
2372     }
2373 
2374     protected Map<String, UserDataRecord> getUserDataRecord(Node n) {
2375         if (nodeUserData == null) {
2376             return null;
2377         }
2378         Map<String, UserDataRecord> t = nodeUserData.get(n);
2379         if (t == null) {
2380             return null;
2381         }
2382         return t;
2383     }
2384 
2385     /**
2386      * Remove user data table for the given node.
2387      * @param n The node this operation applies to.
2388      * @return The removed table.
2389      */
2390     Map<String, UserDataRecord> removeUserDataTable(Node n) {
2391         if (nodeUserData == null) {
2392             return null;
2393         }
2394         return nodeUserData.get(n);
2395     }
2396 
2397     /**
2398      * Set user data table for the given node.
2399      * @param n The node this operation applies to.
2400      * @param data The user data table.
2401      */
2402     void setUserDataTable(Node n, Map<String, UserDataRecord> data) {
2403         if (nodeUserData == null) {
2404             nodeUserData = new HashMap<>();
2405         }
2406 
2407         if (data != null) {
2408             nodeUserData.put(n, data);
2409         }
2410     }
2411 
2412     /**
2413      * Call user data handlers when a node is deleted (finalized)
2414      * @param n The node this operation applies to.
2415      * @param c The copy node or null.
2416      * @param operation The operation - import, clone, or delete.
2417      */
2418     void callUserDataHandlers(Node n, Node c, short operation) {
2419         if (nodeUserData == null) {
2420             return;
2421         }
2422 
2423         if (n instanceof NodeImpl) {
2424             Map<String, UserDataRecord> t = ((NodeImpl) n).getUserDataRecord();
2425             if (t == null || t.isEmpty()) {
2426                 return;
2427             }
2428             callUserDataHandlers(n, c, operation, t);
2429         }
2430     }
2431 
2432     /**
2433      * Call user data handlers when a node is deleted (finalized)
2434      * @param n The node this operation applies to.
2435      * @param c The copy node or null.
2436      * @param operation The operation - import, clone, or delete.
2437      * @param handlers Data associated with n.
2438      */
2439     void callUserDataHandlers(Node n, Node c, short operation, Map<String, UserDataRecord> userData) {
2440         if (userData == null || userData.isEmpty()) {
2441             return;
2442         }
2443 
2444         userData.keySet().stream().forEach((key) -> {
2445             UserDataRecord r = userData.get(key);
2446             if (r.fHandler != null) {
2447                 r.fHandler.handle(operation, key, r.fData, n, c);
2448             }
2449         });
2450     }
2451 
2452     /**
2453      * Call user data handlers to let them know the nodes they are related to
2454      * are being deleted. The alternative would be to do that on Node but
2455      * because the nodes are used as the keys we have a reference to them that
2456      * prevents them from being gc'ed until the document is. At the same time,
2457      * doing it here has the advantage of avoiding a finalize() method on Node,
2458      * which would affect all nodes and not just the ones that have a user
2459      * data.
2460      */
2461     // Temporarily comment out this method, because
2462     // 1. It seems that finalizers are not guaranteed to be called, so the
2463     //    functionality is not implemented.
2464     // 2. It affects the performance greatly in multi-thread environment.
2465     // -SG
2466     /*public void finalize() {
2467      if (userData == null) {
2468      return;
2469      }
2470      Enumeration nodes = userData.keys();
2471      while (nodes.hasMoreElements()) {
2472      Object node = nodes.nextElement();
2473      Hashtable t = (Hashtable) userData.get(node);
2474      if (t != null && !t.isEmpty()) {
2475      Enumeration keys = t.keys();
2476      while (keys.hasMoreElements()) {
2477      String key = (String) keys.nextElement();
2478      UserDataRecord r = (UserDataRecord) t.get(key);
2479      if (r.fHandler != null) {
2480      r.fHandler.handle(UserDataHandler.NODE_DELETED,
2481      key, r.fData, null, null);
2482      }
2483      }
2484      }
2485      }
2486      }*/
2487 
2488     protected final void checkNamespaceWF( String qname, int colon1,
2489             int colon2) {
2490 
2491         if (!errorChecking) {
2492             return;
2493         }
2494         // it is an error for NCName to have more than one ':'
2495         // check if it is valid QName [Namespace in XML production 6]
2496         // :camera , nikon:camera:minolta, camera:
2497         if (colon1 == 0 || colon1 == qname.length() - 1 || colon2 != colon1) {
2498             String msg =
2499             DOMMessageFormatter.formatMessage(
2500                             DOMMessageFormatter.DOM_DOMAIN,
2501                             "NAMESPACE_ERR",
2502                             null);
2503             throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2504         }
2505     }
2506     protected final void checkDOMNSErr(String prefix,
2507             String namespace) {
2508         if (errorChecking) {
2509             if (namespace == null) {
2510                 String msg =
2511                 DOMMessageFormatter.formatMessage(
2512                                 DOMMessageFormatter.DOM_DOMAIN,
2513                                 "NAMESPACE_ERR",
2514                                 null);
2515                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2516             }
2517             else if (prefix.equals("xml")
2518                     && !namespace.equals(NamespaceContext.XML_URI)) {
2519                 String msg =
2520                 DOMMessageFormatter.formatMessage(
2521                                 DOMMessageFormatter.DOM_DOMAIN,
2522                                 "NAMESPACE_ERR",
2523                                 null);
2524                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2525             }
2526             else if (
2527             prefix.equals("xmlns")
2528                     && !namespace.equals(NamespaceContext.XMLNS_URI)
2529                     || (!prefix.equals("xmlns")
2530                     && namespace.equals(NamespaceContext.XMLNS_URI))) {
2531                 String msg =
2532                 DOMMessageFormatter.formatMessage(
2533                                 DOMMessageFormatter.DOM_DOMAIN,
2534                                 "NAMESPACE_ERR",
2535                                 null);
2536                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2537             }
2538         }
2539     }
2540 
2541     /**
2542      * Checks if the given qualified name is legal with respect
2543      * to the version of XML to which this document must conform.
2544      *
2545      * @param prefix prefix of qualified name
2546      * @param local local part of qualified name
2547      */
2548     protected final void checkQName(String prefix, String local) {
2549         if (!errorChecking) {
2550             return;
2551         }
2552 
2553         // check that both prefix and local part match NCName
2554         boolean validNCName = false;
2555         if (!xml11Version) {
2556             validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2557                     && XMLChar.isValidNCName(local);
2558         }
2559         else {
2560             validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2561                     && XML11Char.isXML11ValidNCName(local);
2562         }
2563 
2564         if (!validNCName) {
2565             // REVISIT: add qname parameter to the message
2566             String msg =
2567             DOMMessageFormatter.formatMessage(
2568                             DOMMessageFormatter.DOM_DOMAIN,
2569                             "INVALID_CHARACTER_ERR",
2570                             null);
2571             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
2572         }
2573     }
2574 
2575     /**
2576      * We could have more xml versions in future , but for now we could
2577      * do with this to handle XML 1.0 and 1.1
2578      */
2579     boolean isXML11Version(){
2580         return xml11Version;
2581     }
2582 
2583     boolean isNormalizeDocRequired(){
2584         // REVISIT: Implement to optimize when normalization
2585         // is required
2586         return true;
2587     }
2588 
2589     //we should be checking the (elements, attribute, entity etc.) names only when
2590     //version of the document is changed.
2591     boolean isXMLVersionChanged(){
2592         return xmlVersionChanged ;
2593     }
2594     /**
2595      * NON-DOM: kept for backward compatibility
2596      * Store user data related to a given node
2597      * This is a place where we could use weak references! Indeed, the node
2598      * here won't be GC'ed as long as some user data is attached to it, since
2599      * the userData table will have a reference to the node.
2600      */
2601     protected void setUserData(NodeImpl n, Object data) {
2602         setUserData(n, "XERCES1DOMUSERDATA", data, null);
2603     }
2604 
2605     /**
2606      * NON-DOM: kept for backward compatibility
2607      * Retreive user data related to a given node
2608      */
2609     protected Object getUserData(NodeImpl n) {
2610         return getUserData(n, "XERCES1DOMUSERDATA");
2611     }
2612 
2613 
2614     // Event related methods overidden in subclass
2615 
2616     protected void addEventListener(NodeImpl node, String type,
2617             EventListener listener,
2618             boolean useCapture) {
2619         // does nothing by default - overidden in subclass
2620     }
2621 
2622     protected void removeEventListener(NodeImpl node, String type,
2623             EventListener listener,
2624             boolean useCapture) {
2625         // does nothing by default - overidden in subclass
2626     }
2627 
2628     protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
2629         // does nothing by default - overidden in subclass
2630     }
2631 
2632     protected boolean dispatchEvent(NodeImpl node, Event event) {
2633         // does nothing by default - overidden in subclass
2634         return false;
2635     }
2636 
2637     // Notification methods overidden in subclasses
2638 
2639     /**
2640      * A method to be called when some text was changed in a text node,
2641      * so that live objects can be notified.
2642      */
2643     void replacedText(NodeImpl node) {
2644     }
2645 
2646     /**
2647      * A method to be called when some text was deleted from a text node,
2648      * so that live objects can be notified.
2649      */
2650     void deletedText(NodeImpl node, int offset, int count) {
2651     }
2652 
2653     /**
2654      * A method to be called when some text was inserted into a text node,
2655      * so that live objects can be notified.
2656      */
2657     void insertedText(NodeImpl node, int offset, int count) {
2658     }
2659 
2660     /**
2661      * A method to be called when a character data node is about to be modified
2662      */
2663     void modifyingCharacterData(NodeImpl node, boolean replace) {
2664     }
2665 
2666     /**
2667      * A method to be called when a character data node has been modified
2668      */
2669     void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
2670     }
2671 
2672     /**
2673      * A method to be called when a node is about to be inserted in the tree.
2674      */
2675     void insertingNode(NodeImpl node, boolean replace) {
2676     }
2677 
2678     /**
2679      * A method to be called when a node has been inserted in the tree.
2680      */
2681     void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
2682     }
2683 
2684     /**
2685      * A method to be called when a node is about to be removed from the tree.
2686      */
2687     void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
2688     }
2689 
2690     /**
2691      * A method to be called when a node has been removed from the tree.
2692      */
2693     void removedNode(NodeImpl node, boolean replace) {
2694     }
2695 
2696     /**
2697      * A method to be called when a node is about to be replaced in the tree.
2698      */
2699     void replacingNode(NodeImpl node) {
2700     }
2701 
2702     /**
2703      * A method to be called when a node has been replaced in the tree.
2704      */
2705     void replacedNode(NodeImpl node) {
2706     }
2707 
2708     /**
2709      * A method to be called when a character data node is about to be replaced
2710      */
2711     void replacingData(NodeImpl node) {
2712     }
2713 
2714     /**
2715      *  method to be called when a character data node has been replaced.
2716      */
2717     void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
2718     }
2719 
2720 
2721     /**
2722      * A method to be called when an attribute value has been modified
2723      */
2724     void modifiedAttrValue(AttrImpl attr, String oldvalue) {
2725     }
2726 
2727     /**
2728      * A method to be called when an attribute node has been set
2729      */
2730     void setAttrNode(AttrImpl attr, AttrImpl previous) {
2731     }
2732 
2733     /**
2734      * A method to be called when an attribute node has been removed
2735      */
2736     void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
2737     }
2738 
2739     /**
2740      * A method to be called when an attribute node has been renamed
2741      */
2742     void renamedAttrNode(Attr oldAt, Attr newAt) {
2743     }
2744 
2745     /**
2746      * A method to be called when an element has been renamed
2747      */
2748     void renamedElement(Element oldEl, Element newEl) {
2749     }
2750 
2751     /**
2752      * @serialData Serialized fields. Convert Maps to Hashtables for backward
2753      * compatibility.
2754      */
2755     private void writeObject(ObjectOutputStream out) throws IOException {
2756         // Convert Maps to Hashtables
2757         Hashtable<Node, Hashtable<String, UserDataRecord>> nud = null;
2758         if (nodeUserData != null) {
2759             nud = new Hashtable<>();
2760             for (Map.Entry<Node, Map<String, UserDataRecord>> e : nodeUserData.entrySet()) {
2761                 //e.getValue() will not be null since an entry is always put with a non-null value
2762                 nud.put(e.getKey(), new Hashtable<>(e.getValue()));
2763             }
2764         }
2765 
2766         Hashtable<String, Node> ids = (identifiers == null)? null : new Hashtable<>(identifiers);
2767         Hashtable<Node, Integer> nt = (nodeTable == null)? null : new Hashtable<>(nodeTable);
2768 
2769         // Write serialized fields
2770         ObjectOutputStream.PutField pf = out.putFields();
2771         pf.put("docType", docType);
2772         pf.put("docElement", docElement);
2773         pf.put("fFreeNLCache", fFreeNLCache);
2774         pf.put("encoding", encoding);
2775         pf.put("actualEncoding", actualEncoding);
2776         pf.put("version", version);
2777         pf.put("standalone", standalone);
2778         pf.put("fDocumentURI", fDocumentURI);
2779 
2780         //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2781         pf.put("userData", nud);
2782         pf.put("identifiers", ids);
2783         pf.put("changes", changes);
2784         pf.put("allowGrammarAccess", allowGrammarAccess);
2785         pf.put("errorChecking", errorChecking);
2786         pf.put("ancestorChecking", ancestorChecking);
2787         pf.put("xmlVersionChanged", xmlVersionChanged);
2788         pf.put("documentNumber", documentNumber);
2789         pf.put("nodeCounter", nodeCounter);
2790         pf.put("nodeTable", nt);
2791         pf.put("xml11Version", xml11Version);
2792         out.writeFields();
2793     }
2794 
2795     @SuppressWarnings("unchecked")
2796     private void readObject(ObjectInputStream in)
2797                         throws IOException, ClassNotFoundException {
2798         // We have to read serialized fields first.
2799         ObjectInputStream.GetField gf = in.readFields();
2800         docType = (DocumentTypeImpl)gf.get("docType", null);
2801         docElement = (ElementImpl)gf.get("docElement", null);
2802         fFreeNLCache = (NodeListCache)gf.get("fFreeNLCache", null);
2803         encoding = (String)gf.get("encoding", null);
2804         actualEncoding = (String)gf.get("actualEncoding", null);
2805         version = (String)gf.get("version", null);
2806         standalone = gf.get("standalone", false);
2807         fDocumentURI = (String)gf.get("fDocumentURI", null);
2808 
2809         //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2810         Hashtable<Node, Hashtable<String, UserDataRecord>> nud =
2811                 (Hashtable<Node, Hashtable<String, UserDataRecord>>)gf.get("userData", null);
2812 
2813         Hashtable<String, Node> ids = (Hashtable<String, Node>)gf.get("identifiers", null);
2814 
2815         changes = gf.get("changes", 0);
2816         allowGrammarAccess = gf.get("allowGrammarAccess", false);
2817         errorChecking = gf.get("errorChecking", true);
2818         ancestorChecking = gf.get("ancestorChecking", true);
2819         xmlVersionChanged = gf.get("xmlVersionChanged", false);
2820         documentNumber = gf.get("documentNumber", 0);
2821         nodeCounter = gf.get("nodeCounter", 0);
2822 
2823         Hashtable<Node, Integer> nt = (Hashtable<Node, Integer>)gf.get("nodeTable", null);
2824 
2825         xml11Version = gf.get("xml11Version", false);
2826 
2827         //convert Hashtables back to HashMaps
2828         if (nud != null) {
2829             nodeUserData = new HashMap<>();
2830             for (Map.Entry<Node, Hashtable<String, UserDataRecord>> e : nud.entrySet()) {
2831                 nodeUserData.put(e.getKey(), new HashMap<>(e.getValue()));
2832             }
2833         }
2834 
2835         if (ids != null) identifiers = new HashMap<>(ids);
2836         if (nt != null) nodeTable = new HashMap<>(nt);
2837     }
2838 } // class CoreDocumentImpl