KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 1999-2002,2004,2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.dom;
18
19 import java.io.Serializable JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.ObjectInputStream JavaDoc;
22 import java.io.ObjectOutputStream JavaDoc;
23
24 import org.w3c.dom.DOMException JavaDoc;
25 import org.w3c.dom.Document JavaDoc;
26 import org.w3c.dom.Node JavaDoc;
27 import org.w3c.dom.NodeList JavaDoc;
28 import org.w3c.dom.UserDataHandler JavaDoc;
29
30 /**
31  * ParentNode inherits from ChildNode and adds the capability of having child
32  * nodes. Not every node in the DOM can have children, so only nodes that can
33  * should inherit from this class and pay the price for it.
34  * <P>
35  * ParentNode, just like NodeImpl, also implements NodeList, so it can
36  * return itself in response to the getChildNodes() query. This eliminiates
37  * the need for a separate ChildNodeList object. Note that this is an
38  * IMPLEMENTATION DETAIL; applications should _never_ assume that
39  * this identity exists. On the other hand, subclasses may need to override
40  * this, in case of conflicting names. This is the case for the classes
41  * HTMLSelectElementImpl and HTMLFormElementImpl of the HTML DOM.
42  * <P>
43  * While we have a direct reference to the first child, the last child is
44  * stored as the previous sibling of the first child. First child nodes are
45  * marked as being so, and getNextSibling hides this fact.
46  * <P>Note: Not all parent nodes actually need to also be a child. At some
47  * point we used to have ParentNode inheriting from NodeImpl and another class
48  * called ChildAndParentNode that inherited from ChildNode. But due to the lack
49  * of multiple inheritance a lot of code had to be duplicated which led to a
50  * maintenance nightmare. At the same time only a few nodes (Document,
51  * DocumentFragment, Entity, and Attribute) cannot be a child so the gain in
52  * memory wasn't really worth it. The only type for which this would be the
53  * case is Attribute, but we deal with there in another special way, so this is
54  * not applicable.
55  * <p>
56  * This class doesn't directly support mutation events, however, it notifies
57  * the document when mutations are performed so that the document class do so.
58  *
59  * <p><b>WARNING</b>: Some of the code here is partially duplicated in
60  * AttrImpl, be careful to keep these two classes in sync!
61  *
62  * @xerces.internal
63  *
64  * @author Arnaud Le Hors, IBM
65  * @author Joe Kesselman, IBM
66  * @author Andy Clark, IBM
67  * @version $Id: ParentNode.java,v 1.50 2005/05/02 22:02:22 mrglavas Exp $
68  */

69 public abstract class ParentNode
70     extends ChildNode {
71
72     /** Serialization version. */
73     static final long serialVersionUID = 2815829867152120872L;
74
75     /** Owner document. */
76     protected CoreDocumentImpl ownerDocument;
77
78     /** First child. */
79     protected ChildNode firstChild = null;
80
81     // transients
82

83     /** NodeList cache */
84     protected transient NodeListCache fNodeListCache = null;
85
86     //
87
// Constructors
88
//
89

90     /**
91      * No public constructor; only subclasses of ParentNode should be
92      * instantiated, and those normally via a Document's factory methods
93      */

94     protected ParentNode(CoreDocumentImpl ownerDocument) {
95         super(ownerDocument);
96         this.ownerDocument = ownerDocument;
97     }
98
99     /** Constructor for serialization. */
100     public ParentNode() {}
101
102     //
103
// NodeList methods
104
//
105

106     /**
107      * Returns a duplicate of a given node. You can consider this a
108      * generic "copy constructor" for nodes. The newly returned object should
109      * be completely independent of the source object's subtree, so changes
110      * in one after the clone has been made will not affect the other.
111      * <p>
112      * Example: Cloning a Text node will copy both the node and the text it
113      * contains.
114      * <p>
115      * Example: Cloning something that has children -- Element or Attr, for
116      * example -- will _not_ clone those children unless a "deep clone"
117      * has been requested. A shallow clone of an Attr node will yield an
118      * empty Attr of the same name.
119      * <p>
120      * NOTE: Clones will always be read/write, even if the node being cloned
121      * is read-only, to permit applications using only the DOM API to obtain
122      * editable copies of locked portions of the tree.
123      */

124     public Node JavaDoc cloneNode(boolean deep) {
125         
126         if (needsSyncChildren()) {
127             synchronizeChildren();
128         }
129         ParentNode newnode = (ParentNode) super.cloneNode(deep);
130
131         // set owner document
132
newnode.ownerDocument = ownerDocument;
133
134         // Need to break the association w/ original kids
135
newnode.firstChild = null;
136
137         // invalidate cache for children NodeList
138
newnode.fNodeListCache = null;
139
140         // Then, if deep, clone the kids too.
141
if (deep) {
142             for (ChildNode child = firstChild;
143                  child != null;
144                  child = child.nextSibling) {
145                 newnode.appendChild(child.cloneNode(true));
146             }
147         }
148
149         return newnode;
150
151     } // cloneNode(boolean):Node
152

153     /**
154      * Find the Document that this Node belongs to (the document in
155      * whose context the Node was created). The Node may or may not
156      * currently be part of that Document's actual contents.
157      */

158     public Document JavaDoc getOwnerDocument() {
159         return ownerDocument;
160     }
161
162     /**
163      * same as above but returns internal type and this one is not overridden
164      * by CoreDocumentImpl to return null
165      */

166     CoreDocumentImpl ownerDocument() {
167         return ownerDocument;
168     }
169
170     /**
171      * NON-DOM
172      * set the ownerDocument of this node and its children
173      */

174     void setOwnerDocument(CoreDocumentImpl doc) {
175         if (needsSyncChildren()) {
176             synchronizeChildren();
177         }
178         super.setOwnerDocument(doc);
179         ownerDocument = doc;
180     for (ChildNode child = firstChild;
181          child != null; child = child.nextSibling) {
182         child.setOwnerDocument(doc);
183     }
184     }
185
186     /**
187      * Test whether this node has any children. Convenience shorthand
188      * for (Node.getFirstChild()!=null)
189      */

190     public boolean hasChildNodes() {
191         if (needsSyncChildren()) {
192             synchronizeChildren();
193         }
194         return firstChild != null;
195     }
196
197     /**
198      * Obtain a NodeList enumerating all children of this node. If there
199      * are none, an (initially) empty NodeList is returned.
200      * <p>
201      * NodeLists are "live"; as children are added/removed the NodeList
202      * will immediately reflect those changes. Also, the NodeList refers
203      * to the actual nodes, so changes to those nodes made via the DOM tree
204      * will be reflected in the NodeList and vice versa.
205      * <p>
206      * In this implementation, Nodes implement the NodeList interface and
207      * provide their own getChildNodes() support. Other DOMs may solve this
208      * differently.
209      */

210     public NodeList JavaDoc getChildNodes() {
211
212         if (needsSyncChildren()) {
213             synchronizeChildren();
214         }
215         return this;
216
217     } // getChildNodes():NodeList
218

219     /** The first child of this Node, or null if none. */
220     public Node JavaDoc getFirstChild() {
221
222         if (needsSyncChildren()) {
223             synchronizeChildren();
224         }
225         return firstChild;
226
227     } // getFirstChild():Node
228

229     /** The last child of this Node, or null if none. */
230     public Node JavaDoc getLastChild() {
231
232         if (needsSyncChildren()) {
233             synchronizeChildren();
234         }
235         return lastChild();
236
237     } // getLastChild():Node
238

239     final ChildNode lastChild() {
240         // last child is stored as the previous sibling of first child
241
return firstChild != null ? firstChild.previousSibling : null;
242     }
243
244     final void lastChild(ChildNode node) {
245         // store lastChild as previous sibling of first child
246
if (firstChild != null) {
247             firstChild.previousSibling = node;
248         }
249     }
250
251     /**
252      * Move one or more node(s) to our list of children. Note that this
253      * implicitly removes them from their previous parent.
254      *
255      * @param newChild The Node to be moved to our subtree. As a
256      * convenience feature, inserting a DocumentNode will instead insert
257      * all its children.
258      *
259      * @param refChild Current child which newChild should be placed
260      * immediately before. If refChild is null, the insertion occurs
261      * after all existing Nodes, like appendChild().
262      *
263      * @return newChild, in its new state (relocated, or emptied in the case of
264      * DocumentNode.)
265      *
266      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
267      * type that shouldn't be a child of this node, or if newChild is an
268      * ancestor of this node.
269      *
270      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
271      * different owner document than we do.
272      *
273      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
274      * this node.
275      *
276      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
277      * read-only.
278      */

279     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
280         throws DOMException JavaDoc {
281         // Tail-call; optimizer should be able to do good things with.
282
return internalInsertBefore(newChild, refChild, false);
283     } // insertBefore(Node,Node):Node
284

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

290     Node JavaDoc internalInsertBefore(Node JavaDoc newChild, Node JavaDoc refChild, boolean replace)
291         throws DOMException JavaDoc {
292
293         boolean errorChecking = ownerDocument.errorChecking;
294
295         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
296             // SLOW BUT SAFE: We could insert the whole subtree without
297
// juggling so many next/previous pointers. (Wipe out the
298
// parent's child-list, patch the parent pointers, set the
299
// ends of the list.) But we know some subclasses have special-
300
// case behavior they add to insertBefore(), so we don't risk it.
301
// This approch also takes fewer bytecodes.
302

303             // NOTE: If one of the children is not a legal child of this
304
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
305
// have been transferred. (Alternative behaviors would be to
306
// reparent up to the first failure point or reparent all those
307
// which are acceptable to the target node, neither of which is
308
// as robust. PR-DOM-0818 isn't entirely clear on which it
309
// recommends?????
310

311             // No need to check kids for right-document; if they weren't,
312
// they wouldn't be kids of that DocFrag.
313
if (errorChecking) {
314                 for (Node JavaDoc kid = newChild.getFirstChild(); // Prescan
315
kid != null; kid = kid.getNextSibling()) {
316
317                     if (!ownerDocument.isKidOK(this, kid)) {
318                         throw new DOMException JavaDoc(
319                               DOMException.HIERARCHY_REQUEST_ERR,
320                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
321                     }
322                 }
323             }
324
325             while (newChild.hasChildNodes()) {
326                 insertBefore(newChild.getFirstChild(), refChild);
327             }
328             return newChild;
329         }
330
331         if (newChild == refChild) {
332             // stupid case that must be handled as a no-op triggering events...
333
refChild = refChild.getNextSibling();
334             removeChild(newChild);
335             insertBefore(newChild, refChild);
336             return newChild;
337         }
338
339         if (needsSyncChildren()) {
340             synchronizeChildren();
341         }
342
343         if (errorChecking) {
344             if (isReadOnly()) {
345                 throw new DOMException JavaDoc(
346                               DOMException.NO_MODIFICATION_ALLOWED_ERR,
347                               DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
348             }
349             if (newChild.getOwnerDocument() != ownerDocument && newChild != ownerDocument) {
350                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
351                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
352             }
353             if (!ownerDocument.isKidOK(this, newChild)) {
354                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
355                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
356             }
357             // refChild must be a child of this node (or null)
358
if (refChild != null && refChild.getParentNode() != this) {
359                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
360                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
361             }
362
363             // Prevent cycles in the tree
364
// newChild cannot be ancestor of this Node,
365
// and actually cannot be this
366
boolean treeSafe = true;
367             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
368             {
369                 treeSafe = newChild != a;
370             }
371             if(!treeSafe) {
372                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
373                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
374             }
375         }
376
377         // notify document
378
ownerDocument.insertingNode(this, replace);
379
380         // Convert to internal type, to avoid repeated casting
381
ChildNode newInternal = (ChildNode)newChild;
382
383         Node JavaDoc oldparent = newInternal.parentNode();
384         if (oldparent != null) {
385             oldparent.removeChild(newInternal);
386         }
387
388         // Convert to internal type, to avoid repeated casting
389
ChildNode refInternal = (ChildNode)refChild;
390
391         // Attach up
392
newInternal.ownerNode = this;
393         newInternal.isOwned(true);
394
395         // Attach before and after
396
// Note: firstChild.previousSibling == lastChild!!
397
if (firstChild == null) {
398             // this our first and only child
399
firstChild = newInternal;
400             newInternal.isFirstChild(true);
401             newInternal.previousSibling = newInternal;
402         }
403         else {
404             if (refInternal == null) {
405                 // this is an append
406
ChildNode lastChild = firstChild.previousSibling;
407                 lastChild.nextSibling = newInternal;
408                 newInternal.previousSibling = lastChild;
409                 firstChild.previousSibling = newInternal;
410             }
411             else {
412                 // this is an insert
413
if (refChild == firstChild) {
414                     // at the head of the list
415
firstChild.isFirstChild(false);
416                     newInternal.nextSibling = firstChild;
417                     newInternal.previousSibling = firstChild.previousSibling;
418                     firstChild.previousSibling = newInternal;
419                     firstChild = newInternal;
420                     newInternal.isFirstChild(true);
421                 }
422                 else {
423                     // somewhere in the middle
424
ChildNode prev = refInternal.previousSibling;
425                     newInternal.nextSibling = refInternal;
426                     prev.nextSibling = newInternal;
427                     refInternal.previousSibling = newInternal;
428                     newInternal.previousSibling = prev;
429                 }
430             }
431         }
432
433         changed();
434
435         // update cached length if we have any
436
if (fNodeListCache != null) {
437             if (fNodeListCache.fLength != -1) {
438                 fNodeListCache.fLength++;
439             }
440             if (fNodeListCache.fChildIndex != -1) {
441                 // if we happen to insert just before the cached node, update
442
// the cache to the new node to match the cached index
443
if (fNodeListCache.fChild == refInternal) {
444                     fNodeListCache.fChild = newInternal;
445                 } else {
446                     // otherwise just invalidate the cache
447
fNodeListCache.fChildIndex = -1;
448                 }
449             }
450         }
451
452         // notify document
453
ownerDocument.insertedNode(this, newInternal, replace);
454
455         checkNormalizationAfterInsert(newInternal);
456
457         return newChild;
458
459     } // internalInsertBefore(Node,Node,boolean):Node
460

461     /**
462      * Remove a child from this Node. The removed child's subtree
463      * remains intact so it may be re-inserted elsewhere.
464      *
465      * @return oldChild, in its new state (removed).
466      *
467      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
468      * this node.
469      *
470      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
471      * read-only.
472      */

473     public Node JavaDoc removeChild(Node JavaDoc oldChild)
474         throws DOMException JavaDoc {
475         // Tail-call, should be optimizable
476
return internalRemoveChild(oldChild, false);
477     } // removeChild(Node) :Node
478

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

484     Node JavaDoc internalRemoveChild(Node JavaDoc oldChild, boolean replace)
485         throws DOMException JavaDoc {
486
487         CoreDocumentImpl ownerDocument = ownerDocument();
488         if (ownerDocument.errorChecking) {
489             if (isReadOnly()) {
490                 throw new DOMException JavaDoc(
491                             DOMException.NO_MODIFICATION_ALLOWED_ERR,
492                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
493             }
494             if (oldChild != null && oldChild.getParentNode() != this) {
495                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
496                             DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
497             }
498         }
499
500         ChildNode oldInternal = (ChildNode) oldChild;
501
502         // notify document
503
ownerDocument.removingNode(this, oldInternal, replace);
504
505         // update cached length if we have any
506
if (fNodeListCache != null) {
507             if (fNodeListCache.fLength != -1) {
508                 fNodeListCache.fLength--;
509             }
510             if (fNodeListCache.fChildIndex != -1) {
511                 // if the removed node is the cached node
512
// move the cache to its (soon former) previous sibling
513
if (fNodeListCache.fChild == oldInternal) {
514                     fNodeListCache.fChildIndex--;
515                     fNodeListCache.fChild = oldInternal.previousSibling();
516                 } else {
517                     // otherwise just invalidate the cache
518
fNodeListCache.fChildIndex = -1;
519                 }
520             }
521         }
522
523         // Patch linked list around oldChild
524
// Note: lastChild == firstChild.previousSibling
525
if (oldInternal == firstChild) {
526             // removing first child
527
oldInternal.isFirstChild(false);
528             firstChild = oldInternal.nextSibling;
529             if (firstChild != null) {
530                 firstChild.isFirstChild(true);
531                 firstChild.previousSibling = oldInternal.previousSibling;
532             }
533         } else {
534             ChildNode prev = oldInternal.previousSibling;
535             ChildNode next = oldInternal.nextSibling;
536             prev.nextSibling = next;
537             if (next == null) {
538                 // removing last child
539
firstChild.previousSibling = prev;
540             } else {
541                 // removing some other child in the middle
542
next.previousSibling = prev;
543             }
544         }
545
546         // Save previous sibling for normalization checking.
547
ChildNode oldPreviousSibling = oldInternal.previousSibling();
548
549         // Remove oldInternal's references to tree
550
oldInternal.ownerNode = ownerDocument;
551         oldInternal.isOwned(false);
552         oldInternal.nextSibling = null;
553         oldInternal.previousSibling = null;
554
555         changed();
556
557         // notify document
558
ownerDocument.removedNode(this, replace);
559
560         checkNormalizationAfterRemove(oldPreviousSibling);
561
562         return oldInternal;
563
564     } // internalRemoveChild(Node,boolean):Node
565

566     /**
567      * Make newChild occupy the location that oldChild used to
568      * have. Note that newChild will first be removed from its previous
569      * parent, if any. Equivalent to inserting newChild before oldChild,
570      * then removing oldChild.
571      *
572      * @return oldChild, in its new state (removed).
573      *
574      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
575      * type that shouldn't be a child of this node, or if newChild is
576      * one of our ancestors.
577      *
578      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
579      * different owner document than we do.
580      *
581      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
582      * this node.
583      *
584      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
585      * read-only.
586      */

587     public Node JavaDoc replaceChild(Node JavaDoc newChild, Node JavaDoc oldChild)
588         throws DOMException JavaDoc {
589         // If Mutation Events are being generated, this operation might
590
// throw aggregate events twice when modifying an Attr -- once
591
// on insertion and once on removal. DOM Level 2 does not specify
592
// this as either desirable or undesirable, but hints that
593
// aggregations should be issued only once per user request.
594

595         // notify document
596
ownerDocument.replacingNode(this);
597
598         internalInsertBefore(newChild, oldChild, true);
599         if (newChild != oldChild) {
600             internalRemoveChild(oldChild, true);
601         }
602
603         // notify document
604
ownerDocument.replacedNode(this);
605
606         return oldChild;
607     }
608
609     /*
610      * Get Node text content
611      * @since DOM Level 3
612      */

613     public String JavaDoc getTextContent() throws DOMException JavaDoc {
614         Node JavaDoc child = getFirstChild();
615         if (child != null) {
616             Node JavaDoc next = child.getNextSibling();
617             if (next == null) {
618                 return hasTextContent(child) ? ((NodeImpl) child).getTextContent() : "";
619             }
620             if (fBufferStr == null){
621                 fBufferStr = new StringBuffer JavaDoc();
622             }
623             else {
624                 fBufferStr.setLength(0);
625             }
626             getTextContent(fBufferStr);
627             return fBufferStr.toString();
628         }
629         return "";
630     }
631
632     // internal method taking a StringBuffer in parameter
633
void getTextContent(StringBuffer JavaDoc buf) throws DOMException JavaDoc {
634         Node JavaDoc child = getFirstChild();
635         while (child != null) {
636             if (hasTextContent(child)) {
637                 ((NodeImpl) child).getTextContent(buf);
638             }
639             child = child.getNextSibling();
640         }
641     }
642
643     // internal method returning whether to take the given node's text content
644
final boolean hasTextContent(Node JavaDoc child) {
645         return child.getNodeType() != Node.COMMENT_NODE &&
646             child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE &&
647             (child.getNodeType() != Node.TEXT_NODE ||
648              ((TextImpl) child).isIgnorableWhitespace() == false);
649     }
650
651     /*
652      * Set Node text content
653      * @since DOM Level 3
654      */

655     public void setTextContent(String JavaDoc textContent)
656         throws DOMException JavaDoc {
657         // get rid of any existing children
658
Node JavaDoc child;
659         while ((child = getFirstChild()) != null) {
660             removeChild(child);
661         }
662         // create a Text node to hold the given content
663
if (textContent != null && textContent.length() != 0){
664             appendChild(ownerDocument().createTextNode(textContent));
665         }
666     }
667
668     //
669
// NodeList methods
670
//
671

672     /**
673      * Count the immediate children of this node. Use to implement
674      * NodeList.getLength().
675      * @return int
676      */

677     private int nodeListGetLength() {
678
679         if (fNodeListCache == null) {
680             // get rid of trivial cases
681
if (firstChild == null) {
682                 return 0;
683             }
684             if (firstChild == lastChild()) {
685                 return 1;
686             }
687             // otherwise request a cache object
688
fNodeListCache = ownerDocument.getNodeListCache(this);
689         }
690         if (fNodeListCache.fLength == -1) { // is the cached length invalid ?
691
int l;
692             ChildNode n;
693             // start from the cached node if we have one
694
if (fNodeListCache.fChildIndex != -1 &&
695                 fNodeListCache.fChild != null) {
696                 l = fNodeListCache.fChildIndex;
697                 n = fNodeListCache.fChild;
698             } else {
699                 n = firstChild;
700                 l = 0;
701             }
702             while (n != null) {
703                 l++;
704                 n = n.nextSibling;
705             }
706             fNodeListCache.fLength = l;
707         }
708
709         return fNodeListCache.fLength;
710
711     } // nodeListGetLength():int
712

713     /**
714      * NodeList method: Count the immediate children of this node
715      * @return int
716      */

717     public int getLength() {
718         return nodeListGetLength();
719     }
720
721     /**
722      * Return the Nth immediate child of this node, or null if the index is
723      * out of bounds. Use to implement NodeList.item().
724      * @param index int
725      */

726     private Node JavaDoc nodeListItem(int index) {
727
728         if (fNodeListCache == null) {
729             // get rid of trivial case
730
if (firstChild == lastChild()) {
731                 return index == 0 ? firstChild : null;
732             }
733             // otherwise request a cache object
734
fNodeListCache = ownerDocument.getNodeListCache(this);
735         }
736         int i = fNodeListCache.fChildIndex;
737         ChildNode n = fNodeListCache.fChild;
738         boolean firstAccess = true;
739         // short way
740
if (i != -1 && n != null) {
741             firstAccess = false;
742             if (i < index) {
743                 while (i < index && n != null) {
744                     i++;
745                     n = n.nextSibling;
746                 }
747             }
748             else if (i > index) {
749                 while (i > index && n != null) {
750                     i--;
751                     n = n.previousSibling();
752                 }
753             }
754         }
755         else {
756             // long way
757
if (index < 0) {
758                 return null;
759             }
760             n = firstChild;
761             for (i = 0; i < index && n != null; i++) {
762                 n = n.nextSibling;
763             }
764         }
765
766         // release cache if reaching last child or first child
767
if (!firstAccess && (n == firstChild || n == lastChild())) {
768             fNodeListCache.fChildIndex = -1;
769             fNodeListCache.fChild = null;
770             ownerDocument.freeNodeListCache(fNodeListCache);
771             // we can keep using the cache until it is actually reused
772
// fNodeListCache will be nulled by the pool (document) if that
773
// happens.
774
// fNodeListCache = null;
775
}
776         else {
777             // otherwise update it
778
fNodeListCache.fChildIndex = i;
779             fNodeListCache.fChild = n;
780         }
781         return n;
782
783     } // nodeListItem(int):Node
784

785     /**
786      * NodeList method: Return the Nth immediate child of this node, or
787      * null if the index is out of bounds.
788      * @return org.w3c.dom.Node
789      * @param index int
790      */

791     public Node JavaDoc item(int index) {
792         return nodeListItem(index);
793     } // item(int):Node
794

795     /**
796      * Create a NodeList to access children that is use by subclass elements
797      * that have methods named getLength() or item(int). ChildAndParentNode
798      * optimizes getChildNodes() by implementing NodeList itself. However if
799      * a subclass Element implements methods with the same name as the NodeList
800      * methods, they will override the actually methods in this class.
801      * <p>
802      * To use this method, the subclass should implement getChildNodes() and
803      * have it call this method. The resulting NodeList instance maybe
804      * shared and cached in a transient field, but the cached value must be
805      * cleared if the node is cloned.
806      */

807     protected final NodeList JavaDoc getChildNodesUnoptimized() {
808         if (needsSyncChildren()) {
809             synchronizeChildren();
810         }
811         return new NodeList JavaDoc() {
812                 /**
813                  * @see NodeList.getLength()
814                  */

815                 public int getLength() {
816                     return nodeListGetLength();
817                 } // getLength():int
818

819                 /**
820                  * @see NodeList.item(int)
821                  */

822                 public Node JavaDoc item(int index) {
823                     return nodeListItem(index);
824                 } // item(int):Node
825
};
826     } // getChildNodesUnoptimized():NodeList
827

828     //
829
// DOM2: methods, getters, setters
830
//
831

832     /**
833      * Override default behavior to call normalize() on this Node's
834      * children. It is up to implementors or Node to override normalize()
835      * to take action.
836      */

837     public void normalize() {
838         // No need to normalize if already normalized.
839
if (isNormalized()) {
840             return;
841         }
842         if (needsSyncChildren()) {
843             synchronizeChildren();
844         }
845         ChildNode kid;
846         for (kid = firstChild; kid != null; kid = kid.nextSibling) {
847             kid.normalize();
848         }
849         isNormalized(true);
850     }
851
852     /**
853      * DOM Level 3 WD- Experimental.
854      * Override inherited behavior from NodeImpl to support deep equal.
855      */

856     public boolean isEqualNode(Node JavaDoc arg) {
857         if (!super.isEqualNode(arg)) {
858             return false;
859         }
860         // there are many ways to do this test, and there isn't any way
861
// better than another. Performance may vary greatly depending on
862
// the implementations involved. This one should work fine for us.
863
Node JavaDoc child1 = getFirstChild();
864         Node JavaDoc child2 = arg.getFirstChild();
865         while (child1 != null && child2 != null) {
866             if (!((NodeImpl) child1).isEqualNode(child2)) {
867                 return false;
868             }
869             child1 = child1.getNextSibling();
870             child2 = child2.getNextSibling();
871         }
872         if (child1 != child2) {
873             return false;
874         }
875         return true;
876     }
877
878     //
879
// Public methods
880
//
881

882     /**
883      * Override default behavior so that if deep is true, children are also
884      * toggled.
885      * @see Node
886      * <P>
887      * Note: this will not change the state of an EntityReference or its
888      * children, which are always read-only.
889      */

890     public void setReadOnly(boolean readOnly, boolean deep) {
891
892         super.setReadOnly(readOnly, deep);
893
894         if (deep) {
895
896             if (needsSyncChildren()) {
897                 synchronizeChildren();
898             }
899
900             // Recursively set kids
901
for (ChildNode mykid = firstChild;
902                  mykid != null;
903                  mykid = mykid.nextSibling) {
904                 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
905                     mykid.setReadOnly(readOnly,true);
906                 }
907             }
908         }
909     } // setReadOnly(boolean,boolean)
910

911     //
912
// Protected methods
913
//
914

915     /**
916      * Override this method in subclass to hook in efficient
917      * internal data structure.
918      */

919     protected void synchronizeChildren() {
920         // By default just change the flag to avoid calling this method again
921
needsSyncChildren(false);
922     }
923
924     /**
925      * Checks the normalized state of this node after inserting a child.
926      * If the inserted child causes this node to be unnormalized, then this
927      * node is flagged accordingly.
928      * The conditions for changing the normalized state are:
929      * <ul>
930      * <li>The inserted child is a text node and one of its adjacent siblings
931      * is also a text node.
932      * <li>The inserted child is is itself unnormalized.
933      * </ul>
934      *
935      * @param insertedChild the child node that was inserted into this node
936      *
937      * @throws NullPointerException if the inserted child is <code>null</code>
938      */

939     void checkNormalizationAfterInsert(ChildNode insertedChild) {
940         // See if insertion caused this node to be unnormalized.
941
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
942             ChildNode prev = insertedChild.previousSibling();
943             ChildNode next = insertedChild.nextSibling;
944             // If an adjacent sibling of the new child is a text node,
945
// flag this node as unnormalized.
946
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
947                 (next != null && next.getNodeType() == Node.TEXT_NODE)) {
948                 isNormalized(false);
949             }
950         }
951         else {
952             // If the new child is not normalized,
953
// then this node is inherently not normalized.
954
if (!insertedChild.isNormalized()) {
955                 isNormalized(false);
956             }
957         }
958     } // checkNormalizationAfterInsert(ChildNode)
959

960     /**
961      * Checks the normalized of this node after removing a child.
962      * If the removed 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 removed child had two adjacent siblings that were text nodes.
967      * </ul>
968      *
969      * @param previousSibling the previous sibling of the removed child, or
970      * <code>null</code>
971      */

972     void checkNormalizationAfterRemove(ChildNode previousSibling) {
973         // See if removal caused this node to be unnormalized.
974
// If the adjacent siblings of the removed child were both text nodes,
975
// flag this node as unnormalized.
976
if (previousSibling != null &&
977             previousSibling.getNodeType() == Node.TEXT_NODE) {
978
979             ChildNode next = previousSibling.nextSibling;
980             if (next != null && next.getNodeType() == Node.TEXT_NODE) {
981                 isNormalized(false);
982             }
983         }
984     } // checkNormalizationAfterRemove(Node)
985

986     //
987
// Serialization methods
988
//
989

990     /** Serialize object. */
991     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
992
993         // synchronize chilren
994
if (needsSyncChildren()) {
995             synchronizeChildren();
996         }
997         // write object
998
out.defaultWriteObject();
999
1000    } // writeObject(ObjectOutputStream)
1001

1002    /** Deserialize object. */
1003    private void readObject(ObjectInputStream JavaDoc ois)
1004        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1005
1006        // perform default deseralization
1007
ois.defaultReadObject();
1008
1009        // hardset synchildren - so we don't try to sync - it does not make any
1010
// sense to try to synchildren when we just deserialize object.
1011
needsSyncChildren(false);
1012
1013    } // readObject(ObjectInputStream)
1014

1015    /*
1016     * a class to store some user data along with its handler
1017     */

1018    class UserDataRecord implements Serializable JavaDoc {
1019        /** Serialization version. */
1020        private static final long serialVersionUID = 3258126977134310455L;
1021        
1022        Object JavaDoc fData;
1023        UserDataHandler JavaDoc fHandler;
1024        UserDataRecord(Object JavaDoc data, UserDataHandler JavaDoc handler) {
1025            fData = data;
1026            fHandler = handler;
1027        }
1028    }
1029} // class ParentNode
1030
Popular Tags