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