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