Save This Page
Home » openjdk-7 » com.sun.org.apache.xerces.internal » dom » [javadoc | source]
    1   /*
    2    * reserved comment block
    3    * DO NOT REMOVE OR ALTER!
    4    */
    5   /*
    6    * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
    7    *
    8    * Licensed under the Apache License, Version 2.0 (the "License");
    9    * you may not use this file except in compliance with the License.
   10    * 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.Serializable;
   24   import java.io.IOException;
   25   import java.io.ObjectInputStream;
   26   import java.io.ObjectOutputStream;
   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               boolean treeSafe = true;
  372               for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
  373               {
  374                   treeSafe = newChild != a;
  375               }
  376               if(!treeSafe) {
  377                   throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
  378                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
  379               }
  380           }
  381   
  382           // notify document
  383           ownerDocument.insertingNode(this, replace);
  384   
  385           // Convert to internal type, to avoid repeated casting
  386           ChildNode newInternal = (ChildNode)newChild;
  387   
  388           Node oldparent = newInternal.parentNode();
  389           if (oldparent != null) {
  390               oldparent.removeChild(newInternal);
  391           }
  392   
  393           // Convert to internal type, to avoid repeated casting
  394           ChildNode refInternal = (ChildNode)refChild;
  395   
  396           // Attach up
  397           newInternal.ownerNode = this;
  398           newInternal.isOwned(true);
  399   
  400           // Attach before and after
  401           // Note: firstChild.previousSibling == lastChild!!
  402           if (firstChild == null) {
  403               // this our first and only child
  404               firstChild = newInternal;
  405               newInternal.isFirstChild(true);
  406               newInternal.previousSibling = newInternal;
  407           }
  408           else {
  409               if (refInternal == null) {
  410                   // this is an append
  411                   ChildNode lastChild = firstChild.previousSibling;
  412                   lastChild.nextSibling = newInternal;
  413                   newInternal.previousSibling = lastChild;
  414                   firstChild.previousSibling = newInternal;
  415               }
  416               else {
  417                   // this is an insert
  418                   if (refChild == firstChild) {
  419                       // at the head of the list
  420                       firstChild.isFirstChild(false);
  421                       newInternal.nextSibling = firstChild;
  422                       newInternal.previousSibling = firstChild.previousSibling;
  423                       firstChild.previousSibling = newInternal;
  424                       firstChild = newInternal;
  425                       newInternal.isFirstChild(true);
  426                   }
  427                   else {
  428                       // somewhere in the middle
  429                       ChildNode prev = refInternal.previousSibling;
  430                       newInternal.nextSibling = refInternal;
  431                       prev.nextSibling = newInternal;
  432                       refInternal.previousSibling = newInternal;
  433                       newInternal.previousSibling = prev;
  434                   }
  435               }
  436           }
  437   
  438           changed();
  439   
  440           // update cached length if we have any
  441           if (fNodeListCache != null) {
  442               if (fNodeListCache.fLength != -1) {
  443                   fNodeListCache.fLength++;
  444               }
  445               if (fNodeListCache.fChildIndex != -1) {
  446                   // if we happen to insert just before the cached node, update
  447                   // the cache to the new node to match the cached index
  448                   if (fNodeListCache.fChild == refInternal) {
  449                       fNodeListCache.fChild = newInternal;
  450                   } else {
  451                       // otherwise just invalidate the cache
  452                       fNodeListCache.fChildIndex = -1;
  453                   }
  454               }
  455           }
  456   
  457           // notify document
  458           ownerDocument.insertedNode(this, newInternal, replace);
  459   
  460           checkNormalizationAfterInsert(newInternal);
  461   
  462           return newChild;
  463   
  464       } // internalInsertBefore(Node,Node,boolean):Node
  465   
  466       /**
  467        * Remove a child from this Node. The removed child's subtree
  468        * remains intact so it may be re-inserted elsewhere.
  469        *
  470        * @return oldChild, in its new state (removed).
  471        *
  472        * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  473        * this node.
  474        *
  475        * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  476        * read-only.
  477        */
  478       public Node removeChild(Node oldChild)
  479           throws DOMException {
  480           // Tail-call, should be optimizable
  481           return internalRemoveChild(oldChild, false);
  482       } // removeChild(Node) :Node
  483   
  484       /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  485        * to control which mutation events are spawned. This version of the
  486        * removeChild operation allows us to do so. It is not intended
  487        * for use by application programs.
  488        */
  489       Node internalRemoveChild(Node oldChild, boolean replace)
  490           throws DOMException {
  491   
  492           CoreDocumentImpl ownerDocument = ownerDocument();
  493           if (ownerDocument.errorChecking) {
  494               if (isReadOnly()) {
  495                   throw new DOMException(
  496                               DOMException.NO_MODIFICATION_ALLOWED_ERR,
  497                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
  498               }
  499               if (oldChild != null && oldChild.getParentNode() != this) {
  500                   throw new DOMException(DOMException.NOT_FOUND_ERR,
  501                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
  502               }
  503           }
  504   
  505           ChildNode oldInternal = (ChildNode) oldChild;
  506   
  507           // notify document
  508           ownerDocument.removingNode(this, oldInternal, replace);
  509   
  510           // update cached length if we have any
  511           if (fNodeListCache != null) {
  512               if (fNodeListCache.fLength != -1) {
  513                   fNodeListCache.fLength--;
  514               }
  515               if (fNodeListCache.fChildIndex != -1) {
  516                   // if the removed node is the cached node
  517                   // move the cache to its (soon former) previous sibling
  518                   if (fNodeListCache.fChild == oldInternal) {
  519                       fNodeListCache.fChildIndex--;
  520                       fNodeListCache.fChild = oldInternal.previousSibling();
  521                   } else {
  522                       // otherwise just invalidate the cache
  523                       fNodeListCache.fChildIndex = -1;
  524                   }
  525               }
  526           }
  527   
  528           // Patch linked list around oldChild
  529           // Note: lastChild == firstChild.previousSibling
  530           if (oldInternal == firstChild) {
  531               // removing first child
  532               oldInternal.isFirstChild(false);
  533               firstChild = oldInternal.nextSibling;
  534               if (firstChild != null) {
  535                   firstChild.isFirstChild(true);
  536                   firstChild.previousSibling = oldInternal.previousSibling;
  537               }
  538           } else {
  539               ChildNode prev = oldInternal.previousSibling;
  540               ChildNode next = oldInternal.nextSibling;
  541               prev.nextSibling = next;
  542               if (next == null) {
  543                   // removing last child
  544                   firstChild.previousSibling = prev;
  545               } else {
  546                   // removing some other child in the middle
  547                   next.previousSibling = prev;
  548               }
  549           }
  550   
  551           // Save previous sibling for normalization checking.
  552           ChildNode oldPreviousSibling = oldInternal.previousSibling();
  553   
  554           // Remove oldInternal's references to tree
  555           oldInternal.ownerNode       = ownerDocument;
  556           oldInternal.isOwned(false);
  557           oldInternal.nextSibling     = null;
  558           oldInternal.previousSibling = null;
  559   
  560           changed();
  561   
  562           // notify document
  563           ownerDocument.removedNode(this, replace);
  564   
  565           checkNormalizationAfterRemove(oldPreviousSibling);
  566   
  567           return oldInternal;
  568   
  569       } // internalRemoveChild(Node,boolean):Node
  570   
  571       /**
  572        * Make newChild occupy the location that oldChild used to
  573        * have. Note that newChild will first be removed from its previous
  574        * parent, if any. Equivalent to inserting newChild before oldChild,
  575        * then removing oldChild.
  576        *
  577        * @return oldChild, in its new state (removed).
  578        *
  579        * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  580        * type that shouldn't be a child of this node, or if newChild is
  581        * one of our ancestors.
  582        *
  583        * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  584        * different owner document than we do.
  585        *
  586        * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  587        * this node.
  588        *
  589        * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  590        * read-only.
  591        */
  592       public Node replaceChild(Node newChild, Node oldChild)
  593           throws DOMException {
  594           // If Mutation Events are being generated, this operation might
  595           // throw aggregate events twice when modifying an Attr -- once
  596           // on insertion and once on removal. DOM Level 2 does not specify
  597           // this as either desirable or undesirable, but hints that
  598           // aggregations should be issued only once per user request.
  599   
  600           // notify document
  601           ownerDocument.replacingNode(this);
  602   
  603           internalInsertBefore(newChild, oldChild, true);
  604           if (newChild != oldChild) {
  605               internalRemoveChild(oldChild, true);
  606           }
  607   
  608           // notify document
  609           ownerDocument.replacedNode(this);
  610   
  611           return oldChild;
  612       }
  613   
  614       /*
  615        * Get Node text content
  616        * @since DOM Level 3
  617        */
  618       public String getTextContent() throws DOMException {
  619           Node child = getFirstChild();
  620           if (child != null) {
  621               Node next = child.getNextSibling();
  622               if (next == null) {
  623                   return hasTextContent(child) ? ((NodeImpl) child).getTextContent() : "";
  624               }
  625               if (fBufferStr == null){
  626                   fBufferStr = new StringBuffer();
  627               }
  628               else {
  629                   fBufferStr.setLength(0);
  630               }
  631               getTextContent(fBufferStr);
  632               return fBufferStr.toString();
  633           }
  634           return "";
  635       }
  636   
  637       // internal method taking a StringBuffer in parameter
  638       void getTextContent(StringBuffer buf) throws DOMException {
  639           Node child = getFirstChild();
  640           while (child != null) {
  641               if (hasTextContent(child)) {
  642                   ((NodeImpl) child).getTextContent(buf);
  643               }
  644               child = child.getNextSibling();
  645           }
  646       }
  647   
  648       // internal method returning whether to take the given node's text content
  649       final boolean hasTextContent(Node child) {
  650           return child.getNodeType() != Node.COMMENT_NODE &&
  651               child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE &&
  652               (child.getNodeType() != Node.TEXT_NODE ||
  653                ((TextImpl) child).isIgnorableWhitespace() == false);
  654       }
  655   
  656       /*
  657        * Set Node text content
  658        * @since DOM Level 3
  659        */
  660       public void setTextContent(String textContent)
  661           throws DOMException {
  662           // get rid of any existing children
  663           Node child;
  664           while ((child = getFirstChild()) != null) {
  665               removeChild(child);
  666           }
  667           // create a Text node to hold the given content
  668           if (textContent != null && textContent.length() != 0){
  669               appendChild(ownerDocument().createTextNode(textContent));
  670           }
  671       }
  672   
  673       //
  674       // NodeList methods
  675       //
  676   
  677       /**
  678        * Count the immediate children of this node.  Use to implement
  679        * NodeList.getLength().
  680        * @return int
  681        */
  682       private int nodeListGetLength() {
  683   
  684           if (fNodeListCache == null) {
  685               // get rid of trivial cases
  686               if (firstChild == null) {
  687                   return 0;
  688               }
  689               if (firstChild == lastChild()) {
  690                   return 1;
  691               }
  692               // otherwise request a cache object
  693               fNodeListCache = ownerDocument.getNodeListCache(this);
  694           }
  695           if (fNodeListCache.fLength == -1) { // is the cached length invalid ?
  696               int l;
  697               ChildNode n;
  698               // start from the cached node if we have one
  699               if (fNodeListCache.fChildIndex != -1 &&
  700                   fNodeListCache.fChild != null) {
  701                   l = fNodeListCache.fChildIndex;
  702                   n = fNodeListCache.fChild;
  703               } else {
  704                   n = firstChild;
  705                   l = 0;
  706               }
  707               while (n != null) {
  708                   l++;
  709                   n = n.nextSibling;
  710               }
  711               fNodeListCache.fLength = l;
  712           }
  713   
  714           return fNodeListCache.fLength;
  715   
  716       } // nodeListGetLength():int
  717   
  718       /**
  719        * NodeList method: Count the immediate children of this node
  720        * @return int
  721        */
  722       public int getLength() {
  723           return nodeListGetLength();
  724       }
  725   
  726       /**
  727        * Return the Nth immediate child of this node, or null if the index is
  728        * out of bounds.  Use to implement NodeList.item().
  729        * @param index int
  730        */
  731       private Node nodeListItem(int index) {
  732   
  733           if (fNodeListCache == null) {
  734               // get rid of trivial case
  735               if (firstChild == lastChild()) {
  736                   return index == 0 ? firstChild : null;
  737               }
  738               // otherwise request a cache object
  739               fNodeListCache = ownerDocument.getNodeListCache(this);
  740           }
  741           int i = fNodeListCache.fChildIndex;
  742           ChildNode n = fNodeListCache.fChild;
  743           boolean firstAccess = true;
  744           // short way
  745           if (i != -1 && n != null) {
  746               firstAccess = false;
  747               if (i < index) {
  748                   while (i < index && n != null) {
  749                       i++;
  750                       n = n.nextSibling;
  751                   }
  752               }
  753               else if (i > index) {
  754                   while (i > index && n != null) {
  755                       i--;
  756                       n = n.previousSibling();
  757                   }
  758               }
  759           }
  760           else {
  761               // long way
  762               if (index < 0) {
  763                   return null;
  764               }
  765               n = firstChild;
  766               for (i = 0; i < index && n != null; i++) {
  767                   n = n.nextSibling;
  768               }
  769           }
  770   
  771           // release cache if reaching last child or first child
  772           if (!firstAccess && (n == firstChild || n == lastChild())) {
  773               fNodeListCache.fChildIndex = -1;
  774               fNodeListCache.fChild = null;
  775               ownerDocument.freeNodeListCache(fNodeListCache);
  776               // we can keep using the cache until it is actually reused
  777               // fNodeListCache will be nulled by the pool (document) if that
  778               // happens.
  779               // fNodeListCache = null;
  780           }
  781           else {
  782               // otherwise update it
  783               fNodeListCache.fChildIndex = i;
  784               fNodeListCache.fChild = n;
  785           }
  786           return n;
  787   
  788       } // nodeListItem(int):Node
  789   
  790       /**
  791        * NodeList method: Return the Nth immediate child of this node, or
  792        * null if the index is out of bounds.
  793        * @return org.w3c.dom.Node
  794        * @param index int
  795        */
  796       public Node item(int index) {
  797           return nodeListItem(index);
  798       } // item(int):Node
  799   
  800       /**
  801        * Create a NodeList to access children that is use by subclass elements
  802        * that have methods named getLength() or item(int).  ChildAndParentNode
  803        * optimizes getChildNodes() by implementing NodeList itself.  However if
  804        * a subclass Element implements methods with the same name as the NodeList
  805        * methods, they will override the actually methods in this class.
  806        * <p>
  807        * To use this method, the subclass should implement getChildNodes() and
  808        * have it call this method.  The resulting NodeList instance maybe
  809        * shared and cached in a transient field, but the cached value must be
  810        * cleared if the node is cloned.
  811        */
  812       protected final NodeList getChildNodesUnoptimized() {
  813           if (needsSyncChildren()) {
  814               synchronizeChildren();
  815           }
  816           return new NodeList() {
  817                   /**
  818                    * @see NodeList.getLength()
  819                    */
  820                   public int getLength() {
  821                       return nodeListGetLength();
  822                   } // getLength():int
  823   
  824                   /**
  825                    * @see NodeList.item(int)
  826                    */
  827                   public Node item(int index) {
  828                       return nodeListItem(index);
  829                   } // item(int):Node
  830               };
  831       } // getChildNodesUnoptimized():NodeList
  832   
  833       //
  834       // DOM2: methods, getters, setters
  835       //
  836   
  837       /**
  838        * Override default behavior to call normalize() on this Node's
  839        * children. It is up to implementors or Node to override normalize()
  840        * to take action.
  841        */
  842       public void normalize() {
  843           // No need to normalize if already normalized.
  844           if (isNormalized()) {
  845               return;
  846           }
  847           if (needsSyncChildren()) {
  848               synchronizeChildren();
  849           }
  850           ChildNode kid;
  851           for (kid = firstChild; kid != null; kid = kid.nextSibling) {
  852               kid.normalize();
  853           }
  854           isNormalized(true);
  855       }
  856   
  857       /**
  858        * DOM Level 3 WD- Experimental.
  859        * Override inherited behavior from NodeImpl to support deep equal.
  860        */
  861       public boolean isEqualNode(Node arg) {
  862           if (!super.isEqualNode(arg)) {
  863               return false;
  864           }
  865           // there are many ways to do this test, and there isn't any way
  866           // better than another. Performance may vary greatly depending on
  867           // the implementations involved. This one should work fine for us.
  868           Node child1 = getFirstChild();
  869           Node child2 = arg.getFirstChild();
  870           while (child1 != null && child2 != null) {
  871               if (!((NodeImpl) child1).isEqualNode(child2)) {
  872                   return false;
  873               }
  874               child1 = child1.getNextSibling();
  875               child2 = child2.getNextSibling();
  876           }
  877           if (child1 != child2) {
  878               return false;
  879           }
  880           return true;
  881       }
  882   
  883       //
  884       // Public methods
  885       //
  886   
  887       /**
  888        * Override default behavior so that if deep is true, children are also
  889        * toggled.
  890        * @see Node
  891        * <P>
  892        * Note: this will not change the state of an EntityReference or its
  893        * children, which are always read-only.
  894        */
  895       public void setReadOnly(boolean readOnly, boolean deep) {
  896   
  897           super.setReadOnly(readOnly, deep);
  898   
  899           if (deep) {
  900   
  901               if (needsSyncChildren()) {
  902                   synchronizeChildren();
  903               }
  904   
  905               // Recursively set kids
  906               for (ChildNode mykid = firstChild;
  907                    mykid != null;
  908                    mykid = mykid.nextSibling) {
  909                   if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
  910                       mykid.setReadOnly(readOnly,true);
  911                   }
  912               }
  913           }
  914       } // setReadOnly(boolean,boolean)
  915   
  916       //
  917       // Protected methods
  918       //
  919   
  920       /**
  921        * Override this method in subclass to hook in efficient
  922        * internal data structure.
  923        */
  924       protected void synchronizeChildren() {
  925           // By default just change the flag to avoid calling this method again
  926           needsSyncChildren(false);
  927       }
  928   
  929       /**
  930        * Checks the normalized state of this node after inserting a child.
  931        * If the inserted child causes this node to be unnormalized, then this
  932        * node is flagged accordingly.
  933        * The conditions for changing the normalized state are:
  934        * <ul>
  935        * <li>The inserted child is a text node and one of its adjacent siblings
  936        * is also a text node.
  937        * <li>The inserted child is is itself unnormalized.
  938        * </ul>
  939        *
  940        * @param insertedChild the child node that was inserted into this node
  941        *
  942        * @throws NullPointerException if the inserted child is <code>null</code>
  943        */
  944       void checkNormalizationAfterInsert(ChildNode insertedChild) {
  945           // See if insertion caused this node to be unnormalized.
  946           if (insertedChild.getNodeType() == Node.TEXT_NODE) {
  947               ChildNode prev = insertedChild.previousSibling();
  948               ChildNode next = insertedChild.nextSibling;
  949               // If an adjacent sibling of the new child is a text node,
  950               // flag this node as unnormalized.
  951               if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
  952                   (next != null && next.getNodeType() == Node.TEXT_NODE)) {
  953                   isNormalized(false);
  954               }
  955           }
  956           else {
  957               // If the new child is not normalized,
  958               // then this node is inherently not normalized.
  959               if (!insertedChild.isNormalized()) {
  960                   isNormalized(false);
  961               }
  962           }
  963       } // checkNormalizationAfterInsert(ChildNode)
  964   
  965       /**
  966        * Checks the normalized of this node after removing a child.
  967        * If the removed child causes this node to be unnormalized, then this
  968        * node is flagged accordingly.
  969        * The conditions for changing the normalized state are:
  970        * <ul>
  971        * <li>The removed child had two adjacent siblings that were text nodes.
  972        * </ul>
  973        *
  974        * @param previousSibling the previous sibling of the removed child, or
  975        * <code>null</code>
  976        */
  977       void checkNormalizationAfterRemove(ChildNode previousSibling) {
  978           // See if removal caused this node to be unnormalized.
  979           // If the adjacent siblings of the removed child were both text nodes,
  980           // flag this node as unnormalized.
  981           if (previousSibling != null &&
  982               previousSibling.getNodeType() == Node.TEXT_NODE) {
  983   
  984               ChildNode next = previousSibling.nextSibling;
  985               if (next != null && next.getNodeType() == Node.TEXT_NODE) {
  986                   isNormalized(false);
  987               }
  988           }
  989       } // checkNormalizationAfterRemove(Node)
  990   
  991       //
  992       // Serialization methods
  993       //
  994   
  995       /** Serialize object. */
  996       private void writeObject(ObjectOutputStream out) throws IOException {
  997   
  998           // synchronize chilren
  999           if (needsSyncChildren()) {
 1000               synchronizeChildren();
 1001           }
 1002           // write object
 1003           out.defaultWriteObject();
 1004   
 1005       } // writeObject(ObjectOutputStream)
 1006   
 1007       /** Deserialize object. */
 1008       private void readObject(ObjectInputStream ois)
 1009           throws ClassNotFoundException, IOException {
 1010   
 1011           // perform default deseralization
 1012           ois.defaultReadObject();
 1013   
 1014           // hardset synchildren - so we don't try to sync - it does not make any
 1015           // sense to try to synchildren when we just deserialize object.
 1016           needsSyncChildren(false);
 1017   
 1018       } // readObject(ObjectInputStream)
 1019   
 1020       /*
 1021        * a class to store some user data along with its handler
 1022        */
 1023       class UserDataRecord implements Serializable {
 1024           /** Serialization version. */
 1025           private static final long serialVersionUID = 3258126977134310455L;
 1026   
 1027           Object fData;
 1028           UserDataHandler fHandler;
 1029           UserDataRecord(Object data, UserDataHandler handler) {
 1030               fData = data;
 1031               fHandler = handler;
 1032           }
 1033       }
 1034   } // class ParentNode

Save This Page
Home » openjdk-7 » com.sun.org.apache.xerces.internal » dom » [javadoc | source]