KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > apache > xerces > dom > ParentNode


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

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

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

120     /** Cached node list length. */
121     protected transient int fCachedLength = -1;
122
123     /** Last requested node. */
124     protected transient ChildNode fCachedChild;
125
126     /** Last requested node index. */
127     protected transient int fCachedChildIndex = -1;
128
129     //
130
// Constructors
131
//
132

133     /**
134      * No public constructor; only subclasses of ParentNode should be
135      * instantiated, and those normally via a Document's factory methods
136      */

137     protected ParentNode(CoreDocumentImpl ownerDocument) {
138         super(ownerDocument);
139         this.ownerDocument = ownerDocument;
140     }
141
142     /** Constructor for serialization. */
143     public ParentNode() {}
144
145     //
146
// NodeList methods
147
//
148

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

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

197     /**
198      * Find the Document that this Node belongs to (the document in
199      * whose context the Node was created). The Node may or may not
200      * currently be part of that Document's actual contents.
201      */

202     public Document JavaDoc getOwnerDocument() {
203         return ownerDocument;
204     }
205
206     /**
207      * same as above but returns internal type and this one is not overridden
208      * by CoreDocumentImpl to return null
209      */

210     CoreDocumentImpl ownerDocument() {
211         return ownerDocument;
212     }
213
214     /**
215      * NON-DOM
216      * set the ownerDocument of this node and its children
217      */

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

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

254     public NodeList JavaDoc getChildNodes() {
255
256         if (needsSyncChildren()) {
257             synchronizeChildren();
258         }
259         return this;
260
261     } // getChildNodes():NodeList
262

263     /** The first child of this Node, or null if none. */
264     public Node JavaDoc getFirstChild() {
265
266         if (needsSyncChildren()) {
267             synchronizeChildren();
268         }
269         return firstChild;
270
271     } // getFirstChild():Node
272

273     /** The last child of this Node, or null if none. */
274     public Node JavaDoc getLastChild() {
275
276         if (needsSyncChildren()) {
277             synchronizeChildren();
278         }
279         return lastChild();
280
281     } // getLastChild():Node
282

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

323     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
324         throws DOMException JavaDoc {
325         // Tail-call; optimizer should be able to do good things with.
326
return internalInsertBefore(newChild, refChild, false);
327     } // insertBefore(Node,Node):Node
328

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

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

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

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

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

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

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

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

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

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

635         // notify document
636
ownerDocument.replacingNode(this);
637
638         internalInsertBefore(newChild, oldChild, true);
639         if (newChild != oldChild) {
640             internalRemoveChild(oldChild, true);
641         }
642
643         // notify document
644
ownerDocument.replacedNode(this);
645
646         return oldChild;
647     }
648
649     //
650
// NodeList methods
651
//
652

653     /**
654      * Count the immediate children of this node. Use to implement
655      * NodeList.getLength().
656      * @return int
657      */

658     private int nodeListGetLength() {
659
660         if (fCachedLength == -1) { // is the cached length invalid ?
661
ChildNode node;
662             // start from the cached node if we have one
663
if (fCachedChildIndex != -1 && fCachedChild != null) {
664                 fCachedLength = fCachedChildIndex;
665                 node = fCachedChild;
666             } else {
667                 node = firstChild;
668                 fCachedLength = 0;
669             }
670             for (; node != null; node = node.nextSibling) {
671                 fCachedLength++;
672             }
673         }
674
675         return fCachedLength;
676
677     } // nodeListGetLength():int
678

679     /**
680      * NodeList method: Count the immediate children of this node
681      * @return int
682      */

683     public int getLength() {
684         return nodeListGetLength();
685     }
686
687     /**
688      * Return the Nth immediate child of this node, or null if the index is
689      * out of bounds. Use to implement NodeList.item().
690      * @param index int
691      */

692     private Node JavaDoc nodeListItem(int index) {
693         // short way
694
if (fCachedChildIndex != -1 && fCachedChild != null) {
695             if (fCachedChildIndex < index) {
696                 while (fCachedChildIndex < index && fCachedChild != null) {
697                     fCachedChildIndex++;
698                     fCachedChild = fCachedChild.nextSibling;
699                 }
700             }
701             else if (fCachedChildIndex > index) {
702                 while (fCachedChildIndex > index && fCachedChild != null) {
703                     fCachedChildIndex--;
704                     fCachedChild = fCachedChild.previousSibling();
705                 }
706             }
707             return fCachedChild;
708         }
709
710         // long way
711
fCachedChild = firstChild;
712         for (fCachedChildIndex = 0;
713              fCachedChildIndex < index && fCachedChild != null;
714              fCachedChildIndex++) {
715             fCachedChild = fCachedChild.nextSibling;
716         }
717         return fCachedChild;
718
719     } // nodeListItem(int):Node
720

721     /**
722      * NodeList method: Return the Nth immediate child of this node, or
723      * null if the index is out of bounds.
724      * @return org.w3c.dom.Node
725      * @param index int
726      */

727     public Node JavaDoc item(int index) {
728         return nodeListItem(index);
729     } // item(int):Node
730

731     /**
732      * Create a NodeList to access children that is use by subclass elements
733      * that have methods named getLength() or item(int). ChildAndParentNode
734      * optimizes getChildNodes() by implementing NodeList itself. However if
735      * a subclass Element implements methods with the same name as the NodeList
736      * methods, they will override the actually methods in this class.
737      * <p>
738      * To use this method, the subclass should implement getChildNodes() and
739      * have it call this method. The resulting NodeList instance maybe
740      * shared and cached in a transient field, but the cached value must be
741      * cleared if the node is cloned.
742      */

743     protected final NodeList JavaDoc getChildNodesUnoptimized() {
744         if (needsSyncChildren()) {
745             synchronizeChildren();
746         }
747         return new NodeList JavaDoc() {
748                 /**
749                  * @see NodeList.getLength()
750                  */

751                 public int getLength() {
752                     return nodeListGetLength();
753                 } // getLength():int
754

755                 /**
756                  * @see NodeList.item(int)
757                  */

758                 public Node JavaDoc item(int index) {
759                     return nodeListItem(index);
760                 } // item(int):Node
761
};
762     } // getChildNodesUnoptimized():NodeList
763

764     //
765
// DOM2: methods, getters, setters
766
//
767

768     /**
769      * Override default behavior to call normalize() on this Node's
770      * children. It is up to implementors or Node to override normalize()
771      * to take action.
772      */

773     public void normalize() {
774         // No need to normalize if already normalized.
775
if (isNormalized()) {
776             return;
777         }
778         if (needsSyncChildren()) {
779             synchronizeChildren();
780         }
781         ChildNode kid;
782         for (kid = firstChild; kid != null; kid = kid.nextSibling) {
783             kid.normalize();
784         }
785         isNormalized(true);
786     }
787
788     //
789
// Public methods
790
//
791

792     /**
793      * Override default behavior so that if deep is true, children are also
794      * toggled.
795      * @see Node
796      * <P>
797      * Note: this will not change the state of an EntityReference or its
798      * children, which are always read-only.
799      */

800     public void setReadOnly(boolean readOnly, boolean deep) {
801
802         super.setReadOnly(readOnly, deep);
803
804         if (deep) {
805
806             if (needsSyncChildren()) {
807                 synchronizeChildren();
808             }
809
810             // Recursively set kids
811
for (ChildNode mykid = firstChild;
812                  mykid != null;
813                  mykid = mykid.nextSibling) {
814                 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
815                     mykid.setReadOnly(readOnly,true);
816                 }
817             }
818         }
819     } // setReadOnly(boolean,boolean)
820

821     //
822
// Protected methods
823
//
824

825     /**
826      * Override this method in subclass to hook in efficient
827      * internal data structure.
828      */

829     protected void synchronizeChildren() {
830         // By default just change the flag to avoid calling this method again
831
needsSyncChildren(false);
832     }
833
834     /**
835      * Checks the normalized state of this node after inserting a child.
836      * If the inserted child causes this node to be unnormalized, then this
837      * node is flagged accordingly.
838      * The conditions for changing the normalized state are:
839      * <ul>
840      * <li>The inserted child is a text node and one of its adjacent siblings
841      * is also a text node.
842      * <li>The inserted child is is itself unnormalized.
843      * </ul>
844      *
845      * @param insertedChild the child node that was inserted into this node
846      *
847      * @throws NullPointerException if the inserted child is <code>null</code>
848      */

849     void checkNormalizationAfterInsert(ChildNode insertedChild) {
850         // See if insertion caused this node to be unnormalized.
851
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
852             ChildNode prev = insertedChild.previousSibling();
853             ChildNode next = insertedChild.nextSibling;
854             // If an adjacent sibling of the new child is a text node,
855
// flag this node as unnormalized.
856
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
857                 (next != null && next.getNodeType() == Node.TEXT_NODE)) {
858                 isNormalized(false);
859             }
860         }
861         else {
862             // If the new child is not normalized,
863
// then this node is inherently not normalized.
864
if (!insertedChild.isNormalized()) {
865                 isNormalized(false);
866             }
867         }
868     } // checkNormalizationAfterInsert(ChildNode)
869

870     /**
871      * Checks the normalized of this node after removing a child.
872      * If the removed child causes this node to be unnormalized, then this
873      * node is flagged accordingly.
874      * The conditions for changing the normalized state are:
875      * <ul>
876      * <li>The removed child had two adjacent siblings that were text nodes.
877      * </ul>
878      *
879      * @param previousSibling the previous sibling of the removed child, or
880      * <code>null</code>
881      */

882     void checkNormalizationAfterRemove(ChildNode previousSibling) {
883         // See if removal caused this node to be unnormalized.
884
// If the adjacent siblings of the removed child were both text nodes,
885
// flag this node as unnormalized.
886
if (previousSibling != null &&
887             previousSibling.getNodeType() == Node.TEXT_NODE) {
888
889             ChildNode next = previousSibling.nextSibling;
890             if (next != null && next.getNodeType() == Node.TEXT_NODE) {
891                 isNormalized(false);
892             }
893         }
894     } // checkNormalizationAfterRemove(Node)
895

896     //
897
// Serialization methods
898
//
899

900     /** Serialize object. */
901     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
902
903         // synchronize chilren
904
if (needsSyncChildren()) {
905             synchronizeChildren();
906         }
907         // write object
908
out.defaultWriteObject();
909
910     } // writeObject(ObjectOutputStream)
911

912     /** Deserialize object. */
913     private void readObject(ObjectInputStream JavaDoc ois)
914         throws ClassNotFoundException JavaDoc, IOException JavaDoc {
915
916         // perform default deseralization
917
ois.defaultReadObject();
918
919         // hardset synchildren - so we don't try to sync- it does not make any sense
920
// to try to synchildren when we just desealize object.
921

922         needsSyncChildren(false);
923
924         // initialize transients
925
fCachedLength = -1;
926         fCachedChildIndex = -1;
927
928     } // readObject(ObjectInputStream)
929

930 } // class ParentNode
931
Popular Tags