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