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