1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.dom; 22 23 import java.io.IOException; 24 import java.io.ObjectInputStream; 25 import java.io.ObjectOutputStream; 26 import java.io.Serializable; 27 28 import org.w3c.dom.DOMException; 29 import org.w3c.dom.Document; 30 import org.w3c.dom.Node; 31 import org.w3c.dom.NodeList; 32 import org.w3c.dom.UserDataHandler; 33 34 /** 35 * ParentNode inherits from ChildNode and adds the capability of having child 36 * nodes. Not every node in the DOM can have children, so only nodes that can 37 * should inherit from this class and pay the price for it. 38 * <P> 39 * ParentNode, just like NodeImpl, also implements NodeList, so it can 40 * return itself in response to the getChildNodes() query. This eliminiates 41 * the need for a separate ChildNodeList object. Note that this is an 42 * IMPLEMENTATION DETAIL; applications should _never_ assume that 43 * this identity exists. On the other hand, subclasses may need to override 44 * this, in case of conflicting names. This is the case for the classes 45 * HTMLSelectElementImpl and HTMLFormElementImpl of the HTML DOM. 46 * <P> 47 * While we have a direct reference to the first child, the last child is 48 * stored as the previous sibling of the first child. First child nodes are 49 * marked as being so, and getNextSibling hides this fact. 50 * <P>Note: Not all parent nodes actually need to also be a child. At some 51 * point we used to have ParentNode inheriting from NodeImpl and another class 52 * called ChildAndParentNode that inherited from ChildNode. But due to the lack 53 * of multiple inheritance a lot of code had to be duplicated which led to a 54 * maintenance nightmare. At the same time only a few nodes (Document, 55 * DocumentFragment, Entity, and Attribute) cannot be a child so the gain in 56 * memory wasn't really worth it. The only type for which this would be the 57 * case is Attribute, but we deal with there in another special way, so this is 58 * not applicable. 59 * <p> 60 * This class doesn't directly support mutation events, however, it notifies 61 * the document when mutations are performed so that the document class do so. 62 * 63 * <p><b>WARNING</b>: Some of the code here is partially duplicated in 64 * AttrImpl, be careful to keep these two classes in sync! 65 * 66 * @xerces.internal 67 * 68 * @author Arnaud Le Hors, IBM 69 * @author Joe Kesselman, IBM 70 * @author Andy Clark, IBM 71 */ 72 public abstract class ParentNode 73 extends ChildNode { 74 75 /** Serialization version. */ 76 static final long serialVersionUID = 2815829867152120872L; 77 78 /** Owner document. */ 79 protected CoreDocumentImpl ownerDocument; 80 81 /** First child. */ 82 protected ChildNode firstChild = null; 83 84 // transients 85 86 /** NodeList cache */ 87 protected transient NodeListCache fNodeListCache = null; 88 89 // 90 // Constructors 91 // 92 93 /** 94 * No public constructor; only subclasses of ParentNode should be 95 * instantiated, and those normally via a Document's factory methods 96 */ 97 protected ParentNode(CoreDocumentImpl ownerDocument) { 98 super(ownerDocument); 99 this.ownerDocument = ownerDocument; 100 } 101 102 /** Constructor for serialization. */ 103 public ParentNode() {} 104 105 // 106 // NodeList methods 107 // 108 109 /** 110 * Returns a duplicate of a given node. You can consider this a 111 * generic "copy constructor" for nodes. The newly returned object should 112 * be completely independent of the source object's subtree, so changes 113 * in one after the clone has been made will not affect the other. 114 * <p> 115 * Example: Cloning a Text node will copy both the node and the text it 116 * contains. 117 * <p> 118 * Example: Cloning something that has children -- Element or Attr, for 119 * example -- will _not_ clone those children unless a "deep clone" 120 * has been requested. A shallow clone of an Attr node will yield an 121 * empty Attr of the same name. 122 * <p> 123 * NOTE: Clones will always be read/write, even if the node being cloned 124 * is read-only, to permit applications using only the DOM API to obtain 125 * editable copies of locked portions of the tree. 126 */ 127 public Node cloneNode(boolean deep) { 128 129 if (needsSyncChildren()) { 130 synchronizeChildren(); 131 } 132 ParentNode newnode = (ParentNode) super.cloneNode(deep); 133 134 // set owner document 135 newnode.ownerDocument = ownerDocument; 136 137 // Need to break the association w/ original kids 138 newnode.firstChild = null; 139 140 // invalidate cache for children NodeList 141 newnode.fNodeListCache = null; 142 143 // Then, if deep, clone the kids too. 144 if (deep) { 145 for (ChildNode child = firstChild; 146 child != null; 147 child = child.nextSibling) { 148 newnode.appendChild(child.cloneNode(true)); 149 } 150 } 151 152 return newnode; 153 154 } // cloneNode(boolean):Node 155 156 /** 157 * Find the Document that this Node belongs to (the document in 158 * whose context the Node was created). The Node may or may not 159 * currently be part of that Document's actual contents. 160 */ 161 public Document getOwnerDocument() { 162 return ownerDocument; 163 } 164 165 /** 166 * same as above but returns internal type and this one is not overridden 167 * by CoreDocumentImpl to return null 168 */ 169 CoreDocumentImpl ownerDocument() { 170 return ownerDocument; 171 } 172 173 /** 174 * NON-DOM 175 * set the ownerDocument of this node and its children 176 */ 177 void setOwnerDocument(CoreDocumentImpl doc) { 178 if (needsSyncChildren()) { 179 synchronizeChildren(); 180 } 181 for (ChildNode child = firstChild; 182 child != null; child = child.nextSibling) { 183 child.setOwnerDocument(doc); 184 } 185 /* setting the owner document of self, after it's children makes the 186 data of children available to the new document. */ 187 super.setOwnerDocument(doc); 188 ownerDocument = doc; 189 } 190 191 /** 192 * Test whether this node has any children. Convenience shorthand 193 * for (Node.getFirstChild()!=null) 194 */ 195 public boolean hasChildNodes() { 196 if (needsSyncChildren()) { 197 synchronizeChildren(); 198 } 199 return firstChild != null; 200 } 201 202 /** 203 * Obtain a NodeList enumerating all children of this node. If there 204 * are none, an (initially) empty NodeList is returned. 205 * <p> 206 * NodeLists are "live"; as children are added/removed the NodeList 207 * will immediately reflect those changes. Also, the NodeList refers 208 * to the actual nodes, so changes to those nodes made via the DOM tree 209 * will be reflected in the NodeList and vice versa. 210 * <p> 211 * In this implementation, Nodes implement the NodeList interface and 212 * provide their own getChildNodes() support. Other DOMs may solve this 213 * differently. 214 */ 215 public NodeList getChildNodes() { 216 217 if (needsSyncChildren()) { 218 synchronizeChildren(); 219 } 220 return this; 221 222 } // getChildNodes():NodeList 223 224 /** The first child of this Node, or null if none. */ 225 public Node getFirstChild() { 226 227 if (needsSyncChildren()) { 228 synchronizeChildren(); 229 } 230 return firstChild; 231 232 } // getFirstChild():Node 233 234 /** The last child of this Node, or null if none. */ 235 public Node getLastChild() { 236 237 if (needsSyncChildren()) { 238 synchronizeChildren(); 239 } 240 return lastChild(); 241 242 } // getLastChild():Node 243 244 final ChildNode lastChild() { 245 // last child is stored as the previous sibling of first child 246 return firstChild != null ? firstChild.previousSibling : null; 247 } 248 249 final void lastChild(ChildNode node) { 250 // store lastChild as previous sibling of first child 251 if (firstChild != null) { 252 firstChild.previousSibling = node; 253 } 254 } 255 256 /** 257 * Move one or more node(s) to our list of children. Note that this 258 * implicitly removes them from their previous parent. 259 * 260 * @param newChild The Node to be moved to our subtree. As a 261 * convenience feature, inserting a DocumentNode will instead insert 262 * all its children. 263 * 264 * @param refChild Current child which newChild should be placed 265 * immediately before. If refChild is null, the insertion occurs 266 * after all existing Nodes, like appendChild(). 267 * 268 * @return newChild, in its new state (relocated, or emptied in the case of 269 * DocumentNode.) 270 * 271 * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a 272 * type that shouldn't be a child of this node, or if newChild is an 273 * ancestor of this node. 274 * 275 * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a 276 * different owner document than we do. 277 * 278 * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of 279 * this node. 280 * 281 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is 282 * read-only. 283 */ 284 public Node insertBefore(Node newChild, Node refChild) 285 throws DOMException { 286 // Tail-call; optimizer should be able to do good things with. 287 return internalInsertBefore(newChild, refChild, false); 288 } // insertBefore(Node,Node):Node 289 290 /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able 291 * to control which mutation events are spawned. This version of the 292 * insertBefore operation allows us to do so. It is not intended 293 * for use by application programs. 294 */ 295 Node internalInsertBefore(Node newChild, Node refChild, boolean replace) 296 throws DOMException { 297 298 boolean errorChecking = ownerDocument.errorChecking; 299 300 if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 301 // SLOW BUT SAFE: We could insert the whole subtree without 302 // juggling so many next/previous pointers. (Wipe out the 303 // parent's child-list, patch the parent pointers, set the 304 // ends of the list.) But we know some subclasses have special- 305 // case behavior they add to insertBefore(), so we don't risk it. 306 // This approch also takes fewer bytecodes. 307 308 // NOTE: If one of the children is not a legal child of this 309 // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children 310 // have been transferred. (Alternative behaviors would be to 311 // reparent up to the first failure point or reparent all those 312 // which are acceptable to the target node, neither of which is 313 // as robust. PR-DOM-0818 isn't entirely clear on which it 314 // recommends????? 315 316 // No need to check kids for right-document; if they weren't, 317 // they wouldn't be kids of that DocFrag. 318 if (errorChecking) { 319 for (Node kid = newChild.getFirstChild(); // Prescan 320 kid != null; kid = kid.getNextSibling()) { 321 322 if (!ownerDocument.isKidOK(this, kid)) { 323 throw new DOMException( 324 DOMException.HIERARCHY_REQUEST_ERR, 325 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null)); 326 } 327 } 328 } 329 330 while (newChild.hasChildNodes()) { 331 insertBefore(newChild.getFirstChild(), refChild); 332 } 333 return newChild; 334 } 335 336 if (newChild == refChild) { 337 // stupid case that must be handled as a no-op triggering events... 338 refChild = refChild.getNextSibling(); 339 removeChild(newChild); 340 insertBefore(newChild, refChild); 341 return newChild; 342 } 343 344 if (needsSyncChildren()) { 345 synchronizeChildren(); 346 } 347 348 if (errorChecking) { 349 if (isReadOnly()) { 350 throw new DOMException( 351 DOMException.NO_MODIFICATION_ALLOWED_ERR, 352 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null)); 353 } 354 if (newChild.getOwnerDocument() != ownerDocument && newChild != ownerDocument) { 355 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, 356 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null)); 357 } 358 if (!ownerDocument.isKidOK(this, newChild)) { 359 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 360 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null)); 361 } 362 // refChild must be a child of this node (or null) 363 if (refChild != null && refChild.getParentNode() != this) { 364 throw new DOMException(DOMException.NOT_FOUND_ERR, 365 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null)); 366 } 367 368 // Prevent cycles in the tree 369 // newChild cannot be ancestor of this Node, 370 // and actually cannot be this 371 if (ownerDocument.ancestorChecking) { 372 boolean treeSafe = true; 373 for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode()) 374 { 375 treeSafe = newChild != a; 376 } 377 if(!treeSafe) { 378 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 379 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null)); 380 } 381 } 382 } 383 384 // notify document 385 ownerDocument.insertingNode(this, replace); 386 387 // Convert to internal type, to avoid repeated casting 388 ChildNode newInternal = (ChildNode)newChild; 389 390 Node oldparent = newInternal.parentNode(); 391 if (oldparent != null) { 392 oldparent.removeChild(newInternal); 393 } 394 395 // Convert to internal type, to avoid repeated casting 396 ChildNode refInternal = (ChildNode)refChild; 397 398 // Attach up 399 newInternal.ownerNode = this; 400 newInternal.isOwned(true); 401 402 // Attach before and after 403 // Note: firstChild.previousSibling == lastChild!! 404 if (firstChild == null) { 405 // this our first and only child 406 firstChild = newInternal; 407 newInternal.isFirstChild(true); 408 newInternal.previousSibling = newInternal; 409 } 410 else { 411 if (refInternal == null) { 412 // this is an append 413 ChildNode lastChild = firstChild.previousSibling; 414 lastChild.nextSibling = newInternal; 415 newInternal.previousSibling = lastChild; 416 firstChild.previousSibling = newInternal; 417 } 418 else { 419 // this is an insert 420 if (refChild == firstChild) { 421 // at the head of the list 422 firstChild.isFirstChild(false); 423 newInternal.nextSibling = firstChild; 424 newInternal.previousSibling = firstChild.previousSibling; 425 firstChild.previousSibling = newInternal; 426 firstChild = newInternal; 427 newInternal.isFirstChild(true); 428 } 429 else { 430 // somewhere in the middle 431 ChildNode prev = refInternal.previousSibling; 432 newInternal.nextSibling = refInternal; 433 prev.nextSibling = newInternal; 434 refInternal.previousSibling = newInternal; 435 newInternal.previousSibling = prev; 436 } 437 } 438 } 439 440 changed(); 441 442 // update cached length if we have any 443 if (fNodeListCache != null) { 444 if (fNodeListCache.fLength != -1) { 445 fNodeListCache.fLength++; 446 } 447 if (fNodeListCache.fChildIndex != -1) { 448 // if we happen to insert just before the cached node, update 449 // the cache to the new node to match the cached index 450 if (fNodeListCache.fChild == refInternal) { 451 fNodeListCache.fChild = newInternal; 452 } else { 453 // otherwise just invalidate the cache 454 fNodeListCache.fChildIndex = -1; 455 } 456 } 457 } 458 459 // notify document 460 ownerDocument.insertedNode(this, newInternal, replace); 461 462 checkNormalizationAfterInsert(newInternal); 463 464 return newChild; 465 466 } // internalInsertBefore(Node,Node,boolean):Node 467 468 /** 469 * Remove a child from this Node. The removed child's subtree 470 * remains intact so it may be re-inserted elsewhere. 471 * 472 * @return oldChild, in its new state (removed). 473 * 474 * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of 475 * this node. 476 * 477 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is 478 * read-only. 479 */ 480 public Node removeChild(Node oldChild) 481 throws DOMException { 482 // Tail-call, should be optimizable 483 return internalRemoveChild(oldChild, false); 484 } // removeChild(Node) :Node 485 486 /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able 487 * to control which mutation events are spawned. This version of the 488 * removeChild operation allows us to do so. It is not intended 489 * for use by application programs. 490 */ 491 Node internalRemoveChild(Node oldChild, boolean replace) 492 throws DOMException { 493 494 CoreDocumentImpl ownerDocument = ownerDocument(); 495 if (ownerDocument.errorChecking) { 496 if (isReadOnly()) { 497 throw new DOMException( 498 DOMException.NO_MODIFICATION_ALLOWED_ERR, 499 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null)); 500 } 501 if (oldChild != null && oldChild.getParentNode() != this) { 502 throw new DOMException(DOMException.NOT_FOUND_ERR, 503 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null)); 504 } 505 } 506 507 ChildNode oldInternal = (ChildNode) oldChild; 508 509 // notify document 510 ownerDocument.removingNode(this, oldInternal, replace); 511 512 // update cached length if we have any 513 if (fNodeListCache != null) { 514 if (fNodeListCache.fLength != -1) { 515 fNodeListCache.fLength--; 516 } 517 if (fNodeListCache.fChildIndex != -1) { 518 // if the removed node is the cached node 519 // move the cache to its (soon former) previous sibling 520 if (fNodeListCache.fChild == oldInternal) { 521 fNodeListCache.fChildIndex--; 522 fNodeListCache.fChild = oldInternal.previousSibling(); 523 } else { 524 // otherwise just invalidate the cache 525 fNodeListCache.fChildIndex = -1; 526 } 527 } 528 } 529 530 // Patch linked list around oldChild 531 // Note: lastChild == firstChild.previousSibling 532 if (oldInternal == firstChild) { 533 // removing first child 534 oldInternal.isFirstChild(false); 535 firstChild = oldInternal.nextSibling; 536 if (firstChild != null) { 537 firstChild.isFirstChild(true); 538 firstChild.previousSibling = oldInternal.previousSibling; 539 } 540 } else { 541 ChildNode prev = oldInternal.previousSibling; 542 ChildNode next = oldInternal.nextSibling; 543 prev.nextSibling = next; 544 if (next == null) { 545 // removing last child 546 firstChild.previousSibling = prev; 547 } else { 548 // removing some other child in the middle 549 next.previousSibling = prev; 550 } 551 } 552 553 // Save previous sibling for normalization checking. 554 ChildNode oldPreviousSibling = oldInternal.previousSibling(); 555 556 // Remove oldInternal's references to tree 557 oldInternal.ownerNode = ownerDocument; 558 oldInternal.isOwned(false); 559 oldInternal.nextSibling = null; 560 oldInternal.previousSibling = null; 561 562 changed(); 563 564 // notify document 565 ownerDocument.removedNode(this, replace); 566 567 checkNormalizationAfterRemove(oldPreviousSibling); 568 569 return oldInternal; 570 571 } // internalRemoveChild(Node,boolean):Node 572 573 /** 574 * Make newChild occupy the location that oldChild used to 575 * have. Note that newChild will first be removed from its previous 576 * parent, if any. Equivalent to inserting newChild before oldChild, 577 * then removing oldChild. 578 * 579 * @return oldChild, in its new state (removed). 580 * 581 * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a 582 * type that shouldn't be a child of this node, or if newChild is 583 * one of our ancestors. 584 * 585 * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a 586 * different owner document than we do. 587 * 588 * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of 589 * this node. 590 * 591 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is 592 * read-only. 593 */ 594 public Node replaceChild(Node newChild, Node oldChild) 595 throws DOMException { 596 // If Mutation Events are being generated, this operation might 597 // throw aggregate events twice when modifying an Attr -- once 598 // on insertion and once on removal. DOM Level 2 does not specify 599 // this as either desirable or undesirable, but hints that 600 // aggregations should be issued only once per user request. 601 602 // notify document 603 ownerDocument.replacingNode(this); 604 605 internalInsertBefore(newChild, oldChild, true); 606 if (newChild != oldChild) { 607 internalRemoveChild(oldChild, true); 608 } 609 610 // notify document 611 ownerDocument.replacedNode(this); 612 613 return oldChild; 614 } 615 616 /* 617 * Get Node text content 618 * @since DOM Level 3 619 */ 620 public String getTextContent() throws DOMException { 621 Node child = getFirstChild(); 622 if (child != null) { 623 Node next = child.getNextSibling(); 624 if (next == null) { 625 return hasTextContent(child) ? ((NodeImpl) child).getTextContent() : ""; 626 } 627 if (fBufferStr == null){ 628 fBufferStr = new StringBuffer(); 629 } 630 else { 631 fBufferStr.setLength(0); 632 } 633 getTextContent(fBufferStr); 634 return fBufferStr.toString(); 635 } 636 return ""; 637 } 638 639 // internal method taking a StringBuffer in parameter 640 void getTextContent(StringBuffer buf) throws DOMException { 641 Node child = getFirstChild(); 642 while (child != null) { 643 if (hasTextContent(child)) { 644 ((NodeImpl) child).getTextContent(buf); 645 } 646 child = child.getNextSibling(); 647 } 648 } 649 650 // internal method returning whether to take the given node's text content 651 final boolean hasTextContent(Node child) { 652 return child.getNodeType() != Node.COMMENT_NODE && 653 child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE && 654 (child.getNodeType() != Node.TEXT_NODE || 655 ((TextImpl) child).isIgnorableWhitespace() == false); 656 } 657 658 /* 659 * Set Node text content 660 * @since DOM Level 3 661 */ 662 public void setTextContent(String textContent) 663 throws DOMException { 664 // get rid of any existing children 665 Node child; 666 while ((child = getFirstChild()) != null) { 667 removeChild(child); 668 } 669 // create a Text node to hold the given content 670 if (textContent != null && textContent.length() != 0){ 671 appendChild(ownerDocument().createTextNode(textContent)); 672 } 673 } 674 675 // 676 // NodeList methods 677 // 678 679 /** 680 * Count the immediate children of this node. Use to implement 681 * NodeList.getLength(). 682 * @return int 683 */ 684 private int nodeListGetLength() { 685 686 if (fNodeListCache == null) { 687 // get rid of trivial cases 688 if (firstChild == null) { 689 return 0; 690 } 691 if (firstChild == lastChild()) { 692 return 1; 693 } 694 // otherwise request a cache object 695 fNodeListCache = ownerDocument.getNodeListCache(this); 696 } 697 if (fNodeListCache.fLength == -1) { // is the cached length invalid ? 698 int l; 699 ChildNode n; 700 // start from the cached node if we have one 701 if (fNodeListCache.fChildIndex != -1 && 702 fNodeListCache.fChild != null) { 703 l = fNodeListCache.fChildIndex; 704 n = fNodeListCache.fChild; 705 } else { 706 n = firstChild; 707 l = 0; 708 } 709 while (n != null) { 710 l++; 711 n = n.nextSibling; 712 } 713 fNodeListCache.fLength = l; 714 } 715 716 return fNodeListCache.fLength; 717 718 } // nodeListGetLength():int 719 720 /** 721 * NodeList method: Count the immediate children of this node 722 * @return int 723 */ 724 public int getLength() { 725 return nodeListGetLength(); 726 } 727 728 /** 729 * Return the Nth immediate child of this node, or null if the index is 730 * out of bounds. Use to implement NodeList.item(). 731 * @param index int 732 */ 733 private Node nodeListItem(int index) { 734 735 if (fNodeListCache == null) { 736 // get rid of trivial case 737 if (firstChild == lastChild()) { 738 return index == 0 ? firstChild : null; 739 } 740 // otherwise request a cache object 741 fNodeListCache = ownerDocument.getNodeListCache(this); 742 } 743 int i = fNodeListCache.fChildIndex; 744 ChildNode n = fNodeListCache.fChild; 745 boolean firstAccess = true; 746 // short way 747 if (i != -1 && n != null) { 748 firstAccess = false; 749 if (i < index) { 750 while (i < index && n != null) { 751 i++; 752 n = n.nextSibling; 753 } 754 } 755 else if (i > index) { 756 while (i > index && n != null) { 757 i--; 758 n = n.previousSibling(); 759 } 760 } 761 } 762 else { 763 // long way 764 if (index < 0) { 765 return null; 766 } 767 n = firstChild; 768 for (i = 0; i < index && n != null; i++) { 769 n = n.nextSibling; 770 } 771 } 772 773 // release cache if reaching last child or first child 774 if (!firstAccess && (n == firstChild || n == lastChild())) { 775 fNodeListCache.fChildIndex = -1; 776 fNodeListCache.fChild = null; 777 ownerDocument.freeNodeListCache(fNodeListCache); 778 // we can keep using the cache until it is actually reused 779 // fNodeListCache will be nulled by the pool (document) if that 780 // happens. 781 // fNodeListCache = null; 782 } 783 else { 784 // otherwise update it 785 fNodeListCache.fChildIndex = i; 786 fNodeListCache.fChild = n; 787 } 788 return n; 789 790 } // nodeListItem(int):Node 791 792 /** 793 * NodeList method: Return the Nth immediate child of this node, or 794 * null if the index is out of bounds. 795 * @return org.w3c.dom.Node 796 * @param index int 797 */ 798 public Node item(int index) { 799 return nodeListItem(index); 800 } // item(int):Node 801 802 /** 803 * Create a NodeList to access children that is use by subclass elements 804 * that have methods named getLength() or item(int). ChildAndParentNode 805 * optimizes getChildNodes() by implementing NodeList itself. However if 806 * a subclass Element implements methods with the same name as the NodeList 807 * methods, they will override the actually methods in this class. 808 * <p> 809 * To use this method, the subclass should implement getChildNodes() and 810 * have it call this method. The resulting NodeList instance maybe 811 * shared and cached in a transient field, but the cached value must be 812 * cleared if the node is cloned. 813 */ 814 protected final NodeList getChildNodesUnoptimized() { 815 if (needsSyncChildren()) { 816 synchronizeChildren(); 817 } 818 return new NodeList() { 819 /** 820 * @see NodeList.getLength() 821 */ 822 public int getLength() { 823 return nodeListGetLength(); 824 } // getLength():int 825 826 /** 827 * @see NodeList.item(int) 828 */ 829 public Node item(int index) { 830 return nodeListItem(index); 831 } // item(int):Node 832 }; 833 } // getChildNodesUnoptimized():NodeList 834 835 // 836 // DOM2: methods, getters, setters 837 // 838 839 /** 840 * Override default behavior to call normalize() on this Node's 841 * children. It is up to implementors or Node to override normalize() 842 * to take action. 843 */ 844 public void normalize() { 845 // No need to normalize if already normalized. 846 if (isNormalized()) { 847 return; 848 } 849 if (needsSyncChildren()) { 850 synchronizeChildren(); 851 } 852 ChildNode kid; 853 for (kid = firstChild; kid != null; kid = kid.nextSibling) { 854 kid.normalize(); 855 } 856 isNormalized(true); 857 } 858 859 /** 860 * DOM Level 3 WD- Experimental. 861 * Override inherited behavior from NodeImpl to support deep equal. 862 */ 863 public boolean isEqualNode(Node arg) { 864 if (!super.isEqualNode(arg)) { 865 return false; 866 } 867 // there are many ways to do this test, and there isn't any way 868 // better than another. Performance may vary greatly depending on 869 // the implementations involved. This one should work fine for us. 870 Node child1 = getFirstChild(); 871 Node child2 = arg.getFirstChild(); 872 while (child1 != null && child2 != null) { 873 if (!((NodeImpl) child1).isEqualNode(child2)) { 874 return false; 875 } 876 child1 = child1.getNextSibling(); 877 child2 = child2.getNextSibling(); 878 } 879 if (child1 != child2) { 880 return false; 881 } 882 return true; 883 } 884 885 // 886 // Public methods 887 // 888 889 /** 890 * Override default behavior so that if deep is true, children are also 891 * toggled. 892 * @see Node 893 * <P> 894 * Note: this will not change the state of an EntityReference or its 895 * children, which are always read-only. 896 */ 897 public void setReadOnly(boolean readOnly, boolean deep) { 898 899 super.setReadOnly(readOnly, deep); 900 901 if (deep) { 902 903 if (needsSyncChildren()) { 904 synchronizeChildren(); 905 } 906 907 // Recursively set kids 908 for (ChildNode mykid = firstChild; 909 mykid != null; 910 mykid = mykid.nextSibling) { 911 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) { 912 mykid.setReadOnly(readOnly,true); 913 } 914 } 915 } 916 } // setReadOnly(boolean,boolean) 917 918 // 919 // Protected methods 920 // 921 922 /** 923 * Override this method in subclass to hook in efficient 924 * internal data structure. 925 */ 926 protected void synchronizeChildren() { 927 // By default just change the flag to avoid calling this method again 928 needsSyncChildren(false); 929 } 930 931 /** 932 * Checks the normalized state of this node after inserting a child. 933 * If the inserted child causes this node to be unnormalized, then this 934 * node is flagged accordingly. 935 * The conditions for changing the normalized state are: 936 * <ul> 937 * <li>The inserted child is a text node and one of its adjacent siblings 938 * is also a text node. 939 * <li>The inserted child is is itself unnormalized. 940 * </ul> 941 * 942 * @param insertedChild the child node that was inserted into this node 943 * 944 * @throws NullPointerException if the inserted child is <code>null</code> 945 */ 946 void checkNormalizationAfterInsert(ChildNode insertedChild) { 947 // See if insertion caused this node to be unnormalized. 948 if (insertedChild.getNodeType() == Node.TEXT_NODE) { 949 ChildNode prev = insertedChild.previousSibling(); 950 ChildNode next = insertedChild.nextSibling; 951 // If an adjacent sibling of the new child is a text node, 952 // flag this node as unnormalized. 953 if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) || 954 (next != null && next.getNodeType() == Node.TEXT_NODE)) { 955 isNormalized(false); 956 } 957 } 958 else { 959 // If the new child is not normalized, 960 // then this node is inherently not normalized. 961 if (!insertedChild.isNormalized()) { 962 isNormalized(false); 963 } 964 } 965 } // checkNormalizationAfterInsert(ChildNode) 966 967 /** 968 * Checks the normalized of this node after removing a child. 969 * If the removed child causes this node to be unnormalized, then this 970 * node is flagged accordingly. 971 * The conditions for changing the normalized state are: 972 * <ul> 973 * <li>The removed child had two adjacent siblings that were text nodes. 974 * </ul> 975 * 976 * @param previousSibling the previous sibling of the removed child, or 977 * <code>null</code> 978 */ 979 void checkNormalizationAfterRemove(ChildNode previousSibling) { 980 // See if removal caused this node to be unnormalized. 981 // If the adjacent siblings of the removed child were both text nodes, 982 // flag this node as unnormalized. 983 if (previousSibling != null && 984 previousSibling.getNodeType() == Node.TEXT_NODE) { 985 986 ChildNode next = previousSibling.nextSibling; 987 if (next != null && next.getNodeType() == Node.TEXT_NODE) { 988 isNormalized(false); 989 } 990 } 991 } // checkNormalizationAfterRemove(Node) 992 993 // 994 // Serialization methods 995 // 996 997 /** Serialize object. */ 998 private void writeObject(ObjectOutputStream out) throws IOException { 999 1000 // synchronize chilren 1001 if (needsSyncChildren()) { 1002 synchronizeChildren(); 1003 } 1004 // write object 1005 out.defaultWriteObject(); 1006 1007 } // writeObject(ObjectOutputStream) 1008 1009 /** Deserialize object. */ 1010 private void readObject(ObjectInputStream ois) 1011 throws ClassNotFoundException, IOException { 1012 1013 // perform default deseralization 1014 ois.defaultReadObject(); 1015 1016 // hardset synchildren - so we don't try to sync - it does not make any 1017 // sense to try to synchildren when we just deserialize object. 1018 needsSyncChildren(false); 1019 1020 } // readObject(ObjectInputStream) 1021 1022 /* 1023 * a class to store some user data along with its handler 1024 */ 1025 protected class UserDataRecord implements Serializable { 1026 /** Serialization version. */ 1027 private static final long serialVersionUID = 3258126977134310455L; 1028 1029 Object fData; 1030 UserDataHandler fHandler; 1031 UserDataRecord(Object data, UserDataHandler handler) { 1032 fData = data; 1033 fHandler = handler; 1034 } 1035 } 1036 } // class ParentNode