KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > dom > ParentNode


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package com.sun.org.apache.xerces.internal.dom;
59
60 import java.io.Serializable JavaDoc;
61 import java.io.IOException JavaDoc;
62 import java.io.ObjectInputStream JavaDoc;
63 import java.io.ObjectOutputStream JavaDoc;
64
65 import org.w3c.dom.DOMException JavaDoc;
66 import org.w3c.dom.Document JavaDoc;
67 import org.w3c.dom.Node JavaDoc;
68 import org.w3c.dom.NodeList JavaDoc;
69 import org.w3c.dom.UserDataHandler JavaDoc;
70
71 /**
72  * ParentNode inherits from ChildNode and adds the capability of having child
73  * nodes. Not every node in the DOM can have children, so only nodes that can
74  * should inherit from this class and pay the price for it.
75  * <P>
76  * ParentNode, just like NodeImpl, also implements NodeList, so it can
77  * return itself in response to the getChildNodes() query. This eliminiates
78  * the need for a separate ChildNodeList object. Note that this is an
79  * IMPLEMENTATION DETAIL; applications should _never_ assume that
80  * this identity exists. On the other hand, subclasses may need to override
81  * this, in case of conflicting names. This is the case for the classes
82  * HTMLSelectElementImpl and HTMLFormElementImpl of the HTML DOM.
83  * <P>
84  * While we have a direct reference to the first child, the last child is
85  * stored as the previous sibling of the first child. First child nodes are
86  * marked as being so, and getNextSibling hides this fact.
87  * <P>Note: Not all parent nodes actually need to also be a child. At some
88  * point we used to have ParentNode inheriting from NodeImpl and another class
89  * called ChildAndParentNode that inherited from ChildNode. But due to the lack
90  * of multiple inheritance a lot of code had to be duplicated which led to a
91  * maintenance nightmare. At the same time only a few nodes (Document,
92  * DocumentFragment, Entity, and Attribute) cannot be a child so the gain in
93  * memory wasn't really worth it. The only type for which this would be the
94  * case is Attribute, but we deal with there in another special way, so this is
95  * not applicable.
96  * <p>
97  * This class doesn't directly support mutation events, however, it notifies
98  * the document when mutations are performed so that the document class do so.
99  *
100  * <p><b>WARNING</b>: Some of the code here is partially duplicated in
101  * AttrImpl, be careful to keep these two classes in sync!
102  *
103  * @author Arnaud Le Hors, IBM
104  * @author Joe Kesselman, IBM
105  * @author Andy Clark, IBM
106  * @version $Id: ParentNode.java,v 1.41 2004/02/10 17:09:45 elena Exp $
107  */

108 public abstract class ParentNode
109     extends ChildNode {
110
111     /** Serialization version. */
112     static final long serialVersionUID = 2815829867152120872L;
113
114     /** Owner document. */
115     protected CoreDocumentImpl ownerDocument;
116
117     /** First child. */
118     protected ChildNode firstChild = null;
119
120     // transients
121

122     /** NodeList cache */
123     protected transient NodeListCache fNodeListCache = null;
124
125     //
126
// Constructors
127
//
128

129     /**
130      * No public constructor; only subclasses of ParentNode should be
131      * instantiated, and those normally via a Document's factory methods
132      */

133     protected ParentNode(CoreDocumentImpl ownerDocument) {
134         super(ownerDocument);
135         this.ownerDocument = ownerDocument;
136     }
137
138     /** Constructor for serialization. */
139     public ParentNode() {}
140
141     //
142
// NodeList methods
143
//
144

145     /**
146      * Returns a duplicate of a given node. You can consider this a
147      * generic "copy constructor" for nodes. The newly returned object should
148      * be completely independent of the source object's subtree, so changes
149      * in one after the clone has been made will not affect the other.
150      * <p>
151      * Example: Cloning a Text node will copy both the node and the text it
152      * contains.
153      * <p>
154      * Example: Cloning something that has children -- Element or Attr, for
155      * example -- will _not_ clone those children unless a "deep clone"
156      * has been requested. A shallow clone of an Attr node will yield an
157      * empty Attr of the same name.
158      * <p>
159      * NOTE: Clones will always be read/write, even if the node being cloned
160      * is read-only, to permit applications using only the DOM API to obtain
161      * editable copies of locked portions of the tree.
162      */

163     public Node JavaDoc cloneNode(boolean deep) {
164         
165         if (needsSyncChildren()) {
166             synchronizeChildren();
167         }
168         ParentNode newnode = (ParentNode) super.cloneNode(deep);
169
170         // set owner document
171
newnode.ownerDocument = ownerDocument;
172
173         // Need to break the association w/ original kids
174
newnode.firstChild = null;
175
176         // invalidate cache for children NodeList
177
newnode.fNodeListCache = null;
178
179         // Then, if deep, clone the kids too.
180
if (deep) {
181             for (ChildNode child = firstChild;
182                  child != null;
183                  child = child.nextSibling) {
184                 newnode.appendChild(child.cloneNode(true));
185             }
186         }
187
188         return newnode;
189
190     } // cloneNode(boolean):Node
191

192     /**
193      * Find the Document that this Node belongs to (the document in
194      * whose context the Node was created). The Node may or may not
195      * currently be part of that Document's actual contents.
196      */

197     public Document JavaDoc getOwnerDocument() {
198         return ownerDocument;
199     }
200
201     /**
202      * same as above but returns internal type and this one is not overridden
203      * by CoreDocumentImpl to return null
204      */

205     CoreDocumentImpl ownerDocument() {
206         return ownerDocument;
207     }
208
209     /**
210      * NON-DOM
211      * set the ownerDocument of this node and its children
212      */

213     void setOwnerDocument(CoreDocumentImpl doc) {
214         if (needsSyncChildren()) {
215             synchronizeChildren();
216         }
217         super.setOwnerDocument(doc);
218         ownerDocument = doc;
219     for (ChildNode child = firstChild;
220          child != null; child = child.nextSibling) {
221         child.setOwnerDocument(doc);
222     }
223     }
224
225     /**
226      * Test whether this node has any children. Convenience shorthand
227      * for (Node.getFirstChild()!=null)
228      */

229     public boolean hasChildNodes() {
230         if (needsSyncChildren()) {
231             synchronizeChildren();
232         }
233         return firstChild != null;
234     }
235
236     /**
237      * Obtain a NodeList enumerating all children of this node. If there
238      * are none, an (initially) empty NodeList is returned.
239      * <p>
240      * NodeLists are "live"; as children are added/removed the NodeList
241      * will immediately reflect those changes. Also, the NodeList refers
242      * to the actual nodes, so changes to those nodes made via the DOM tree
243      * will be reflected in the NodeList and vice versa.
244      * <p>
245      * In this implementation, Nodes implement the NodeList interface and
246      * provide their own getChildNodes() support. Other DOMs may solve this
247      * differently.
248      */

249     public NodeList JavaDoc getChildNodes() {
250
251         if (needsSyncChildren()) {
252             synchronizeChildren();
253         }
254         return this;
255
256     } // getChildNodes():NodeList
257

258     /** The first child of this Node, or null if none. */
259     public Node JavaDoc getFirstChild() {
260
261         if (needsSyncChildren()) {
262             synchronizeChildren();
263         }
264         return firstChild;
265
266     } // getFirstChild():Node
267

268     /** The last child of this Node, or null if none. */
269     public Node JavaDoc getLastChild() {
270
271         if (needsSyncChildren()) {
272             synchronizeChildren();
273         }
274         return lastChild();
275
276     } // getLastChild():Node
277

278     final ChildNode lastChild() {
279         // last child is stored as the previous sibling of first child
280
return firstChild != null ? firstChild.previousSibling : null;
281     }
282
283     final void lastChild(ChildNode node) {
284         // store lastChild as previous sibling of first child
285
if (firstChild != null) {
286             firstChild.previousSibling = node;
287         }
288     }
289
290     /**
291      * Move one or more node(s) to our list of children. Note that this
292      * implicitly removes them from their previous parent.
293      *
294      * @param newChild The Node to be moved to our subtree. As a
295      * convenience feature, inserting a DocumentNode will instead insert
296      * all its children.
297      *
298      * @param refChild Current child which newChild should be placed
299      * immediately before. If refChild is null, the insertion occurs
300      * after all existing Nodes, like appendChild().
301      *
302      * @return newChild, in its new state (relocated, or emptied in the case of
303      * DocumentNode.)
304      *
305      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
306      * type that shouldn't be a child of this node, or if newChild is an
307      * ancestor of this node.
308      *
309      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
310      * different owner document than we do.
311      *
312      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
313      * this node.
314      *
315      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
316      * read-only.
317      */

318     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
319         throws DOMException JavaDoc {
320         // Tail-call; optimizer should be able to do good things with.
321
return internalInsertBefore(newChild, refChild, false);
322     } // insertBefore(Node,Node):Node
323

324     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
325      * to control which mutation events are spawned. This version of the
326      * insertBefore operation allows us to do so. It is not intended
327      * for use by application programs.
328      */

329     Node JavaDoc internalInsertBefore(Node JavaDoc newChild, Node JavaDoc refChild, boolean replace)
330         throws DOMException JavaDoc {
331
332         boolean errorChecking = ownerDocument.errorChecking;
333
334         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
335             // SLOW BUT SAFE: We could insert the whole subtree without
336
// juggling so many next/previous pointers. (Wipe out the
337
// parent's child-list, patch the parent pointers, set the
338
// ends of the list.) But we know some subclasses have special-
339
// case behavior they add to insertBefore(), so we don't risk it.
340
// This approch also takes fewer bytecodes.
341

342             // NOTE: If one of the children is not a legal child of this
343
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
344
// have been transferred. (Alternative behaviors would be to
345
// reparent up to the first failure point or reparent all those
346
// which are acceptable to the target node, neither of which is
347
// as robust. PR-DOM-0818 isn't entirely clear on which it
348
// recommends?????
349

350             // No need to check kids for right-document; if they weren't,
351
// they wouldn't be kids of that DocFrag.
352
if (errorChecking) {
353                 for (Node JavaDoc kid = newChild.getFirstChild(); // Prescan
354
kid != null; kid = kid.getNextSibling()) {
355
356                     if (!ownerDocument.isKidOK(this, kid)) {
357                         throw new DOMException JavaDoc(
358                               DOMException.HIERARCHY_REQUEST_ERR,
359                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
360                     }
361                 }
362             }
363
364             while (newChild.hasChildNodes()) {
365                 insertBefore(newChild.getFirstChild(), refChild);
366             }
367             return newChild;
368         }
369
370         if (newChild == refChild) {
371             // stupid case that must be handled as a no-op triggering events...
372
refChild = refChild.getNextSibling();
373             removeChild(newChild);
374             insertBefore(newChild, refChild);
375             return newChild;
376         }
377
378         if (needsSyncChildren()) {
379             synchronizeChildren();
380         }
381
382         if (errorChecking) {
383             if (isReadOnly()) {
384                 throw new DOMException JavaDoc(
385                               DOMException.NO_MODIFICATION_ALLOWED_ERR,
386                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
387             }
388             if (newChild.getOwnerDocument() != ownerDocument && newChild != ownerDocument) {
389                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
390                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
391             }
392             if (!ownerDocument.isKidOK(this, newChild)) {
393                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
394                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
395             }
396             // refChild must be a child of this node (or null)
397
if (refChild != null && refChild.getParentNode() != this) {
398                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
399                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
400             }
401
402             // Prevent cycles in the tree
403
// newChild cannot be ancestor of this Node,
404
// and actually cannot be this
405
boolean treeSafe = true;
406             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
407             {
408                 treeSafe = newChild != a;
409             }
410             if(!treeSafe) {
411                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
412                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
413             }
414         }
415
416         // notify document
417
ownerDocument.insertingNode(this, replace);
418
419         // Convert to internal type, to avoid repeated casting
420
ChildNode newInternal = (ChildNode)newChild;
421
422         Node JavaDoc oldparent = newInternal.parentNode();
423         if (oldparent != null) {
424             oldparent.removeChild(newInternal);
425         }
426
427         // Convert to internal type, to avoid repeated casting
428
ChildNode refInternal = (ChildNode)refChild;
429
430         // Attach up
431
newInternal.ownerNode = this;
432         newInternal.isOwned(true);
433
434         // Attach before and after
435
// Note: firstChild.previousSibling == lastChild!!
436
if (firstChild == null) {
437             // this our first and only child
438
firstChild = newInternal;
439             newInternal.isFirstChild(true);
440             newInternal.previousSibling = newInternal;
441         }
442         else {
443             if (refInternal == null) {
444                 // this is an append
445
ChildNode lastChild = firstChild.previousSibling;
446                 lastChild.nextSibling = newInternal;
447                 newInternal.previousSibling = lastChild;
448                 firstChild.previousSibling = newInternal;
449             }
450             else {
451                 // this is an insert
452
if (refChild == firstChild) {
453                     // at the head of the list
454
firstChild.isFirstChild(false);
455                     newInternal.nextSibling = firstChild;
456                     newInternal.previousSibling = firstChild.previousSibling;
457                     firstChild.previousSibling = newInternal;
458                     firstChild = newInternal;
459                     newInternal.isFirstChild(true);
460                 }
461                 else {
462                     // somewhere in the middle
463
ChildNode prev = refInternal.previousSibling;
464                     newInternal.nextSibling = refInternal;
465                     prev.nextSibling = newInternal;
466                     refInternal.previousSibling = newInternal;
467                     newInternal.previousSibling = prev;
468                 }
469             }
470         }
471
472         changed();
473
474         // update cached length if we have any
475
if (fNodeListCache != null) {
476             if (fNodeListCache.fLength != -1) {
477                 fNodeListCache.fLength++;
478             }
479             if (fNodeListCache.fChildIndex != -1) {
480                 // if we happen to insert just before the cached node, update
481
// the cache to the new node to match the cached index
482
if (fNodeListCache.fChild == refInternal) {
483                     fNodeListCache.fChild = newInternal;
484                 } else {
485                     // otherwise just invalidate the cache
486
fNodeListCache.fChildIndex = -1;
487                 }
488             }
489         }
490
491         // notify document
492
ownerDocument.insertedNode(this, newInternal, replace);
493
494         checkNormalizationAfterInsert(newInternal);
495
496         return newChild;
497
498     } // internalInsertBefore(Node,Node,boolean):Node
499

500     /**
501      * Remove a child from this Node. The removed child's subtree
502      * remains intact so it may be re-inserted elsewhere.
503      *
504      * @return oldChild, in its new state (removed).
505      *
506      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
507      * this node.
508      *
509      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
510      * read-only.
511      */

512     public Node JavaDoc removeChild(Node JavaDoc oldChild)
513         throws DOMException JavaDoc {
514         // Tail-call, should be optimizable
515
return internalRemoveChild(oldChild, false);
516     } // removeChild(Node) :Node
517

518     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
519      * to control which mutation events are spawned. This version of the
520      * removeChild operation allows us to do so. It is not intended
521      * for use by application programs.
522      */

523     Node JavaDoc internalRemoveChild(Node JavaDoc oldChild, boolean replace)
524         throws DOMException JavaDoc {
525
526         CoreDocumentImpl ownerDocument = ownerDocument();
527         if (ownerDocument.errorChecking) {
528             if (isReadOnly()) {
529                 throw new DOMException JavaDoc(
530                             DOMException.NO_MODIFICATION_ALLOWED_ERR,
531                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
532             }
533             if (oldChild != null && oldChild.getParentNode() != this) {
534                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
535                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
536             }
537         }
538
539         ChildNode oldInternal = (ChildNode) oldChild;
540
541         // notify document
542
ownerDocument.removingNode(this, oldInternal, replace);
543
544         // update cached length if we have any
545
if (fNodeListCache != null) {
546             if (fNodeListCache.fLength != -1) {
547                 fNodeListCache.fLength--;
548             }
549             if (fNodeListCache.fChildIndex != -1) {
550                 // if the removed node is the cached node
551
// move the cache to its (soon former) previous sibling
552
if (fNodeListCache.fChild == oldInternal) {
553                     fNodeListCache.fChildIndex--;
554                     fNodeListCache.fChild = oldInternal.previousSibling();
555                 } else {
556                     // otherwise just invalidate the cache
557
fNodeListCache.fChildIndex = -1;
558                 }
559             }
560         }
561
562         // Patch linked list around oldChild
563
// Note: lastChild == firstChild.previousSibling
564
if (oldInternal == firstChild) {
565             // removing first child
566
oldInternal.isFirstChild(false);
567             firstChild = oldInternal.nextSibling;
568             if (firstChild != null) {
569                 firstChild.isFirstChild(true);
570                 firstChild.previousSibling = oldInternal.previousSibling;
571             }
572         } else {
573             ChildNode prev = oldInternal.previousSibling;
574             ChildNode next = oldInternal.nextSibling;
575             prev.nextSibling = next;
576             if (next == null) {
577                 // removing last child
578
firstChild.previousSibling = prev;
579             } else {
580                 // removing some other child in the middle
581
next.previousSibling = prev;
582             }
583         }
584
585         // Save previous sibling for normalization checking.
586
ChildNode oldPreviousSibling = oldInternal.previousSibling();
587
588         // Remove oldInternal's references to tree
589
oldInternal.ownerNode = ownerDocument;
590         oldInternal.isOwned(false);
591         oldInternal.nextSibling = null;
592         oldInternal.previousSibling = null;
593
594         changed();
595
596         // notify document
597
ownerDocument.removedNode(this, replace);
598
599         checkNormalizationAfterRemove(oldPreviousSibling);
600
601         return oldInternal;
602
603     } // internalRemoveChild(Node,boolean):Node
604

605     /**
606      * Make newChild occupy the location that oldChild used to
607      * have. Note that newChild will first be removed from its previous
608      * parent, if any. Equivalent to inserting newChild before oldChild,
609      * then removing oldChild.
610      *
611      * @return oldChild, in its new state (removed).
612      *
613      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
614      * type that shouldn't be a child of this node, or if newChild is
615      * one of our ancestors.
616      *
617      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
618      * different owner document than we do.
619      *
620      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
621      * this node.
622      *
623      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
624      * read-only.
625      */

626     public Node JavaDoc replaceChild(Node JavaDoc newChild, Node JavaDoc oldChild)
627         throws DOMException JavaDoc {
628         // If Mutation Events are being generated, this operation might
629
// throw aggregate events twice when modifying an Attr -- once
630
// on insertion and once on removal. DOM Level 2 does not specify
631
// this as either desirable or undesirable, but hints that
632
// aggregations should be issued only once per user request.
633

634         // notify document
635
ownerDocument.replacingNode(this);
636
637         internalInsertBefore(newChild, oldChild, true);
638         if (newChild != oldChild) {
639             internalRemoveChild(oldChild, true);
640         }
641
642         // notify document
643
ownerDocument.replacedNode(this);
644
645         return oldChild;
646     }
647
648     /*
649      * Get Node text content
650      * @since DOM Level 3
651      */

652     public String JavaDoc getTextContent() throws DOMException JavaDoc {
653         Node JavaDoc child = getFirstChild();
654         if (child != null) {
655             Node JavaDoc next = child.getNextSibling();
656             if (next == null) {
657                 return hasTextContent(child) ? ((NodeImpl) child).getTextContent() : "";
658             }
659             if (fBufferStr == null){
660                 fBufferStr = new StringBuffer JavaDoc();
661             }
662             else {
663                 fBufferStr.setLength(0);
664             }
665             getTextContent(fBufferStr);
666             return fBufferStr.toString();
667         }
668         return "";
669     }
670
671     // internal method taking a StringBuffer in parameter
672
void getTextContent(StringBuffer JavaDoc buf) throws DOMException JavaDoc {
673         Node JavaDoc child = getFirstChild();
674         while (child != null) {
675             if (hasTextContent(child)) {
676                 ((NodeImpl) child).getTextContent(buf);
677             }
678             child = child.getNextSibling();
679         }
680     }
681
682     // internal method returning whether to take the given node's text content
683
final boolean hasTextContent(Node JavaDoc child) {
684         return child.getNodeType() != Node.COMMENT_NODE &&
685             child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE &&
686             (child.getNodeType() != Node.TEXT_NODE ||
687              ((TextImpl) child).isIgnorableWhitespace() == false);
688     }
689
690     /*
691      * Set Node text content
692      * @since DOM Level 3
693      */

694     public void setTextContent(String JavaDoc textContent)
695         throws DOMException JavaDoc {
696         // get rid of any existing children
697
Node JavaDoc child;
698         while ((child = getFirstChild()) != null) {
699             removeChild(child);
700         }
701         // create a Text node to hold the given content
702
if (textContent != null && textContent.length() != 0){
703             appendChild(ownerDocument().createTextNode(textContent));
704         }
705     }
706
707     //
708
// NodeList methods
709
//
710

711     /**
712      * Count the immediate children of this node. Use to implement
713      * NodeList.getLength().
714      * @return int
715      */

716     private int nodeListGetLength() {
717
718         if (fNodeListCache == null) {
719             // get rid of trivial cases
720
if (firstChild == null) {
721                 return 0;
722             }
723             if (firstChild == lastChild()) {
724                 return 1;
725             }
726             // otherwise request a cache object
727
fNodeListCache = ownerDocument.getNodeListCache(this);
728         }
729         if (fNodeListCache.fLength == -1) { // is the cached length invalid ?
730
int l;
731             ChildNode n;
732             // start from the cached node if we have one
733
if (fNodeListCache.fChildIndex != -1 &&
734                 fNodeListCache.fChild != null) {
735                 l = fNodeListCache.fChildIndex;
736                 n = fNodeListCache.fChild;
737             } else {
738                 n = firstChild;
739                 l = 0;
740             }
741             while (n != null) {
742                 l++;
743                 n = n.nextSibling;
744             }
745             fNodeListCache.fLength = l;
746         }
747
748         return fNodeListCache.fLength;
749
750     } // nodeListGetLength():int
751

752     /**
753      * NodeList method: Count the immediate children of this node
754      * @return int
755      */

756     public int getLength() {
757         return nodeListGetLength();
758     }
759
760     /**
761      * Return the Nth immediate child of this node, or null if the index is
762      * out of bounds. Use to implement NodeList.item().
763      * @param index int
764      */

765     private Node JavaDoc nodeListItem(int index) {
766
767         if (fNodeListCache == null) {
768             // get rid of trivial case
769
if (firstChild == lastChild()) {
770                 return index == 0 ? firstChild : null;
771             }
772             // otherwise request a cache object
773
fNodeListCache = ownerDocument.getNodeListCache(this);
774         }
775         int i = fNodeListCache.fChildIndex;
776         ChildNode n = fNodeListCache.fChild;
777         boolean firstAccess = true;
778         // short way
779
if (i != -1 && n != null) {
780             firstAccess = false;
781             if (i < index) {
782                 while (i < index && n != null) {
783                     i++;
784                     n = n.nextSibling;
785                 }
786             }
787             else if (i > index) {
788                 while (i > index && n != null) {
789                     i--;
790                     n = n.previousSibling();
791                 }
792             }
793         }
794         else {
795             // long way
796
n = firstChild;
797             for (i = 0; i < index && n != null; i++) {
798                 n = n.nextSibling;
799             }
800         }
801
802         // release cache if reaching last child or first child
803
if (!firstAccess && (n == firstChild || n == lastChild())) {
804             fNodeListCache.fChildIndex = -1;
805             fNodeListCache.fChild = null;
806             ownerDocument.freeNodeListCache(fNodeListCache);
807             // we can keep using the cache until it is actually reused
808
// fNodeListCache will be nulled by the pool (document) if that
809
// happens.
810
// fNodeListCache = null;
811
}
812         else {
813             // otherwise update it
814
fNodeListCache.fChildIndex = i;
815             fNodeListCache.fChild = n;
816         }
817         return n;
818
819     } // nodeListItem(int):Node
820

821     /**
822      * NodeList method: Return the Nth immediate child of this node, or
823      * null if the index is out of bounds.
824      * @return org.w3c.dom.Node
825      * @param index int
826      */

827     public Node JavaDoc item(int index) {
828         return nodeListItem(index);
829     } // item(int):Node
830

831     /**
832      * Create a NodeList to access children that is use by subclass elements
833      * that have methods named getLength() or item(int). ChildAndParentNode
834      * optimizes getChildNodes() by implementing NodeList itself. However if
835      * a subclass Element implements methods with the same name as the NodeList
836      * methods, they will override the actually methods in this class.
837      * <p>
838      * To use this method, the subclass should implement getChildNodes() and
839      * have it call this method. The resulting NodeList instance maybe
840      * shared and cached in a transient field, but the cached value must be
841      * cleared if the node is cloned.
842      */

843     protected final NodeList JavaDoc getChildNodesUnoptimized() {
844         if (needsSyncChildren()) {
845             synchronizeChildren();
846         }
847         return new NodeList JavaDoc() {
848                 /**
849                  * @see NodeList.getLength()
850                  */

851                 public int getLength() {
852                     return nodeListGetLength();
853                 } // getLength():int
854

855                 /**
856                  * @see NodeList.item(int)
857                  */

858                 public Node JavaDoc item(int index) {
859                     return nodeListItem(index);
860                 } // item(int):Node
861
};
862     } // getChildNodesUnoptimized():NodeList
863

864     //
865
// DOM2: methods, getters, setters
866
//
867

868     /**
869      * Override default behavior to call normalize() on this Node's
870      * children. It is up to implementors or Node to override normalize()
871      * to take action.
872      */

873     public void normalize() {
874         // No need to normalize if already normalized.
875
if (isNormalized()) {
876             return;
877         }
878         if (needsSyncChildren()) {
879             synchronizeChildren();
880         }
881         ChildNode kid;
882         for (kid = firstChild; kid != null; kid = kid.nextSibling) {
883             kid.normalize();
884         }
885         isNormalized(true);
886     }
887
888     /**
889      * DOM Level 3 WD- Experimental.
890      * Override inherited behavior from NodeImpl to support deep equal.
891      */

892     public boolean isEqualNode(Node JavaDoc arg) {
893         if (!super.isEqualNode(arg)) {
894             return false;
895         }
896         // there are many ways to do this test, and there isn't any way
897
// better than another. Performance may vary greatly depending on
898
// the implementations involved. This one should work fine for us.
899
Node JavaDoc child1 = getFirstChild();
900         Node JavaDoc child2 = arg.getFirstChild();
901         while (child1 != null && child2 != null) {
902             if (!((NodeImpl) child1).isEqualNode(child2)) {
903                 return false;
904             }
905             child1 = child1.getNextSibling();
906             child2 = child2.getNextSibling();
907         }
908         if (child1 != child2) {
909             return false;
910         }
911         return true;
912     }
913
914     //
915
// Public methods
916
//
917

918     /**
919      * Override default behavior so that if deep is true, children are also
920      * toggled.
921      * @see Node
922      * <P>
923      * Note: this will not change the state of an EntityReference or its
924      * children, which are always read-only.
925      */

926     public void setReadOnly(boolean readOnly, boolean deep) {
927
928         super.setReadOnly(readOnly, deep);
929
930         if (deep) {
931
932             if (needsSyncChildren()) {
933                 synchronizeChildren();
934             }
935
936             // Recursively set kids
937
for (ChildNode mykid = firstChild;
938                  mykid != null;
939                  mykid = mykid.nextSibling) {
940                 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
941                     mykid.setReadOnly(readOnly,true);
942                 }
943             }
944         }
945     } // setReadOnly(boolean,boolean)
946

947     //
948
// Protected methods
949
//
950

951     /**
952      * Override this method in subclass to hook in efficient
953      * internal data structure.
954      */

955     protected void synchronizeChildren() {
956         // By default just change the flag to avoid calling this method again
957
needsSyncChildren(false);
958     }
959
960     /**
961      * Checks the normalized state of this node after inserting a child.
962      * If the inserted child causes this node to be unnormalized, then this
963      * node is flagged accordingly.
964      * The conditions for changing the normalized state are:
965      * <ul>
966      * <li>The inserted child is a text node and one of its adjacent siblings
967      * is also a text node.
968      * <li>The inserted child is is itself unnormalized.
969      * </ul>
970      *
971      * @param insertedChild the child node that was inserted into this node
972      *
973      * @throws NullPointerException if the inserted child is <code>null</code>
974      */

975     void checkNormalizationAfterInsert(ChildNode insertedChild) {
976         // See if insertion caused this node to be unnormalized.
977
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
978             ChildNode prev = insertedChild.previousSibling();
979             ChildNode next = insertedChild.nextSibling;
980             // If an adjacent sibling of the new child is a text node,
981
// flag this node as unnormalized.
982
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
983                 (next != null && next.getNodeType() == Node.TEXT_NODE)) {
984                 isNormalized(false);
985             }
986         }
987         else {
988             // If the new child is not normalized,
989
// then this node is inherently not normalized.
990
if (!insertedChild.isNormalized()) {
991                 isNormalized(false);
992             }
993         }
994     } // checkNormalizationAfterInsert(ChildNode)
995

996     /**
997      * Checks the normalized of this node after removing a child.
998      * If the removed child causes this node to be unnormalized, then this
999      * node is flagged accordingly.
1000     * The conditions for changing the normalized state are:
1001     * <ul>
1002     * <li>The removed child had two adjacent siblings that were text nodes.
1003     * </ul>
1004     *
1005     * @param previousSibling the previous sibling of the removed child, or
1006     * <code>null</code>
1007     */

1008    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1009        // See if removal caused this node to be unnormalized.
1010
// If the adjacent siblings of the removed child were both text nodes,
1011
// flag this node as unnormalized.
1012
if (previousSibling != null &&
1013            previousSibling.getNodeType() == Node.TEXT_NODE) {
1014
1015            ChildNode next = previousSibling.nextSibling;
1016            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1017                isNormalized(false);
1018            }
1019        }
1020    } // checkNormalizationAfterRemove(Node)
1021

1022    //
1023
// Serialization methods
1024
//
1025

1026    /** Serialize object. */
1027    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1028
1029        // synchronize chilren
1030
if (needsSyncChildren()) {
1031            synchronizeChildren();
1032        }
1033        // write object
1034
out.defaultWriteObject();
1035
1036    } // writeObject(ObjectOutputStream)
1037

1038    /** Deserialize object. */
1039    private void readObject(ObjectInputStream JavaDoc ois)
1040        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1041
1042        // perform default deseralization
1043
ois.defaultReadObject();
1044
1045        // hardset synchildren - so we don't try to sync - it does not make any
1046
// sense to try to synchildren when we just deserialize object.
1047
needsSyncChildren(false);
1048
1049    } // readObject(ObjectInputStream)
1050

1051    /*
1052     * a class to store some user data along with its handler
1053     */

1054    class UserDataRecord implements Serializable JavaDoc {
1055        Object JavaDoc fData;
1056        UserDataHandler JavaDoc fHandler;
1057        UserDataRecord(Object JavaDoc data, UserDataHandler JavaDoc handler) {
1058            fData = data;
1059            fHandler = handler;
1060        }
1061    }
1062} // class ParentNode
1063
Popular Tags