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