1 /*
   2  * Copyright (c) 2009, 2015, 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     public void setEncoding(String value) {
 836         setXmlEncoding(value);
 837     }
 838 
 839     /**
 840      * DOM Level 3 WD - Experimental.
 841      * The encoding of this document (part of XML Declaration)
 842      */
 843     public String getXmlEncoding() {
 844         return encoding;
 845     }
 846 
 847     /**
 848      * @deprecated This method is internal and only exists for
 849      * compatibility with older applications. New applications
 850      * should never call this method.
 851      */
 852     public String getEncoding() {
 853         return getXmlEncoding();
 854     }
 855 
 856     /**
 857      * DOM Level 3 CR - Experimental.
 858      * version - An attribute specifying, as part of the XML declaration,
 859      * the version number of this document.
 860      */
 861     public void setXmlVersion(String value) {
 862         if(value.equals("1.0") || value.equals("1.1")){
 863             //we need to change the flag value only --
 864             // when the version set is different than already set.
 865             if(!getXmlVersion().equals(value)){
 866                 xmlVersionChanged = true ;
 867                 //change the normalization value back to false
 868                 isNormalized(false);
 869                 version = value;
 870             }
 871         }
 872         else{
 873             //NOT_SUPPORTED_ERR: Raised if the vesion is set to a value that is not supported by
 874             //this document
 875             //we dont support any other XML version
 876             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
 877             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
 878 
 879         }
 880         if((getXmlVersion()).equals("1.1")){
 881             xml11Version = true;
 882         }
 883         else{
 884             xml11Version = false;
 885         }
 886     }
 887 
 888     /**
 889      * @deprecated This method is internal and only exists for
 890      * compatibility with older applications. New applications
 891      * should never call this method.
 892      */
 893     public void setVersion(String value) {
 894         setXmlVersion(value);
 895     }
 896 
 897     /**
 898      * DOM Level 3 WD - Experimental.
 899      * The version of this document (part of XML Declaration)
 900      */
 901 
 902     public String getXmlVersion() {
 903         return (version == null)?"1.0":version;
 904     }
 905 
 906     /**
 907      * @deprecated This method is internal and only exists for
 908      * compatibility with older applications. New applications
 909      * should never call this method.
 910      */
 911     public String getVersion() {
 912         return getXmlVersion();
 913     }
 914 
 915     /**
 916      * DOM Level 3 CR - Experimental.
 917      *
 918      * Xmlstandalone - An attribute specifying, as part of the XML declaration,
 919      * whether this document is standalone
 920      * @exception DOMException
 921      *    NOT_SUPPORTED_ERR: Raised if this document does not support the
 922      *   "XML" feature.
 923      * @since DOM Level 3
 924      */
 925     public void setXmlStandalone(boolean value)
 926             throws DOMException {
 927         standalone = value;
 928     }
 929 
 930     /**
 931      * @deprecated This method is internal and only exists for
 932      * compatibility with older applications. New applications
 933      * should never call this method.
 934      */
 935     public void setStandalone(boolean value) {
 936         setXmlStandalone(value);
 937     }
 938 
 939     /**
 940      * DOM Level 3 WD - Experimental.
 941      * standalone that specifies whether this document is standalone
 942      * (part of XML Declaration)
 943      */
 944     public boolean getXmlStandalone() {
 945         return standalone;
 946     }
 947 
 948     /**
 949      * @deprecated This method is internal and only exists for
 950      * compatibility with older applications. New applications
 951      * should never call this method.
 952      */
 953     public boolean getStandalone() {
 954         return getXmlStandalone();
 955     }
 956 
 957     /**
 958      * DOM Level 3 WD - Experimental.
 959      * The location of the document or <code>null</code> if undefined.
 960      * <br>Beware that when the <code>Document</code> supports the feature
 961      * "HTML" , the href attribute of the HTML BASE element takes precedence
 962      * over this attribute.
 963      * @since DOM Level 3
 964      */
 965     public String getDocumentURI(){
 966         return fDocumentURI;
 967     }
 968 
 969 
 970     /**
 971      * DOM Level 3 WD - Experimental.
 972      * Renaming node
 973      */
 974     public Node renameNode(Node n,String namespaceURI,String name)
 975     throws DOMException{
 976 
 977         if (errorChecking && n.getOwnerDocument() != this && n != this) {
 978             String msg = DOMMessageFormatter.formatMessage(
 979                     DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
 980             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
 981         }
 982         switch (n.getNodeType()) {
 983             case ELEMENT_NODE: {
 984                 ElementImpl el = (ElementImpl) n;
 985                 if (el instanceof ElementNSImpl) {
 986                     ((ElementNSImpl) el).rename(namespaceURI, name);
 987 
 988                     // fire user data NODE_RENAMED event
 989                     callUserDataHandlers(el, null, UserDataHandler.NODE_RENAMED);
 990                 }
 991                 else {
 992                     if (namespaceURI == null) {
 993                         if (errorChecking) {
 994                             int colon1 = name.indexOf(':');
 995                             if(colon1 != -1){
 996                                 String msg =
 997                                     DOMMessageFormatter.formatMessage(
 998                                                 DOMMessageFormatter.DOM_DOMAIN,
 999                                                 "NAMESPACE_ERR",
1000                                                 null);
1001                                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
1002                             }
1003                             if (!isXMLName(name,xml11Version)) {
1004                                 String msg = DOMMessageFormatter.formatMessage(
1005                                         DOMMessageFormatter.DOM_DOMAIN,
1006                                         "INVALID_CHARACTER_ERR", null);
1007                                 throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
1008                                         msg);
1009                             }
1010                         }
1011                         el.rename(name);
1012 
1013                         // fire user data NODE_RENAMED event
1014                         callUserDataHandlers(el, null,
1015                                 UserDataHandler.NODE_RENAMED);
1016                     }
1017                     else {
1018                         // we need to create a new object
1019                         ElementNSImpl nel =
1020                             new ElementNSImpl(this, namespaceURI, name);
1021 
1022                         // register event listeners on new node
1023                         copyEventListeners(el, nel);
1024 
1025                         // remove user data from old node
1026                         Map<String, UserDataRecord> data = removeUserDataTable(el);
1027 
1028                         // remove old node from parent if any
1029                         Node parent = el.getParentNode();
1030                         Node nextSib = el.getNextSibling();
1031                         if (parent != null) {
1032                             parent.removeChild(el);
1033                         }
1034                         // move children to new node
1035                         Node child = el.getFirstChild();
1036                         while (child != null) {
1037                             el.removeChild(child);
1038                             nel.appendChild(child);
1039                             child = el.getFirstChild();
1040                         }
1041                         // move specified attributes to new node
1042                         nel.moveSpecifiedAttributes(el);
1043 
1044                         // attach user data to new node
1045                         setUserDataTable(nel, data);
1046 
1047                         // and fire user data NODE_RENAMED event
1048                         callUserDataHandlers(el, nel,
1049                                 UserDataHandler.NODE_RENAMED);
1050 
1051                         // insert new node where old one was
1052                         if (parent != null) {
1053                             parent.insertBefore(nel, nextSib);
1054                         }
1055                         el = nel;
1056                     }
1057                 }
1058                 // fire ElementNameChanged event
1059                 renamedElement((Element) n, el);
1060                 return el;
1061             }
1062             case ATTRIBUTE_NODE: {
1063                 AttrImpl at = (AttrImpl) n;
1064 
1065                 // dettach attr from element
1066                 Element el = at.getOwnerElement();
1067                 if (el != null) {
1068                     el.removeAttributeNode(at);
1069                 }
1070                 if (n instanceof AttrNSImpl) {
1071                     ((AttrNSImpl) at).rename(namespaceURI, name);
1072                     // reattach attr to element
1073                     if (el != null) {
1074                         el.setAttributeNodeNS(at);
1075                     }
1076 
1077                     // fire user data NODE_RENAMED event
1078                     callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1079                 }
1080                 else {
1081                     if (namespaceURI == null) {
1082                         at.rename(name);
1083                         // reattach attr to element
1084                         if (el != null) {
1085                             el.setAttributeNode(at);
1086                         }
1087 
1088                         // fire user data NODE_RENAMED event
1089                         callUserDataHandlers(at, null, UserDataHandler.NODE_RENAMED);
1090                     }
1091                     else {
1092                         // we need to create a new object
1093                         AttrNSImpl nat = new AttrNSImpl(this, namespaceURI, name);
1094 
1095                         // register event listeners on new node
1096                         copyEventListeners(at, nat);
1097 
1098                         // remove user data from old node
1099                         Map<String, UserDataRecord> data = removeUserDataTable(at);
1100 
1101                         // move children to new node
1102                         Node child = at.getFirstChild();
1103                         while (child != null) {
1104                             at.removeChild(child);
1105                             nat.appendChild(child);
1106                             child = at.getFirstChild();
1107                         }
1108 
1109                         // attach user data to new node
1110                         setUserDataTable(nat, data);
1111 
1112                         // and fire user data NODE_RENAMED event
1113                         callUserDataHandlers(at, nat, UserDataHandler.NODE_RENAMED);
1114 
1115                         // reattach attr to element
1116                         if (el != null) {
1117                             el.setAttributeNode(nat);
1118                         }
1119                         at = nat;
1120                     }
1121                 }
1122                 // fire AttributeNameChanged event
1123                 renamedAttrNode((Attr) n, at);
1124 
1125                 return at;
1126             }
1127             default: {
1128                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1129                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1130             }
1131         }
1132 
1133     }
1134 
1135 
1136     /**
1137      *  DOM Level 3 WD - Experimental
1138      *  Normalize document.
1139      */
1140     public void normalizeDocument(){
1141         // No need to normalize if already normalized.
1142         if (isNormalized() && !isNormalizeDocRequired()) {
1143             return;
1144         }
1145         if (needsSyncChildren()) {
1146             synchronizeChildren();
1147         }
1148 
1149         if (domNormalizer == null) {
1150             domNormalizer = new DOMNormalizer();
1151         }
1152 
1153         if (fConfiguration == null) {
1154             fConfiguration =  new DOMConfigurationImpl();
1155         }
1156         else {
1157             fConfiguration.reset();
1158         }
1159 
1160         domNormalizer.normalizeDocument(this, fConfiguration);
1161         isNormalized(true);
1162         //set the XMLversion changed value to false -- once we have finished
1163         //doing normalization
1164         xmlVersionChanged = false ;
1165     }
1166 
1167 
1168     /**
1169      * DOM Level 3 CR - Experimental
1170      *
1171      *  The configuration used when <code>Document.normalizeDocument</code> is
1172      * invoked.
1173      * @since DOM Level 3
1174      */
1175     public DOMConfiguration getDomConfig(){
1176         if (fConfiguration == null) {
1177             fConfiguration = new DOMConfigurationImpl();
1178         }
1179         return fConfiguration;
1180     }
1181 
1182 
1183     /**
1184      * Returns the absolute base URI of this node or null if the implementation
1185      * wasn't able to obtain an absolute URI. Note: If the URI is malformed, a
1186      * null is returned.
1187      *
1188      * @return The absolute base URI of this node or null.
1189      * @since DOM Level 3
1190      */
1191     public String getBaseURI() {
1192         if (fDocumentURI != null && fDocumentURI.length() != 0 ) {// attribute value is always empty string
1193             try {
1194                 return new URI(fDocumentURI).toString();
1195             }
1196             catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
1197                 // REVISIT: what should happen in this case?
1198                 return null;
1199             }
1200         }
1201         return fDocumentURI;
1202     }
1203 
1204     /**
1205      * DOM Level 3 WD - Experimental.
1206      */
1207     public void setDocumentURI(String documentURI){
1208         fDocumentURI = documentURI;
1209     }
1210 
1211 
1212     //
1213     // DOM L3 LS
1214     //
1215     /**
1216      * DOM Level 3 WD - Experimental.
1217      * Indicates whether the method load should be synchronous or
1218      * asynchronous. When the async attribute is set to <code>true</code>
1219      * the load method returns control to the caller before the document has
1220      * completed loading. The default value of this property is
1221      * <code>false</code>.
1222      * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1223      * if the implementation doesn't support the mode the attribute is being
1224      * set to. Should the DOM spec define the default value of this
1225      * property? What if implementing both async and sync IO is impractical
1226      * in some systems?  2001-09-14. default is <code>false</code> but we
1227      * need to check with Mozilla and IE.
1228      */
1229     public boolean getAsync() {
1230         return false;
1231     }
1232 
1233     /**
1234      * DOM Level 3 WD - Experimental.
1235      * Indicates whether the method load should be synchronous or
1236      * asynchronous. When the async attribute is set to <code>true</code>
1237      * the load method returns control to the caller before the document has
1238      * completed loading. The default value of this property is
1239      * <code>false</code>.
1240      * <br>Setting the value of this attribute might throw NOT_SUPPORTED_ERR
1241      * if the implementation doesn't support the mode the attribute is being
1242      * set to. Should the DOM spec define the default value of this
1243      * property? What if implementing both async and sync IO is impractical
1244      * in some systems?  2001-09-14. default is <code>false</code> but we
1245      * need to check with Mozilla and IE.
1246      */
1247     public void setAsync(boolean async) {
1248         if (async) {
1249             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1250             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1251         }
1252     }
1253     /**
1254      * DOM Level 3 WD - Experimental.
1255      * If the document is currently being loaded as a result of the method
1256      * <code>load</code> being invoked the loading and parsing is
1257      * immediately aborted. The possibly partial result of parsing the
1258      * document is discarded and the document is cleared.
1259      */
1260     public void abort() {
1261     }
1262 
1263     /**
1264      * DOM Level 3 WD - Experimental.
1265      *
1266      * Replaces the content of the document with the result of parsing the
1267      * given URI. Invoking this method will either block the caller or
1268      * return to the caller immediately depending on the value of the async
1269      * attribute. Once the document is fully loaded a "load" event (as
1270      * defined in [<a href='http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331'>DOM Level 3 Events</a>]
1271      * , except that the <code>Event.targetNode</code> will be the document,
1272      * not an element) will be dispatched on the document. If an error
1273      * occurs, an implementation dependent "error" event will be dispatched
1274      * on the document. If this method is called on a document that is
1275      * currently loading, the current load is interrupted and the new URI
1276      * load is initiated.
1277      * <br> When invoking this method the parameters used in the
1278      * <code>DOMParser</code> interface are assumed to have their default
1279      * values with the exception that the parameters <code>"entities"</code>
1280      * , <code>"normalize-characters"</code>,
1281      * <code>"check-character-normalization"</code> are set to
1282      * <code>"false"</code>.
1283      * <br> The result of a call to this method is the same the result of a
1284      * call to <code>DOMParser.parseWithContext</code> with an input stream
1285      * referencing the URI that was passed to this call, the document as the
1286      * context node, and the action <code>ACTION_REPLACE_CHILDREN</code>.
1287      * @param uri The URI reference for the XML file to be loaded. If this is
1288      *  a relative URI, the base URI used by the implementation is
1289      *  implementation dependent.
1290      * @return If async is set to <code>true</code> <code>load</code> returns
1291      *   <code>true</code> if the document load was successfully initiated.
1292      *   If an error occurred when initiating the document load,
1293      *   <code>load</code> returns <code>false</code>.If async is set to
1294      *   <code>false</code> <code>load</code> returns <code>true</code> if
1295      *   the document was successfully loaded and parsed. If an error
1296      *   occurred when either loading or parsing the URI, <code>load</code>
1297      *   returns <code>false</code>.
1298      */
1299     public boolean load(String uri) {
1300         return false;
1301     }
1302 
1303     /**
1304      * DOM Level 3 WD - Experimental.
1305      * Replace the content of the document with the result of parsing the
1306      * input string, this method is always synchronous.
1307      * @param source A string containing an XML document.
1308      * @return <code>true</code> if parsing the input string succeeded
1309      *   without errors, otherwise <code>false</code>.
1310      */
1311     public boolean loadXML(String source) {
1312         return false;
1313     }
1314 
1315     /**
1316      * DOM Level 3 WD - Experimental.
1317      * Save the document or the given node and all its descendants to a string
1318      * (i.e. serialize the document or node).
1319      * <br>The parameters used in the <code>LSSerializer</code> interface are
1320      * assumed to have their default values when invoking this method.
1321      * <br> The result of a call to this method is the same the result of a
1322      * call to <code>LSSerializer.writeToString</code> with the document as
1323      * the node to write.
1324      * @param node Specifies what to serialize, if this parameter is
1325      *   <code>null</code> the whole document is serialized, if it's
1326      *   non-null the given node is serialized.
1327      * @return The serialized document or <code>null</code> in case an error
1328      *   occurred.
1329      * @exception DOMException
1330      *   WRONG_DOCUMENT_ERR: Raised if the node passed in as the node
1331      *   parameter is from an other document.
1332      */
1333     public String saveXML(Node node)
1334             throws DOMException {
1335         if (errorChecking && node != null
1336                 && this != node.getOwnerDocument()) {
1337             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
1338             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
1339         }
1340         DOMImplementationLS domImplLS = (DOMImplementationLS) DOMImplementationImpl.getDOMImplementation();
1341         LSSerializer xmlWriter = domImplLS.createLSSerializer();
1342         if (node == null) {
1343             node = this;
1344         }
1345         return xmlWriter.writeToString(node);
1346     }
1347 
1348     /**
1349      * Sets whether the DOM implementation generates mutation events upon
1350      * operations.
1351      */
1352     void setMutationEvents(boolean set) {
1353         // does nothing by default - overidden in subclass
1354     }
1355 
1356     /**
1357      * Returns true if the DOM implementation generates mutation events.
1358      */
1359     boolean getMutationEvents() {
1360         // does nothing by default - overriden in subclass
1361         return false;
1362     }
1363 
1364     // non-DOM factory methods
1365     /**
1366      * NON-DOM Factory method; creates a DocumentType having this Document as
1367      * its OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1368      * information unspecified.)
1369      *
1370      * @param name The name of the Entity we wish to provide a value for.
1371      *
1372      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where DTDs
1373      * are not permitted. (HTML not yet implemented.)
1374      */
1375     public DocumentType createDocumentType(String qualifiedName,
1376             String publicID,
1377             String systemID)
1378             throws DOMException {
1379 
1380         return new DocumentTypeImpl(this, qualifiedName, publicID, systemID);
1381 
1382     } // createDocumentType(String):DocumentType
1383 
1384     /**
1385      * NON-DOM Factory method; creates an Entity having this Document as its
1386      * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1387      * information unspecified.)
1388      *
1389      * @param name The name of the Entity we wish to provide a value for.
1390      *
1391      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1392      * nonstandard entities are not permitted. (HTML not yet implemented.)
1393      */
1394     public Entity createEntity(String name)
1395             throws DOMException {
1396 
1397         if (errorChecking && !isXMLName(name, xml11Version)) {
1398             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1399             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1400         }
1401         return new EntityImpl(this, name);
1402 
1403     } // createEntity(String):Entity
1404 
1405     /**
1406      * NON-DOM Factory method; creates a Notation having this Document as its
1407      * OwnerDoc. (REC-DOM-Level-1-19981001 left the process of building DTD
1408      * information unspecified.)
1409      *
1410      * @param name The name of the Notation we wish to describe
1411      *
1412      * @throws DOMException(NOT_SUPPORTED_ERR) for HTML documents, where
1413      * notations are not permitted. (HTML not yet implemented.)
1414      */
1415     public Notation createNotation(String name)
1416             throws DOMException {
1417 
1418         if (errorChecking && !isXMLName(name, xml11Version)) {
1419             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1420             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1421         }
1422         return new NotationImpl(this, name);
1423 
1424     } // createNotation(String):Notation
1425 
1426     /**
1427      * NON-DOM Factory method: creates an element definition. Element
1428      * definitions hold default attribute values.
1429      */
1430     public ElementDefinitionImpl createElementDefinition(String name)
1431             throws DOMException {
1432 
1433         if (errorChecking && !isXMLName(name, xml11Version)) {
1434             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_CHARACTER_ERR", null);
1435             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
1436         }
1437         return new ElementDefinitionImpl(this, name);
1438 
1439     } // createElementDefinition(String):ElementDefinitionImpl
1440 
1441     // other non-DOM methods
1442     /**
1443      * NON-DOM: Get the number associated with this document. Used to order
1444      * documents in the implementation.
1445      */
1446     protected int getNodeNumber() {
1447         if (documentNumber == 0) {
1448 
1449             CoreDOMImplementationImpl cd = (CoreDOMImplementationImpl) CoreDOMImplementationImpl.getDOMImplementation();
1450             documentNumber = cd.assignDocumentNumber();
1451         }
1452         return documentNumber;
1453     }
1454 
1455     /**
1456      * NON-DOM: Get a number associated with a node created with respect to this
1457      * document. Needed for compareDocumentPosition when nodes are disconnected.
1458      * This is only used on demand.
1459      */
1460     protected int getNodeNumber(Node node) {
1461 
1462         // Check if the node is already in the hash
1463         // If so, retrieve the node number
1464         // If not, assign a number to the node
1465         // Node numbers are negative, from -1 to -n
1466         int num;
1467         if (nodeTable == null) {
1468             nodeTable = new HashMap<>();
1469             num = --nodeCounter;
1470             nodeTable.put(node, new Integer(num));
1471         } else {
1472             Integer n = (Integer) nodeTable.get(node);
1473             if (n == null) {
1474                 num = --nodeCounter;
1475                 nodeTable.put(node, new Integer(num));
1476             } else {
1477                 num = n.intValue();
1478             }
1479         }
1480         return num;
1481     }
1482 
1483     /**
1484      * Copies a node from another document to this document. The new nodes are
1485      * created using this document's factory methods and are populated with the
1486      * data from the source's accessor methods defined by the DOM interfaces.
1487      * Its behavior is otherwise similar to that of cloneNode.
1488      * <p>
1489      * According to the DOM specifications, document nodes cannot be imported
1490      * and a NOT_SUPPORTED_ERR exception is thrown if attempted.
1491      */
1492     public Node importNode(Node source, boolean deep)
1493             throws DOMException {
1494         return importNode(source, deep, false, null);
1495     } // importNode(Node,boolean):Node
1496 
1497     /**
1498      * Overloaded implementation of DOM's importNode method. This method
1499      * provides the core functionality for the public importNode and cloneNode
1500      * methods.
1501      *
1502      * The reversedIdentifiers parameter is provided for cloneNode to preserve
1503      * the document's identifiers. The Map has Elements as the keys and
1504      * their identifiers as the values. When an element is being imported, a
1505      * check is done for an associated identifier. If one exists, the identifier
1506      * is registered with the new, imported element. If reversedIdentifiers is
1507      * null, the parameter is not applied.
1508      */
1509     private Node importNode(Node source, boolean deep, boolean cloningDoc,
1510             Map<Node, String> reversedIdentifiers)
1511             throws DOMException {
1512         Node newnode = null;
1513         Map<String, UserDataRecord> userData = null;
1514 
1515         // Sigh. This doesn't work; too many nodes have private data that
1516         // would have to be manually tweaked. May be able to add local
1517         // shortcuts to each nodetype. Consider ?????
1518         // if(source instanceof NodeImpl &&
1519         //  !(source instanceof DocumentImpl))
1520         // {
1521         //  // Can't clone DocumentImpl since it invokes us...
1522         //  newnode=(NodeImpl)source.cloneNode(false);
1523         //  newnode.ownerDocument=this;
1524         // }
1525         // else
1526         if (source instanceof NodeImpl) {
1527             userData = ((NodeImpl) source).getUserDataRecord();
1528         }
1529         int type = source.getNodeType();
1530         switch (type) {
1531             case ELEMENT_NODE: {
1532                 Element newElement;
1533                 boolean domLevel20 = source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0");
1534                 // Create element according to namespace support/qualification.
1535                 if(domLevel20 == false || source.getLocalName() == null)
1536                     newElement = createElement(source.getNodeName());
1537                 else
1538                     newElement = createElementNS(source.getNamespaceURI(),
1539                             source.getNodeName());
1540 
1541                 // Copy element's attributes, if any.
1542                 NamedNodeMap sourceAttrs = source.getAttributes();
1543                 if (sourceAttrs != null) {
1544                     int length = sourceAttrs.getLength();
1545                     for (int index = 0; index < length; index++) {
1546                         Attr attr = (Attr)sourceAttrs.item(index);
1547 
1548                         // NOTE: this methods is used for both importingNode
1549                         // and cloning the document node. In case of the
1550                         // clonning default attributes should be copied.
1551                         // But for importNode defaults should be ignored.
1552                         if (attr.getSpecified() || cloningDoc) {
1553                             Attr newAttr = (Attr)importNode(attr, true, cloningDoc,
1554                                     reversedIdentifiers);
1555 
1556                             // Attach attribute according to namespace
1557                             // support/qualification.
1558                             if (domLevel20 == false ||
1559                             attr.getLocalName() == null)
1560                                 newElement.setAttributeNode(newAttr);
1561                             else
1562                                 newElement.setAttributeNodeNS(newAttr);
1563                             }
1564                         }
1565                     }
1566 
1567                 // Register element identifier.
1568                 if (reversedIdentifiers != null) {
1569                     // Does element have an associated identifier?
1570                     String elementId = reversedIdentifiers.get(source);
1571                     if (elementId != null) {
1572                         if (identifiers == null) {
1573                             identifiers = new HashMap<>();
1574                         }
1575 
1576                         identifiers.put(elementId, newElement);
1577                     }
1578                 }
1579 
1580                 newnode = newElement;
1581                 break;
1582             }
1583 
1584             case ATTRIBUTE_NODE: {
1585 
1586                 if( source.getOwnerDocument().getImplementation().hasFeature("XML", "2.0") ){
1587                     if (source.getLocalName() == null) {
1588                         newnode = createAttribute(source.getNodeName());
1589                     } else {
1590                         newnode = createAttributeNS(source.getNamespaceURI(),
1591                                 source.getNodeName());
1592                     }
1593                 }
1594                 else {
1595                     newnode = createAttribute(source.getNodeName());
1596                 }
1597                 // if source is an AttrImpl from this very same implementation
1598                 // avoid creating the child nodes if possible
1599                 if (source instanceof AttrImpl) {
1600                     AttrImpl attr = (AttrImpl) source;
1601                     if (attr.hasStringValue()) {
1602                         AttrImpl newattr = (AttrImpl) newnode;
1603                         newattr.setValue(attr.getValue());
1604                         deep = false;
1605                     }
1606                     else {
1607                         deep = true;
1608                     }
1609                 }
1610                 else {
1611                     // According to the DOM spec the kids carry the value.
1612                     // However, there are non compliant implementations out
1613                     // there that fail to do so. To avoid ending up with no
1614                     // value at all, in this case we simply copy the text value
1615                     // directly.
1616                     if (source.getFirstChild() == null) {
1617                         newnode.setNodeValue(source.getNodeValue());
1618                         deep = false;
1619                     } else {
1620                         deep = true;
1621                     }
1622                 }
1623                 break;
1624             }
1625 
1626             case TEXT_NODE: {
1627                 newnode = createTextNode(source.getNodeValue());
1628                 break;
1629             }
1630 
1631             case CDATA_SECTION_NODE: {
1632                 newnode = createCDATASection(source.getNodeValue());
1633                 break;
1634             }
1635 
1636             case ENTITY_REFERENCE_NODE: {
1637                 newnode = createEntityReference(source.getNodeName());
1638                 // the subtree is created according to this doc by the method
1639                 // above, so avoid carrying over original subtree
1640                 deep = false;
1641                 break;
1642             }
1643 
1644             case ENTITY_NODE: {
1645                 Entity srcentity = (Entity)source;
1646                 EntityImpl newentity =
1647                 (EntityImpl)createEntity(source.getNodeName());
1648                 newentity.setPublicId(srcentity.getPublicId());
1649                 newentity.setSystemId(srcentity.getSystemId());
1650                 newentity.setNotationName(srcentity.getNotationName());
1651                 // Kids carry additional value,
1652                 // allow deep import temporarily
1653                 newentity.isReadOnly(false);
1654                 newnode = newentity;
1655                 break;
1656             }
1657 
1658             case PROCESSING_INSTRUCTION_NODE: {
1659                 newnode = createProcessingInstruction(source.getNodeName(),
1660                         source.getNodeValue());
1661                 break;
1662             }
1663 
1664             case COMMENT_NODE: {
1665                 newnode = createComment(source.getNodeValue());
1666                 break;
1667             }
1668 
1669             case DOCUMENT_TYPE_NODE: {
1670                 // unless this is used as part of cloning a Document
1671                 // forbid it for the sake of being compliant to the DOM spec
1672                 if (!cloningDoc) {
1673                     String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1674                     throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1675                 }
1676                 DocumentType srcdoctype = (DocumentType)source;
1677                 DocumentTypeImpl newdoctype = (DocumentTypeImpl)
1678                 createDocumentType(srcdoctype.getNodeName(),
1679                         srcdoctype.getPublicId(),
1680                         srcdoctype.getSystemId());
1681                 // Values are on NamedNodeMaps
1682                 NamedNodeMap smap = srcdoctype.getEntities();
1683                 NamedNodeMap tmap = newdoctype.getEntities();
1684                 if(smap != null) {
1685                     for(int i = 0; i < smap.getLength(); i++) {
1686                         tmap.setNamedItem(importNode(smap.item(i), true, true,
1687                                 reversedIdentifiers));
1688                     }
1689                 }
1690                 smap = srcdoctype.getNotations();
1691                 tmap = newdoctype.getNotations();
1692                 if (smap != null) {
1693                     for(int i = 0; i < smap.getLength(); i++) {
1694                         tmap.setNamedItem(importNode(smap.item(i), true, true,
1695                                 reversedIdentifiers));
1696                     }
1697                 }
1698 
1699                 // NOTE: At this time, the DOM definition of DocumentType
1700                 // doesn't cover Elements and their Attributes. domimpl's
1701                 // extentions in that area will not be preserved, even if
1702                 // copying from domimpl to domimpl. We could special-case
1703                 // that here. Arguably we should. Consider. ?????
1704                 newnode = newdoctype;
1705                 break;
1706             }
1707 
1708             case DOCUMENT_FRAGMENT_NODE: {
1709                 newnode = createDocumentFragment();
1710                 // No name, kids carry value
1711                 break;
1712             }
1713 
1714             case NOTATION_NODE: {
1715                 Notation srcnotation = (Notation)source;
1716                 NotationImpl newnotation =
1717                 (NotationImpl)createNotation(source.getNodeName());
1718                 newnotation.setPublicId(srcnotation.getPublicId());
1719                 newnotation.setSystemId(srcnotation.getSystemId());
1720                 // Kids carry additional value
1721                 newnode = newnotation;
1722                 // No name, no value
1723                 break;
1724             }
1725             case DOCUMENT_NODE : // Can't import document nodes
1726             default: {           // Unknown node type
1727                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1728                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1729             }
1730         }
1731 
1732                 if(userData != null)
1733                         callUserDataHandlers(source, newnode, UserDataHandler.NODE_IMPORTED,userData);
1734 
1735         // If deep, replicate and attach the kids.
1736         if (deep) {
1737             for (Node srckid = source.getFirstChild();
1738                     srckid != null;
1739                     srckid = srckid.getNextSibling()) {
1740                 newnode.appendChild(importNode(srckid, true, cloningDoc,
1741                         reversedIdentifiers));
1742             }
1743         }
1744         if (newnode.getNodeType() == Node.ENTITY_NODE) {
1745             ((NodeImpl)newnode).setReadOnly(true, true);
1746         }
1747         return newnode;
1748 
1749     } // importNode(Node,boolean,boolean,Map):Node
1750 
1751     /**
1752      * DOM Level 3 WD - Experimental
1753      * Change the node's ownerDocument, and its subtree, to this Document
1754      *
1755      * @param source The node to adopt.
1756      * @see #importNode
1757      **/
1758     public Node adoptNode(Node source) {
1759         NodeImpl node;
1760         Map<String, UserDataRecord> userData;
1761         try {
1762             node = (NodeImpl) source;
1763         } catch (ClassCastException e) {
1764             // source node comes from a different DOMImplementation
1765             return null;
1766         }
1767 
1768         // Return null if the source is null
1769 
1770         if (source == null ) {
1771             return null;
1772         } else if (source.getOwnerDocument() != null) {
1773 
1774             DOMImplementation thisImpl = this.getImplementation();
1775             DOMImplementation otherImpl = source.getOwnerDocument().getImplementation();
1776 
1777             // when the source node comes from a different implementation.
1778             if (thisImpl != otherImpl) {
1779 
1780                 // Adopting from a DefferedDOM to DOM
1781                 if (thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl &&
1782                         otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl) {
1783                     // traverse the DOM and expand deffered nodes and then allow adoption
1784                     undeferChildren (node);
1785                 } else if ( thisImpl instanceof com.sun.org.apache.xerces.internal.dom.DeferredDOMImplementationImpl
1786                         && otherImpl instanceof com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl) {
1787                     // Adopting from a DOM into a DefferedDOM, this should be okay
1788                 } else {
1789                     // Adopting between two dissimilar DOM's is not allowed
1790                     return null;
1791                 }
1792             }
1793         }
1794 
1795         switch (node.getNodeType()) {
1796             case ATTRIBUTE_NODE: {
1797                 AttrImpl attr = (AttrImpl) node;
1798                 // remove node from wherever it is
1799                 if( attr.getOwnerElement() != null){
1800                     //1. owner element attribute is set to null
1801                     attr.getOwnerElement().removeAttributeNode(attr);
1802                 }
1803                 //2. specified flag is set to true
1804                 attr.isSpecified(true);
1805                 userData = node.getUserDataRecord();
1806 
1807                 //3. change ownership
1808                 attr.setOwnerDocument(this);
1809                 if (userData != null) {
1810                     setUserDataTable(node, userData);
1811                 }
1812                 break;
1813             }
1814             //entity, notation nodes are read only nodes.. so they can't be adopted.
1815             //runtime will fall through to NOTATION_NODE
1816             case ENTITY_NODE:
1817             case NOTATION_NODE:{
1818                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
1819                 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
1820 
1821             }
1822             //document, documentype nodes can't be adopted.
1823             //runtime will fall through to DocumentTypeNode
1824             case DOCUMENT_NODE:
1825             case DOCUMENT_TYPE_NODE: {
1826                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
1827                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
1828             }
1829             case ENTITY_REFERENCE_NODE: {
1830                 userData = node.getUserDataRecord();
1831                 // remove node from wherever it is
1832                 Node parent = node.getParentNode();
1833                 if (parent != null) {
1834                     parent.removeChild(source);
1835                 }
1836                 // discard its replacement value
1837                 Node child;
1838                 while ((child = node.getFirstChild()) != null) {
1839                     node.removeChild(child);
1840                 }
1841                 // change ownership
1842                 node.setOwnerDocument(this);
1843                 if (userData != null) {
1844                     setUserDataTable(node, userData);
1845                 }
1846                 // set its new replacement value if any
1847                 if (docType == null) {
1848                     break;
1849                 }
1850                 NamedNodeMap entities = docType.getEntities();
1851                 Node entityNode = entities.getNamedItem(node.getNodeName());
1852                 if (entityNode == null) {
1853                     break;
1854                 }
1855                 for (child = entityNode.getFirstChild();
1856                         child != null; child = child.getNextSibling()) {
1857                     Node childClone = child.cloneNode(true);
1858                     node.appendChild(childClone);
1859                 }
1860                 break;
1861             }
1862             case ELEMENT_NODE: {
1863                 userData = node.getUserDataRecord();
1864                 // remove node from wherever it is
1865                 Node parent = node.getParentNode();
1866                 if (parent != null) {
1867                     parent.removeChild(source);
1868                 }
1869                 // change ownership
1870                 node.setOwnerDocument(this);
1871                 if (userData != null) {
1872                     setUserDataTable(node, userData);
1873                 }
1874                 // reconcile default attributes
1875                 ((ElementImpl)node).reconcileDefaultAttributes();
1876                 break;
1877             }
1878             default: {
1879                 userData = node.getUserDataRecord();
1880                 // remove node from wherever it is
1881                 Node parent = node.getParentNode();
1882                 if (parent != null) {
1883                     parent.removeChild(source);
1884                 }
1885                 // change ownership
1886                 node.setOwnerDocument(this);
1887                 if (userData != null) {
1888                     setUserDataTable(node, userData);
1889                 }
1890             }
1891         }
1892 
1893                 //DOM L3 Core CR
1894         //http://www.w3.org/TR/2003/CR-DOM-Level-3-Core-20031107/core.html#UserDataHandler-ADOPTED
1895         if (userData != null) {
1896             callUserDataHandlers(source, null, UserDataHandler.NODE_ADOPTED, userData);
1897         }
1898 
1899         return node;
1900     }
1901 
1902     /**
1903      * Traverses the DOM Tree and expands deferred nodes and their
1904      * children.
1905      *
1906      */
1907     protected void undeferChildren(Node node) {
1908 
1909         Node top = node;
1910 
1911         while (null != node) {
1912 
1913             if (((NodeImpl)node).needsSyncData()) {
1914                 ((NodeImpl)node).synchronizeData();
1915             }
1916 
1917             NamedNodeMap attributes = node.getAttributes();
1918             if (attributes != null) {
1919                 int length = attributes.getLength();
1920                 for (int i = 0; i < length; ++i) {
1921                     undeferChildren(attributes.item(i));
1922                 }
1923             }
1924 
1925             Node nextNode = null;
1926             nextNode = node.getFirstChild();
1927 
1928             while (null == nextNode) {
1929 
1930                 if (top.equals(node))
1931                     break;
1932 
1933                 nextNode = node.getNextSibling();
1934 
1935                 if (null == nextNode) {
1936                     node = node.getParentNode();
1937 
1938                     if ((null == node) || (top.equals(node))) {
1939                         nextNode = null;
1940                         break;
1941                     }
1942                 }
1943             }
1944 
1945             node = nextNode;
1946         }
1947     }
1948 
1949     // identifier maintenence
1950     /**
1951      * Introduced in DOM Level 2
1952      * Returns the Element whose ID is given by elementId. If no such element
1953      * exists, returns null. Behavior is not defined if more than one element
1954      * has this ID.
1955      * <p>
1956      * Note: The DOM implementation must have information that says which
1957      * attributes are of type ID. Attributes with the name "ID" are not of type
1958      * ID unless so defined. Implementations that do not know whether
1959      * attributes are of type ID or not are expected to return null.
1960      * @see #getIdentifier
1961      */
1962     public Element getElementById(String elementId) {
1963         return getIdentifier(elementId);
1964     }
1965 
1966     /**
1967      * Remove all identifiers from the ID table
1968      */
1969     protected final void clearIdentifiers(){
1970         if (identifiers != null){
1971             identifiers.clear();
1972         }
1973     }
1974 
1975     /**
1976      * Registers an identifier name with a specified element node.
1977      * If the identifier is already registered, the new element
1978      * node replaces the previous node. If the specified element
1979      * node is null, removeIdentifier() is called.
1980      *
1981      * @see #getIdentifier
1982      * @see #removeIdentifier
1983      */
1984     public void putIdentifier(String idName, Element element) {
1985 
1986         if (element == null) {
1987             removeIdentifier(idName);
1988             return;
1989         }
1990 
1991         if (needsSyncData()) {
1992             synchronizeData();
1993         }
1994 
1995         if (identifiers == null) {
1996             identifiers = new HashMap<>();
1997         }
1998 
1999         identifiers.put(idName, element);
2000 
2001     } // putIdentifier(String,Element)
2002 
2003     /**
2004      * Returns a previously registered element with the specified
2005      * identifier name, or null if no element is registered.
2006      *
2007      * @see #putIdentifier
2008      * @see #removeIdentifier
2009      */
2010     public Element getIdentifier(String idName) {
2011 
2012         if (needsSyncData()) {
2013             synchronizeData();
2014         }
2015 
2016         if (identifiers == null) {
2017             return null;
2018         }
2019         Element elem = (Element) identifiers.get(idName);
2020         if (elem != null) {
2021             // check that the element is in the tree
2022             Node parent = elem.getParentNode();
2023             while (parent != null) {
2024                 if (parent == this) {
2025                     return elem;
2026                 }
2027                 parent = parent.getParentNode();
2028             }
2029         }
2030         return null;
2031     } // getIdentifier(String):Element
2032 
2033     /**
2034      * Removes a previously registered element with the specified
2035      * identifier name.
2036      *
2037      * @see #putIdentifier
2038      * @see #getIdentifier
2039      */
2040     public void removeIdentifier(String idName) {
2041 
2042         if (needsSyncData()) {
2043             synchronizeData();
2044         }
2045 
2046         if (identifiers == null) {
2047             return;
2048         }
2049 
2050         identifiers.remove(idName);
2051 
2052     } // removeIdentifier(String)
2053 
2054     //
2055     // DOM2: Namespace methods
2056     //
2057     /**
2058      * Introduced in DOM Level 2. <p>
2059      * Creates an element of the given qualified name and namespace URI.
2060      * If the given namespaceURI is null or an empty string and the
2061      * qualifiedName has a prefix that is "xml", the created element
2062      * is bound to the predefined namespace
2063      * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2064      * @param namespaceURI The namespace URI of the element to
2065      *                     create.
2066      * @param qualifiedName The qualified name of the element type to
2067      *                      instantiate.
2068      * @return Element A new Element object with the following attributes:
2069      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2070      * name contains an invalid character.
2071      * @throws DOMException NAMESPACE_ERR: Raised if the qualifiedName has a
2072      *                      prefix that is "xml" and the namespaceURI is
2073      *                      neither null nor an empty string nor
2074      *                      "http://www.w3.org/XML/1998/namespace", or
2075      *                      if the qualifiedName has a prefix different
2076      *                      from "xml" and the namespaceURI is null or an
2077      *                      empty string.
2078      * @since WD-DOM-Level-2-19990923
2079      */
2080     public Element createElementNS(String namespaceURI, String qualifiedName)
2081             throws DOMException {
2082         return new ElementNSImpl(this, namespaceURI, qualifiedName);
2083     }
2084 
2085     /**
2086      * NON-DOM: a factory method used by the Xerces DOM parser
2087      * to create an element.
2088      *
2089      * @param namespaceURI The namespace URI of the element to
2090      *                     create.
2091      * @param qualifiedName The qualified name of the element type to
2092      *                      instantiate.
2093      * @param localpart  The local name of the attribute to instantiate.
2094      *
2095      * @return Element A new Element object with the following attributes:
2096      * @exception DOMException INVALID_CHARACTER_ERR: Raised if the specified
2097      *                   name contains an invalid character.
2098      */
2099     public Element createElementNS(String namespaceURI, String qualifiedName,
2100             String localpart)
2101             throws DOMException {
2102         return new ElementNSImpl(this, namespaceURI, qualifiedName, localpart);
2103     }
2104 
2105     /**
2106      * Introduced in DOM Level 2. <p>
2107      * Creates an attribute of the given qualified name and namespace URI.
2108      * If the given namespaceURI is null or an empty string and the
2109      * qualifiedName has a prefix that is "xml", the created element
2110      * is bound to the predefined namespace
2111      * "http://www.w3.org/XML/1998/namespace" [Namespaces].
2112      *
2113      * @param namespaceURI  The namespace URI of the attribute to
2114      *                      create. When it is null or an empty string,
2115      *                      this method behaves like createAttribute.
2116      * @param qualifiedName The qualified name of the attribute to
2117      *                      instantiate.
2118      * @return Attr         A new Attr object.
2119      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2120      * name contains an invalid character.
2121      * @since WD-DOM-Level-2-19990923
2122      */
2123     public Attr createAttributeNS(String namespaceURI, String qualifiedName)
2124             throws DOMException {
2125         return new AttrNSImpl(this, namespaceURI, qualifiedName);
2126     }
2127 
2128     /**
2129      * NON-DOM: a factory method used by the Xerces DOM parser
2130      * to create an element.
2131      *
2132      * @param namespaceURI  The namespace URI of the attribute to
2133      *                      create. When it is null or an empty string,
2134      *                      this method behaves like createAttribute.
2135      * @param qualifiedName The qualified name of the attribute to
2136      *                      instantiate.
2137      * @param localpart     The local name of the attribute to instantiate.
2138      *
2139      * @return Attr         A new Attr object.
2140      * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
2141      * name contains an invalid character.
2142      */
2143     public Attr createAttributeNS(String namespaceURI, String qualifiedName,
2144             String localpart)
2145             throws DOMException {
2146         return new AttrNSImpl(this, namespaceURI, qualifiedName, localpart);
2147     }
2148 
2149     /**
2150      * Introduced in DOM Level 2. <p>
2151      * Returns a NodeList of all the Elements with a given local name and
2152      * namespace URI in the order in which they would be encountered in a
2153      * preorder traversal of the Document tree.
2154      * @param namespaceURI  The namespace URI of the elements to match
2155      *                      on. The special value "*" matches all
2156      *                      namespaces. When it is null or an empty
2157      *                      string, this method behaves like
2158      *                      getElementsByTagName.
2159      * @param localName     The local name of the elements to match on.
2160      *                      The special value "*" matches all local names.
2161      * @return NodeList     A new NodeList object containing all the matched
2162      *                      Elements.
2163      * @since WD-DOM-Level-2-19990923
2164      */
2165     public NodeList getElementsByTagNameNS(String namespaceURI,
2166             String localName) {
2167         return new DeepNodeListImpl(this, namespaceURI, localName);
2168     }
2169 
2170     //
2171     // Object methods
2172     //
2173 
2174     /** Clone. */
2175     public Object clone() throws CloneNotSupportedException {
2176         CoreDocumentImpl newdoc = (CoreDocumentImpl) super.clone();
2177         newdoc.docType = null;
2178         newdoc.docElement = null;
2179         return newdoc;
2180     }
2181 
2182     //
2183     // Public static methods
2184     //
2185 
2186     /**
2187      * Check the string against XML's definition of acceptable names for
2188      * elements and attributes and so on using the XMLCharacterProperties
2189      * utility class
2190      */
2191 
2192     public static final boolean isXMLName(String s, boolean xml11Version) {
2193 
2194         if (s == null) {
2195             return false;
2196         }
2197         if(!xml11Version)
2198             return XMLChar.isValidName(s);
2199         else
2200             return XML11Char.isXML11ValidName(s);
2201 
2202     } // isXMLName(String):boolean
2203 
2204     /**
2205      * Checks if the given qualified name is legal with respect
2206      * to the version of XML to which this document must conform.
2207      *
2208      * @param prefix prefix of qualified name
2209      * @param local local part of qualified name
2210      */
2211     public static final boolean isValidQName(String prefix, String local, boolean xml11Version) {
2212 
2213         // check that both prefix and local part match NCName
2214         if (local == null) return false;
2215         boolean validNCName = false;
2216 
2217         if (!xml11Version) {
2218             validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2219                     && XMLChar.isValidNCName(local);
2220         }
2221         else {
2222             validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2223                     && XML11Char.isXML11ValidNCName(local);
2224         }
2225 
2226         return validNCName;
2227     }
2228     //
2229     // Protected methods
2230     //
2231 
2232     /**
2233      * Uses the kidOK lookup table to check whether the proposed
2234      * tree structure is legal.
2235      */
2236     protected boolean isKidOK(Node parent, Node child) {
2237         if (allowGrammarAccess &&
2238         parent.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
2239             return child.getNodeType() == Node.ELEMENT_NODE;
2240         }
2241         return 0 != (kidOK[parent.getNodeType()] & 1 << child.getNodeType());
2242     }
2243 
2244     /**
2245      * Denotes that this node has changed.
2246      */
2247     protected void changed() {
2248         changes++;
2249     }
2250 
2251     /**
2252      * Returns the number of changes to this node.
2253      */
2254     protected int changes() {
2255         return changes;
2256     }
2257 
2258     //  NodeListCache pool
2259 
2260     /**
2261      * Returns a NodeListCache for the given node.
2262      */
2263     NodeListCache getNodeListCache(ParentNode owner) {
2264         if (fFreeNLCache == null) {
2265             return new NodeListCache(owner);
2266         }
2267         NodeListCache c = fFreeNLCache;
2268         fFreeNLCache = fFreeNLCache.next;
2269         c.fChild = null;
2270         c.fChildIndex = -1;
2271         c.fLength = -1;
2272         // revoke previous ownership
2273         if (c.fOwner != null) {
2274             c.fOwner.fNodeListCache = null;
2275         }
2276         c.fOwner = owner;
2277         // c.next = null; not necessary, except for confused people...
2278         return c;
2279     }
2280 
2281     /**
2282      * Puts the given NodeListCache in the free list.
2283      * Note: The owner node can keep using it until we reuse it
2284      */
2285     void freeNodeListCache(NodeListCache c) {
2286         c.next = fFreeNLCache;
2287         fFreeNLCache = c;
2288     }
2289 
2290 
2291 
2292     /**
2293      * Associate an object to a key on this node. The object can later be
2294      * retrieved from this node by calling <code>getUserData</code> with the
2295      * same key.
2296      * @param n The node to associate the object to.
2297      * @param key The key to associate the object to.
2298      * @param data The object to associate to the given key, or
2299      *   <code>null</code> to remove any existing association to that key.
2300      * @param handler The handler to associate to that key, or
2301      *   <code>null</code>.
2302      * @return Returns the <code>DOMObject</code> previously associated to
2303      *   the given key on this node, or <code>null</code> if there was none.
2304      * @since DOM Level 3
2305      *
2306      * REVISIT: we could use a free list of UserDataRecord here
2307      */
2308     public Object setUserData(Node n, String key,
2309             Object data, UserDataHandler handler) {
2310         if (data == null) {
2311             if (nodeUserData != null) {
2312                 Map<String, UserDataRecord> t = nodeUserData.get(n);
2313                 if (t != null) {
2314                     UserDataRecord r = t.remove(key);
2315                     if (r != null) {
2316                         return r.fData;
2317                     }
2318                 }
2319             }
2320             return null;
2321         } else {
2322             Map<String, UserDataRecord> t;
2323             if (nodeUserData == null) {
2324                 nodeUserData = new HashMap<>();
2325                 t = new HashMap<>();
2326                 nodeUserData.put(n, t);
2327             } else {
2328                 t = nodeUserData.get(n);
2329                 if (t == null) {
2330                     t = new HashMap<>();
2331                     nodeUserData.put(n, t);
2332                 }
2333             }
2334             UserDataRecord r = t.put(key, new UserDataRecord(data, handler));
2335             if (r != null) {
2336                 return r.fData;
2337             }
2338             return null;
2339         }
2340     }
2341 
2342 
2343     /**
2344      * Retrieves the object associated to a key on a this node. The object
2345      * must first have been set to this node by calling
2346      * <code>setUserData</code> with the same key.
2347      * @param n The node the object is associated to.
2348      * @param key The key the object is associated to.
2349      * @return Returns the <code>DOMObject</code> associated to the given key
2350      *   on this node, or <code>null</code> if there was none.
2351      * @since DOM Level 3
2352      */
2353     public Object getUserData(Node n, String key) {
2354         if (nodeUserData == null) {
2355             return null;
2356         }
2357         Map<String, UserDataRecord> t = nodeUserData.get(n);
2358         if (t == null) {
2359             return null;
2360         }
2361         UserDataRecord r = t.get(key);
2362         if (r != null) {
2363             return r.fData;
2364         }
2365         return null;
2366     }
2367 
2368     protected Map<String, UserDataRecord> getUserDataRecord(Node n) {
2369         if (nodeUserData == null) {
2370             return null;
2371         }
2372         Map<String, UserDataRecord> t = nodeUserData.get(n);
2373         if (t == null) {
2374             return null;
2375         }
2376         return t;
2377     }
2378 
2379     /**
2380      * Remove user data table for the given node.
2381      * @param n The node this operation applies to.
2382      * @return The removed table.
2383      */
2384     Map<String, UserDataRecord> removeUserDataTable(Node n) {
2385         if (nodeUserData == null) {
2386             return null;
2387         }
2388         return nodeUserData.get(n);
2389     }
2390 
2391     /**
2392      * Set user data table for the given node.
2393      * @param n The node this operation applies to.
2394      * @param data The user data table.
2395      */
2396     void setUserDataTable(Node n, Map<String, UserDataRecord> data) {
2397         if (nodeUserData == null) {
2398             nodeUserData = new HashMap<>();
2399         }
2400 
2401         if (data != null) {
2402             nodeUserData.put(n, data);
2403         }
2404     }
2405 
2406     /**
2407      * Call user data handlers when a node is deleted (finalized)
2408      * @param n The node this operation applies to.
2409      * @param c The copy node or null.
2410      * @param operation The operation - import, clone, or delete.
2411      */
2412     void callUserDataHandlers(Node n, Node c, short operation) {
2413         if (nodeUserData == null) {
2414             return;
2415         }
2416 
2417         if (n instanceof NodeImpl) {
2418             Map<String, UserDataRecord> t = ((NodeImpl) n).getUserDataRecord();
2419             if (t == null || t.isEmpty()) {
2420                 return;
2421             }
2422             callUserDataHandlers(n, c, operation, t);
2423         }
2424     }
2425 
2426     /**
2427      * Call user data handlers when a node is deleted (finalized)
2428      * @param n The node this operation applies to.
2429      * @param c The copy node or null.
2430      * @param operation The operation - import, clone, or delete.
2431      * @param handlers Data associated with n.
2432      */
2433     void callUserDataHandlers(Node n, Node c, short operation, Map<String, UserDataRecord> userData) {
2434         if (userData == null || userData.isEmpty()) {
2435             return;
2436         }
2437 
2438         userData.keySet().stream().forEach((key) -> {
2439             UserDataRecord r = userData.get(key);
2440             if (r.fHandler != null) {
2441                 r.fHandler.handle(operation, key, r.fData, n, c);
2442             }
2443         });
2444     }
2445 
2446     /**
2447      * Call user data handlers to let them know the nodes they are related to
2448      * are being deleted. The alternative would be to do that on Node but
2449      * because the nodes are used as the keys we have a reference to them that
2450      * prevents them from being gc'ed until the document is. At the same time,
2451      * doing it here has the advantage of avoiding a finalize() method on Node,
2452      * which would affect all nodes and not just the ones that have a user
2453      * data.
2454      */
2455     // Temporarily comment out this method, because
2456     // 1. It seems that finalizers are not guaranteed to be called, so the
2457     //    functionality is not implemented.
2458     // 2. It affects the performance greatly in multi-thread environment.
2459     // -SG
2460     /*public void finalize() {
2461      if (userData == null) {
2462      return;
2463      }
2464      Enumeration nodes = userData.keys();
2465      while (nodes.hasMoreElements()) {
2466      Object node = nodes.nextElement();
2467      Hashtable t = (Hashtable) userData.get(node);
2468      if (t != null && !t.isEmpty()) {
2469      Enumeration keys = t.keys();
2470      while (keys.hasMoreElements()) {
2471      String key = (String) keys.nextElement();
2472      UserDataRecord r = (UserDataRecord) t.get(key);
2473      if (r.fHandler != null) {
2474      r.fHandler.handle(UserDataHandler.NODE_DELETED,
2475      key, r.fData, null, null);
2476      }
2477      }
2478      }
2479      }
2480      }*/
2481 
2482     protected final void checkNamespaceWF( String qname, int colon1,
2483             int colon2) {
2484 
2485         if (!errorChecking) {
2486             return;
2487         }
2488         // it is an error for NCName to have more than one ':'
2489         // check if it is valid QName [Namespace in XML production 6]
2490         // :camera , nikon:camera:minolta, camera:
2491         if (colon1 == 0 || colon1 == qname.length() - 1 || colon2 != colon1) {
2492             String msg =
2493             DOMMessageFormatter.formatMessage(
2494                             DOMMessageFormatter.DOM_DOMAIN,
2495                             "NAMESPACE_ERR",
2496                             null);
2497             throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2498         }
2499     }
2500     protected final void checkDOMNSErr(String prefix,
2501             String namespace) {
2502         if (errorChecking) {
2503             if (namespace == null) {
2504                 String msg =
2505                 DOMMessageFormatter.formatMessage(
2506                                 DOMMessageFormatter.DOM_DOMAIN,
2507                                 "NAMESPACE_ERR",
2508                                 null);
2509                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2510             }
2511             else if (prefix.equals("xml")
2512                     && !namespace.equals(NamespaceContext.XML_URI)) {
2513                 String msg =
2514                 DOMMessageFormatter.formatMessage(
2515                                 DOMMessageFormatter.DOM_DOMAIN,
2516                                 "NAMESPACE_ERR",
2517                                 null);
2518                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2519             }
2520             else if (
2521             prefix.equals("xmlns")
2522                     && !namespace.equals(NamespaceContext.XMLNS_URI)
2523                     || (!prefix.equals("xmlns")
2524                     && namespace.equals(NamespaceContext.XMLNS_URI))) {
2525                 String msg =
2526                 DOMMessageFormatter.formatMessage(
2527                                 DOMMessageFormatter.DOM_DOMAIN,
2528                                 "NAMESPACE_ERR",
2529                                 null);
2530                 throw new DOMException(DOMException.NAMESPACE_ERR, msg);
2531             }
2532         }
2533     }
2534 
2535     /**
2536      * Checks if the given qualified name is legal with respect
2537      * to the version of XML to which this document must conform.
2538      *
2539      * @param prefix prefix of qualified name
2540      * @param local local part of qualified name
2541      */
2542     protected final void checkQName(String prefix, String local) {
2543         if (!errorChecking) {
2544             return;
2545         }
2546 
2547         // check that both prefix and local part match NCName
2548         boolean validNCName = false;
2549         if (!xml11Version) {
2550             validNCName = (prefix == null || XMLChar.isValidNCName(prefix))
2551                     && XMLChar.isValidNCName(local);
2552         }
2553         else {
2554             validNCName = (prefix == null || XML11Char.isXML11ValidNCName(prefix))
2555                     && XML11Char.isXML11ValidNCName(local);
2556         }
2557 
2558         if (!validNCName) {
2559             // REVISIT: add qname parameter to the message
2560             String msg =
2561             DOMMessageFormatter.formatMessage(
2562                             DOMMessageFormatter.DOM_DOMAIN,
2563                             "INVALID_CHARACTER_ERR",
2564                             null);
2565             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
2566         }
2567     }
2568 
2569     /**
2570      * We could have more xml versions in future , but for now we could
2571      * do with this to handle XML 1.0 and 1.1
2572      */
2573     boolean isXML11Version(){
2574         return xml11Version;
2575     }
2576 
2577     boolean isNormalizeDocRequired(){
2578         // REVISIT: Implement to optimize when normalization
2579         // is required
2580         return true;
2581     }
2582 
2583     //we should be checking the (elements, attribute, entity etc.) names only when
2584     //version of the document is changed.
2585     boolean isXMLVersionChanged(){
2586         return xmlVersionChanged ;
2587     }
2588     /**
2589      * NON-DOM: kept for backward compatibility
2590      * Store user data related to a given node
2591      * This is a place where we could use weak references! Indeed, the node
2592      * here won't be GC'ed as long as some user data is attached to it, since
2593      * the userData table will have a reference to the node.
2594      */
2595     protected void setUserData(NodeImpl n, Object data) {
2596         setUserData(n, "XERCES1DOMUSERDATA", data, null);
2597     }
2598 
2599     /**
2600      * NON-DOM: kept for backward compatibility
2601      * Retreive user data related to a given node
2602      */
2603     protected Object getUserData(NodeImpl n) {
2604         return getUserData(n, "XERCES1DOMUSERDATA");
2605     }
2606 
2607 
2608     // Event related methods overidden in subclass
2609 
2610     protected void addEventListener(NodeImpl node, String type,
2611             EventListener listener,
2612             boolean useCapture) {
2613         // does nothing by default - overidden in subclass
2614     }
2615 
2616     protected void removeEventListener(NodeImpl node, String type,
2617             EventListener listener,
2618             boolean useCapture) {
2619         // does nothing by default - overidden in subclass
2620     }
2621 
2622     protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
2623         // does nothing by default - overidden in subclass
2624     }
2625 
2626     protected boolean dispatchEvent(NodeImpl node, Event event) {
2627         // does nothing by default - overidden in subclass
2628         return false;
2629     }
2630 
2631     // Notification methods overidden in subclasses
2632 
2633     /**
2634      * A method to be called when some text was changed in a text node,
2635      * so that live objects can be notified.
2636      */
2637     void replacedText(NodeImpl node) {
2638     }
2639 
2640     /**
2641      * A method to be called when some text was deleted from a text node,
2642      * so that live objects can be notified.
2643      */
2644     void deletedText(NodeImpl node, int offset, int count) {
2645     }
2646 
2647     /**
2648      * A method to be called when some text was inserted into a text node,
2649      * so that live objects can be notified.
2650      */
2651     void insertedText(NodeImpl node, int offset, int count) {
2652     }
2653 
2654     /**
2655      * A method to be called when a character data node is about to be modified
2656      */
2657     void modifyingCharacterData(NodeImpl node, boolean replace) {
2658     }
2659 
2660     /**
2661      * A method to be called when a character data node has been modified
2662      */
2663     void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
2664     }
2665 
2666     /**
2667      * A method to be called when a node is about to be inserted in the tree.
2668      */
2669     void insertingNode(NodeImpl node, boolean replace) {
2670     }
2671 
2672     /**
2673      * A method to be called when a node has been inserted in the tree.
2674      */
2675     void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
2676     }
2677 
2678     /**
2679      * A method to be called when a node is about to be removed from the tree.
2680      */
2681     void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
2682     }
2683 
2684     /**
2685      * A method to be called when a node has been removed from the tree.
2686      */
2687     void removedNode(NodeImpl node, boolean replace) {
2688     }
2689 
2690     /**
2691      * A method to be called when a node is about to be replaced in the tree.
2692      */
2693     void replacingNode(NodeImpl node) {
2694     }
2695 
2696     /**
2697      * A method to be called when a node has been replaced in the tree.
2698      */
2699     void replacedNode(NodeImpl node) {
2700     }
2701 
2702     /**
2703      * A method to be called when a character data node is about to be replaced
2704      */
2705     void replacingData(NodeImpl node) {
2706     }
2707 
2708     /**
2709      *  method to be called when a character data node has been replaced.
2710      */
2711     void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
2712     }
2713 
2714 
2715     /**
2716      * A method to be called when an attribute value has been modified
2717      */
2718     void modifiedAttrValue(AttrImpl attr, String oldvalue) {
2719     }
2720 
2721     /**
2722      * A method to be called when an attribute node has been set
2723      */
2724     void setAttrNode(AttrImpl attr, AttrImpl previous) {
2725     }
2726 
2727     /**
2728      * A method to be called when an attribute node has been removed
2729      */
2730     void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
2731     }
2732 
2733     /**
2734      * A method to be called when an attribute node has been renamed
2735      */
2736     void renamedAttrNode(Attr oldAt, Attr newAt) {
2737     }
2738 
2739     /**
2740      * A method to be called when an element has been renamed
2741      */
2742     void renamedElement(Element oldEl, Element newEl) {
2743     }
2744 
2745     /**
2746      * @serialData Serialized fields. Convert Maps to Hashtables for backward
2747      * compatibility.
2748      */
2749     private void writeObject(ObjectOutputStream out) throws IOException {
2750         // Convert Maps to Hashtables
2751         Hashtable<Node, Hashtable<String, UserDataRecord>> nud = null;
2752         if (nodeUserData != null) {
2753             nud = new Hashtable<>();
2754             for (Map.Entry<Node, Map<String, UserDataRecord>> e : nodeUserData.entrySet()) {
2755                 //e.getValue() will not be null since an entry is always put with a non-null value
2756                 nud.put(e.getKey(), new Hashtable<>(e.getValue()));
2757             }
2758         }
2759 
2760         Hashtable<String, Node> ids = (identifiers == null)? null : new Hashtable<>(identifiers);
2761         Hashtable<Node, Integer> nt = (nodeTable == null)? null : new Hashtable<>(nodeTable);
2762 
2763         // Write serialized fields
2764         ObjectOutputStream.PutField pf = out.putFields();
2765         pf.put("docType", docType);
2766         pf.put("docElement", docElement);
2767         pf.put("fFreeNLCache", fFreeNLCache);
2768         pf.put("encoding", encoding);
2769         pf.put("actualEncoding", actualEncoding);
2770         pf.put("version", version);
2771         pf.put("standalone", standalone);
2772         pf.put("fDocumentURI", fDocumentURI);
2773 
2774         //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2775         pf.put("userData", nud);
2776         pf.put("identifiers", ids);
2777         pf.put("changes", changes);
2778         pf.put("allowGrammarAccess", allowGrammarAccess);
2779         pf.put("errorChecking", errorChecking);
2780         pf.put("ancestorChecking", ancestorChecking);
2781         pf.put("xmlVersionChanged", xmlVersionChanged);
2782         pf.put("documentNumber", documentNumber);
2783         pf.put("nodeCounter", nodeCounter);
2784         pf.put("nodeTable", nt);
2785         pf.put("xml11Version", xml11Version);
2786         out.writeFields();
2787     }
2788 
2789     @SuppressWarnings("unchecked")
2790     private void readObject(ObjectInputStream in)
2791                         throws IOException, ClassNotFoundException {
2792         // We have to read serialized fields first.
2793         ObjectInputStream.GetField gf = in.readFields();
2794         docType = (DocumentTypeImpl)gf.get("docType", null);
2795         docElement = (ElementImpl)gf.get("docElement", null);
2796         fFreeNLCache = (NodeListCache)gf.get("fFreeNLCache", null);
2797         encoding = (String)gf.get("encoding", null);
2798         actualEncoding = (String)gf.get("actualEncoding", null);
2799         version = (String)gf.get("version", null);
2800         standalone = gf.get("standalone", false);
2801         fDocumentURI = (String)gf.get("fDocumentURI", null);
2802 
2803         //userData is the original name. It has been changed to nodeUserData, refer to the corrsponding @serialField
2804         Hashtable<Node, Hashtable<String, UserDataRecord>> nud =
2805                 (Hashtable<Node, Hashtable<String, UserDataRecord>>)gf.get("userData", null);
2806 
2807         Hashtable<String, Node> ids = (Hashtable<String, Node>)gf.get("identifiers", null);
2808 
2809         changes = gf.get("changes", 0);
2810         allowGrammarAccess = gf.get("allowGrammarAccess", false);
2811         errorChecking = gf.get("errorChecking", true);
2812         ancestorChecking = gf.get("ancestorChecking", true);
2813         xmlVersionChanged = gf.get("xmlVersionChanged", false);
2814         documentNumber = gf.get("documentNumber", 0);
2815         nodeCounter = gf.get("nodeCounter", 0);
2816 
2817         Hashtable<Node, Integer> nt = (Hashtable<Node, Integer>)gf.get("nodeTable", null);
2818 
2819         xml11Version = gf.get("xml11Version", false);
2820 
2821         //convert Hashtables back to HashMaps
2822         if (nud != null) {
2823             nodeUserData = new HashMap<>();
2824             for (Map.Entry<Node, Hashtable<String, UserDataRecord>> e : nud.entrySet()) {
2825                 nodeUserData.put(e.getKey(), new HashMap<>(e.getValue()));
2826             }
2827         }
2828 
2829         if (ids != null) identifiers = new HashMap<>(ids);
2830         if (nt != null) nodeTable = new HashMap<>(nt);
2831     }
2832 } // class CoreDocumentImpl