1 /*
   2  * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Nov 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 package com.sun.org.apache.xerces.internal.dom;
  22 
  23 import com.sun.org.apache.xerces.internal.impl.Constants;
  24 import com.sun.org.apache.xerces.internal.util.URI;
  25 import com.sun.org.apache.xerces.internal.util.XML11Char;
  26 import com.sun.org.apache.xerces.internal.util.XMLChar;
  27 import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
  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 jdk.xml.internal.SecuritySupport;
  38 import org.w3c.dom.Attr;
  39 import org.w3c.dom.CDATASection;
  40 import org.w3c.dom.Comment;
  41 import org.w3c.dom.DOMConfiguration;
  42 import org.w3c.dom.DOMException;
  43 import org.w3c.dom.DOMImplementation;
  44 import org.w3c.dom.Document;
  45 import org.w3c.dom.DocumentFragment;
  46 import org.w3c.dom.DocumentType;
  47 import org.w3c.dom.Element;
  48 import org.w3c.dom.Entity;
  49 import org.w3c.dom.EntityReference;
  50 import org.w3c.dom.NamedNodeMap;
  51 import org.w3c.dom.Node;
  52 import org.w3c.dom.NodeList;
  53 import org.w3c.dom.Notation;
  54 import org.w3c.dom.ProcessingInstruction;
  55 import org.w3c.dom.Text;
  56 import org.w3c.dom.UserDataHandler;
  57 import org.w3c.dom.events.Event;
  58 import org.w3c.dom.events.EventListener;
  59 import org.w3c.dom.ls.DOMImplementationLS;
  60 import org.w3c.dom.ls.LSSerializer;
  61 
  62 /**
  63  * The Document interface represents the entire HTML or XML document.
  64  * Conceptually, it is the root of the document tree, and provides the
  65  * primary access to the document's data.
  66  * <P>
  67  * Since elements, text nodes, comments, processing instructions,
  68  * etc. cannot exist outside the context of a Document, the Document
  69  * interface also contains the factory methods needed to create these
  70  * objects. The Node objects created have a ownerDocument attribute
  71  * which associates them with the Document within whose context they
  72  * were created.
  73  * <p>
  74  * The CoreDocumentImpl class only implements the DOM Core. Additional modules
  75  * are supported by the more complete DocumentImpl subclass.
  76  * <p>
  77  * <b>Note:</b> When any node in the document is serialized, the
  78  * entire document is serialized along with it.
  79  *
  80  * @xerces.internal
  81  *
  82  * @author Arnaud  Le Hors, IBM
  83  * @author Joe Kesselman, IBM
  84  * @author Andy Clark, IBM
  85  * @author Ralf Pfeiffer, IBM
  86  * @since  PR-DOM-Level-1-19980818.

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