KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > xpath > datamodel > xerces > dom > ParentNode


1 /* $Id: ParentNode.java,v 1.1 2003/10/01 16:48:50 lars Exp $ */
2 /*
3  * The Apache Software License, Version 1.1
4  *
5  *
6  * Copyright (c) 1999-2000 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.xquark.xpath.datamodel.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.*;
66 import org.w3c.dom.events.MutationEvent JavaDoc;
67 import org.xquark.xpath.datamodel.xerces.dom.events.MutationEventImpl;
68
69 /**
70  * ParentNode inherits from ChildImpl and adds the capability of having child
71  * nodes. Not every node in the DOM can have children, so only nodes that can
72  * should inherit from this class and pay the price for it.
73  * <P>
74  * ParentNode, just like NodeImpl, also implements NodeList, so it can
75  * return itself in response to the getChildNodes() query. This eliminiates
76  * the need for a separate ChildNodeList object. Note that this is an
77  * IMPLEMENTATION DETAIL; applications should _never_ assume that
78  * this identity exists. On the other hand, subclasses may need to override
79  * this, in case of conflicting names. This is the case for the classes
80  * HTMLSelectElementImpl and HTMLFormElementImpl of the HTML DOM.
81  * <P>
82  * While we have a direct reference to the first child, the last child is
83  * stored as the previous sibling of the first child. First child nodes are
84  * marked as being so, and getNextSibling hides this fact.
85  * <P>Note: Not all parent nodes actually need to also be a child. At some
86  * point we used to have ParentNode inheriting from NodeImpl and another class
87  * called ChildAndParentNode that inherited from ChildNode. But due to the lack
88  * of multiple inheritance a lot of code had to be duplicated which led to a
89  * maintenance nightmare. At the same time only a few nodes (Document,
90  * DocumentFragment, Entity, and Attribute) cannot be a child so the gain is
91  * memory wasn't really worth it. The only type for which this would be the
92  * case is Attribute, but we deal with there in another special way, so this is
93  * not applicable.
94  *
95  * <p><b>WARNING</b>: Some of the code here is partially duplicated in
96  * AttrImpl, be careful to keep these two classes in sync!
97  *
98  * @author Arnaud Le Hors, IBM
99  * @author Joe Kesselman, IBM
100  * @author Andy Clark, IBM
101  */

102 public abstract class ParentNode
103     extends ChildNode {
104
105     /** Serialization version. */
106     static final long serialVersionUID = 2815829867152120872L;
107
108     /** Owner document. */
109     protected DocumentImpl ownerDocument;
110
111     /** First child. */
112     protected ChildNode firstChild = null;
113
114     // transients
115

116     /** Cached node list length. */
117     protected transient int fCachedLength = -1;
118
119     /** Last requested node. */
120     protected transient ChildNode fCachedChild;
121
122     /** Last requested node index. */
123     protected transient int fCachedChildIndex = -1;
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(DocumentImpl 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 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.fCachedChildIndex = -1;
178         newnode.fCachedLength = -1;
179
180         // Then, if deep, clone the kids too.
181
if (deep) {
182             for (ChildNode child = firstChild;
183                  child != null;
184                  child = child.nextSibling) {
185                 newnode.appendChild(child.cloneNode(true));
186             }
187         }
188
189         return newnode;
190
191     } // cloneNode(boolean):Node
192

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

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

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

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

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

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

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

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

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

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

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

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

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

351             // No need to check kids for right-document; if they weren't,
352
// they wouldn't be kids of that DocFrag.
353
if (errorChecking) {
354                 for (Node kid = newChild.getFirstChild(); // Prescan
355
kid != null; kid = kid.getNextSibling()) {
356
357                     if (!ownerDocument.isKidOK(this, kid)) {
358                         throw new DOMException(
359                                            DOMException.HIERARCHY_REQUEST_ERR,
360                                            "DOM006 Hierarchy request error");
361                     }
362                 }
363             }
364
365             while (newChild.hasChildNodes()) {
366                 insertBefore(newChild.getFirstChild(), refChild);
367             }
368             return newChild;
369         }
370
371         if (newChild == refChild) {
372             // stupid case that must be handled as a no-op triggering events...
373
refChild = refChild.getNextSibling();
374             removeChild(newChild);
375             insertBefore(newChild, refChild);
376             return newChild;
377         }
378
379         if (needsSyncChildren()) {
380             synchronizeChildren();
381         }
382
383         if (errorChecking) {
384             if (isReadOnly()) {
385                 throw new DOMException(
386                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
387                                        "DOM001 Modification not allowed");
388             }
389             if (newChild.getOwnerDocument() != ownerDocument) {
390                 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
391                                        "DOM005 Wrong document");
392             }
393             if (!ownerDocument.isKidOK(this, newChild)) {
394                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
395                                        "DOM006 Hierarchy request error");
396             }
397             // refChild must be a child of this node (or null)
398
if (refChild != null && refChild.getParentNode() != this) {
399                 throw new DOMException(DOMException.NOT_FOUND_ERR,
400                                        "DOM008 Not found");
401             }
402
403             // Prevent cycles in the tree
404
// newChild cannot be ancestor of this Node,
405
// and actually cannot be this
406
boolean treeSafe = true;
407             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
408             {
409                 treeSafe = newChild != a;
410             }
411             if(!treeSafe) {
412                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
413                                        "DOM006 Hierarchy request error");
414             }
415         }
416
417         EnclosingAttr enclosingAttr=null;
418         if (MUTATIONEVENTS && ownerDocument.mutationEvents
419             && (mutationMask&MUTATION_AGGREGATE)!=0) {
420             // MUTATION PREPROCESSING
421
// No direct pre-events, but if we're within the scope
422
// of an Attr and DOMAttrModified was requested,
423
// we need to preserve its previous value.
424
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
425             if (lc.captures+lc.bubbles+lc.defaults>0) {
426                 enclosingAttr=getEnclosingAttr();
427             }
428         }
429
430         // Convert to internal type, to avoid repeated casting
431
ChildNode newInternal = (ChildNode)newChild;
432
433         Node oldparent = newInternal.parentNode();
434         if (oldparent != null) {
435             oldparent.removeChild(newInternal);
436         }
437
438         // Convert to internal type, to avoid repeated casting
439
ChildNode refInternal = (ChildNode)refChild;
440
441         // Attach up
442
newInternal.ownerNode = this;
443         newInternal.isOwned(true);
444
445         // Attach before and after
446
// Note: firstChild.previousSibling == lastChild!!
447
if (firstChild == null) {
448             // this our first and only child
449
firstChild = newInternal;
450             newInternal.isFirstChild(true);
451             newInternal.previousSibling = newInternal;
452         }
453         else {
454             if (refInternal == null) {
455                 // this is an append
456
ChildNode lastChild = firstChild.previousSibling;
457                 lastChild.nextSibling = newInternal;
458                 newInternal.previousSibling = lastChild;
459                 firstChild.previousSibling = newInternal;
460             }
461             else {
462                 // this is an insert
463
if (refChild == firstChild) {
464                     // at the head of the list
465
firstChild.isFirstChild(false);
466                     newInternal.nextSibling = firstChild;
467                     newInternal.previousSibling = firstChild.previousSibling;
468                     firstChild.previousSibling = newInternal;
469                     firstChild = newInternal;
470                     newInternal.isFirstChild(true);
471                 }
472                 else {
473                     // somewhere in the middle
474
ChildNode prev = refInternal.previousSibling;
475                     newInternal.nextSibling = refInternal;
476                     prev.nextSibling = newInternal;
477                     refInternal.previousSibling = newInternal;
478                     newInternal.previousSibling = prev;
479                 }
480             }
481         }
482
483         changed();
484
485         // update cached length if we have any
486
if (fCachedLength != -1) {
487             fCachedLength++;
488         }
489         if (fCachedChildIndex != -1) {
490             // if we happen to insert just before the cached node, update
491
// the cache to the new node to match the cached index
492
if (fCachedChild == refInternal) {
493                 fCachedChild = newInternal;
494             } else {
495                 // otherwise just invalidate the cache
496
fCachedChildIndex = -1;
497             }
498         }
499
500         if (MUTATIONEVENTS && ownerDocument.mutationEvents) {
501             // MUTATION POST-EVENTS:
502
// "Local" events (non-aggregated)
503
if ((mutationMask&MUTATION_LOCAL) != 0) {
504                 // New child is told it was inserted, and where
505
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
506                 if (lc.captures+lc.bubbles+lc.defaults>0) {
507                     MutationEvent JavaDoc me= new MutationEventImpl();
508                     me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED,
509                                          true,false,this,null,
510                                           null,null,(short)0);
511                     newInternal.dispatchEvent(me);
512                 }
513
514                 // If within the Document, tell the subtree it's been added
515
// to the Doc.
516
lc=LCount.lookup(
517                             MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
518                 if (lc.captures+lc.bubbles+lc.defaults>0) {
519                     NodeImpl eventAncestor=this;
520                     if (enclosingAttr!=null)
521                         eventAncestor=
522                             (NodeImpl)(enclosingAttr.node.getOwnerElement());
523                     if (eventAncestor!=null) { // Might have been orphan Attr
524
NodeImpl p=eventAncestor;
525                         while (p!=null) {
526                             eventAncestor=p; // Last non-null ancestor
527
// In this context, ancestry includes
528
// walking back from Attr to Element
529
if(p.getNodeType()==ATTRIBUTE_NODE) {
530                                 p=(ElementImpl)((AttrImpl)p).getOwnerElement();
531                             }
532                             else {
533                                 p=p.parentNode();
534                             }
535                         }
536                         if(eventAncestor.getNodeType()==Node.DOCUMENT_NODE) {
537                             MutationEvent JavaDoc me= new MutationEventImpl();
538                             me.initMutationEvent(MutationEventImpl
539                                               .DOM_NODE_INSERTED_INTO_DOCUMENT,
540                                                  false,false,null,null,
541                                                  null,null,(short)0);
542                             dispatchEventToSubtree(newInternal,me);
543                         }
544                     }
545                 }
546             }
547
548             // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
549
// (Common to most kinds of mutation)
550
if ((mutationMask&MUTATION_AGGREGATE) != 0) {
551                 dispatchAggregateEvents(enclosingAttr);
552             }
553         }
554
555         checkNormalizationAfterInsert(newInternal);
556
557         return newChild;
558
559     } // internalInsertBefore(Node,Node,int):Node
560

561     /**
562      * Remove a child from this Node. The removed child's subtree
563      * remains intact so it may be re-inserted elsewhere.
564      *
565      * @return oldChild, in its new state (removed).
566      *
567      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
568      * this node.
569      *
570      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
571      * read-only.
572      */

573     public Node removeChild(Node oldChild)
574         throws DOMException {
575         // Tail-call, should be optimizable
576
return internalRemoveChild(oldChild,MUTATION_ALL);
577     } // removeChild(Node) :Node
578

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

584     Node internalRemoveChild(Node oldChild,int mutationMask)
585         throws DOMException {
586
587         DocumentImpl ownerDocument = ownerDocument();
588         if (ownerDocument.errorChecking) {
589             if (isReadOnly()) {
590                 throw new DOMException(
591                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
592                                      "DOM001 Modification not allowed");
593             }
594             if (oldChild != null && oldChild.getParentNode() != this) {
595                 throw new DOMException(DOMException.NOT_FOUND_ERR,
596                                        "DOM008 Not found");
597             }
598         }
599
600         // notify document
601
ownerDocument.removedChildNode(oldChild);
602
603         ChildNode oldInternal = (ChildNode) oldChild;
604
605         EnclosingAttr enclosingAttr=null;
606         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
607         {
608             // MUTATION PREPROCESSING AND PRE-EVENTS:
609
// If we're within the scope of an Attr and DOMAttrModified
610
// was requested, we need to preserve its previous value for
611
// that event.
612
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
613             if(lc.captures+lc.bubbles+lc.defaults>0)
614             {
615                 enclosingAttr=getEnclosingAttr();
616             }
617             
618             if( (mutationMask&MUTATION_LOCAL) != 0)
619             {
620                 // Child is told that it is about to be removed
621
lc=LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
622                 if(lc.captures+lc.bubbles+lc.defaults>0)
623                 {
624                     MutationEvent JavaDoc me= new MutationEventImpl();
625                     me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
626                                          true,false,this,null,
627                                          null,null,(short)0);
628                     oldInternal.dispatchEvent(me);
629                 }
630             
631                 // If within Document, child's subtree is informed that it's
632
// losing that status
633
lc=LCount.lookup(
634                              MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
635                 if(lc.captures+lc.bubbles+lc.defaults>0)
636                 {
637                     NodeImpl eventAncestor=this;
638                     if(enclosingAttr!=null)
639                         eventAncestor=
640                             (NodeImpl) enclosingAttr.node.getOwnerElement();
641                     if(eventAncestor!=null) // Might have been orphan Attr
642
{
643                         for(NodeImpl p=eventAncestor.parentNode();
644                             p!=null;
645                             p=p.parentNode())
646                         {
647                             eventAncestor=p; // Last non-null ancestor
648
}
649                         if(eventAncestor.getNodeType()==Node.DOCUMENT_NODE)
650                         {
651                             MutationEvent JavaDoc me= new MutationEventImpl();
652                             me.initMutationEvent(MutationEventImpl
653                                                .DOM_NODE_REMOVED_FROM_DOCUMENT,
654                                                  false,false,
655                                                  null,null,null,null,(short)0);
656                             dispatchEventToSubtree(oldInternal,me);
657                         }
658                     }
659                 }
660             }
661         } // End mutation preprocessing
662

663         // update cached length if we have any
664
if (fCachedLength != -1) {
665             fCachedLength--;
666         }
667         if (fCachedChildIndex != -1) {
668             // if the removed node is the cached node
669
// move the cache to its (soon former) previous sibling
670
if (fCachedChild == oldInternal) {
671                 fCachedChildIndex--;
672                 fCachedChild = oldInternal.previousSibling();
673             } else {
674                 // otherwise just invalidate the cache
675
fCachedChildIndex = -1;
676             }
677         }
678
679         // Patch linked list around oldChild
680
// Note: lastChild == firstChild.previousSibling
681
if (oldInternal == firstChild) {
682             // removing first child
683
oldInternal.isFirstChild(false);
684             firstChild = oldInternal.nextSibling;
685             if (firstChild != null) {
686                 firstChild.isFirstChild(true);
687                 firstChild.previousSibling = oldInternal.previousSibling;
688             }
689         } else {
690             ChildNode prev = oldInternal.previousSibling;
691             ChildNode next = oldInternal.nextSibling;
692             prev.nextSibling = next;
693             if (next == null) {
694                 // removing last child
695
firstChild.previousSibling = prev;
696             } else {
697                 // removing some other child in the middle
698
next.previousSibling = prev;
699             }
700         }
701
702         // Save previous sibling for normalization checking.
703
ChildNode oldPreviousSibling = oldInternal.previousSibling();
704
705         // Remove oldInternal's references to tree
706
oldInternal.ownerNode = ownerDocument;
707         oldInternal.isOwned(false);
708         oldInternal.nextSibling = null;
709         oldInternal.previousSibling = null;
710
711         changed();
712
713         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
714         {
715             // MUTATION POST-EVENTS:
716
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
717
// if required. (Common to most kinds of mutation)
718
if( (mutationMask&MUTATION_AGGREGATE) != 0)
719                 dispatchAggregateEvents(enclosingAttr);
720         } // End mutation postprocessing
721

722         checkNormalizationAfterRemove(oldPreviousSibling);
723
724         return oldInternal;
725
726     } // internalRemoveChild(Node,int):Node
727

728     /**
729      * Make newChild occupy the location that oldChild used to
730      * have. Note that newChild will first be removed from its previous
731      * parent, if any. Equivalent to inserting newChild before oldChild,
732      * then removing oldChild.
733      *
734      * @returns oldChild, in its new state (removed).
735      *
736      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
737      * type that shouldn't be a child of this node, or if newChild is
738      * one of our ancestors.
739      *
740      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
741      * different owner document than we do.
742      *
743      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
744      * this node.
745      *
746      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
747      * read-only.
748      */

749     public Node replaceChild(Node newChild, Node oldChild)
750         throws DOMException {
751         // If Mutation Events are being generated, this operation might
752
// throw aggregate events twice when modifying an Attr -- once
753
// on insertion and once on removal. DOM Level 2 does not specify
754
// this as either desirable or undesirable, but hints that
755
// aggregations should be issued only once per user request.
756

757         EnclosingAttr enclosingAttr=null;
758         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
759         {
760             // MUTATION PREPROCESSING AND PRE-EVENTS:
761
// If we're within the scope of an Attr and DOMAttrModified
762
// was requested, we need to preserve its previous value for
763
// that event.
764
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
765             if(lc.captures+lc.bubbles+lc.defaults>0)
766             {
767                 enclosingAttr=getEnclosingAttr();
768             }
769         } // End mutation preprocessing
770

771         internalInsertBefore(newChild, oldChild,MUTATION_LOCAL);
772         if (newChild != oldChild) {
773             internalRemoveChild(oldChild,MUTATION_LOCAL);
774         }
775
776         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
777         {
778             dispatchAggregateEvents(enclosingAttr);
779         }
780
781         return oldChild;
782     }
783
784     //
785
// NodeList methods
786
//
787

788     /**
789      * Count the immediate children of this node. Use to implement
790      * NodeList.getLength().
791      * @return int
792      */

793     private int nodeListGetLength() {
794
795         if (fCachedLength == -1) { // is the cached length invalid ?
796
ChildNode node;
797             // start from the cached node if we have one
798
if (fCachedChildIndex != -1 && fCachedChild != null) {
799                 fCachedLength = fCachedChildIndex;
800                 node = fCachedChild;
801             } else {
802                 node = firstChild;
803                 fCachedLength = 0;
804             }
805             for (; node != null; node = node.nextSibling) {
806                 fCachedLength++;
807             }
808         }
809
810         return fCachedLength;
811
812     } // nodeListGetLength():int
813

814     /**
815      * NodeList method: Count the immediate children of this node
816      * @return int
817      */

818     public int getLength() {
819         return nodeListGetLength();
820     }
821
822     /**
823      * Return the Nth immediate child of this node, or null if the index is
824      * out of bounds. Use to implement NodeList.item().
825      * @param index int
826      */

827     private Node nodeListItem(int index) {
828         // short way
829
if (fCachedChildIndex != -1 && fCachedChild != null) {
830             if (fCachedChildIndex < index) {
831                 while (fCachedChildIndex < index && fCachedChild != null) {
832                     fCachedChildIndex++;
833                     fCachedChild = fCachedChild.nextSibling;
834                 }
835             }
836             else if (fCachedChildIndex > index) {
837                 while (fCachedChildIndex > index && fCachedChild != null) {
838                     fCachedChildIndex--;
839                     fCachedChild = fCachedChild.previousSibling();
840                 }
841             }
842             return fCachedChild;
843         }
844
845         // long way
846
fCachedChild = firstChild;
847         for (fCachedChildIndex = 0;
848              fCachedChildIndex < index && fCachedChild != null;
849              fCachedChildIndex++) {
850             fCachedChild = fCachedChild.nextSibling;
851         }
852         return fCachedChild;
853
854     } // nodeListItem(int):Node
855

856     /**
857      * NodeList method: Return the Nth immediate child of this node, or
858      * null if the index is out of bounds.
859      * @return org.w3c.dom.Node
860      * @param index int
861      */

862     public Node item(int index) {
863         return nodeListItem(index);
864     } // item(int):Node
865

866     /**
867      * Create a NodeList to access children that is use by subclass elements
868      * that have methods named getLength() or item(int). ChildAndParentNode
869      * optimizes getChildNodes() by implementing NodeList itself. However if
870      * a subclass Element implements methods with the same name as the NodeList
871      * methods, they will override the actually methods in this class.
872      * <p>
873      * To use this method, the subclass should implement getChildNodes() and
874      * have it call this method. The resulting NodeList instance maybe
875      * shared and cached in a transient field, but the cached value must be
876      * cleared if the node is cloned.
877      */

878     protected final NodeList getChildNodesUnoptimized() {
879         if (needsSyncChildren()) {
880             synchronizeChildren();
881         }
882         return new NodeList() {
883                 /**
884                  * @see NodeList.getLength()
885                  */

886                 public int getLength() {
887                     return nodeListGetLength();
888                 } // getLength():int
889

890                 /**
891                  * @see NodeList.item(int)
892                  */

893                 public Node item(int index) {
894                     return nodeListItem(index);
895                 } // item(int):Node
896
};
897     } // getChildNodesUnoptimized():NodeList
898

899     //
900
// DOM2: methods, getters, setters
901
//
902

903     /**
904      * Override default behavior to call normalize() on this Node's
905      * children. It is up to implementors or Node to override normalize()
906      * to take action.
907      */

908     public void normalize() {
909         // No need to normalize if already normalized.
910
if (isNormalized()) {
911             return;
912         }
913         if (needsSyncChildren()) {
914             synchronizeChildren();
915         }
916         ChildNode kid;
917         for (kid = firstChild; kid != null; kid = kid.nextSibling) {
918             kid.normalize();
919         }
920         isNormalized(true);
921     }
922
923     //
924
// Public methods
925
//
926

927     /**
928      * Override default behavior so that if deep is true, children are also
929      * toggled.
930      * @see Node
931      * <P>
932      * Note: this will not change the state of an EntityReference or its
933      * children, which are always read-only.
934      */

935     public void setReadOnly(boolean readOnly, boolean deep) {
936
937         super.setReadOnly(readOnly, deep);
938
939         if (deep) {
940
941             if (needsSyncChildren()) {
942                 synchronizeChildren();
943             }
944
945             // Recursively set kids
946
for (ChildNode mykid = firstChild;
947                  mykid != null;
948                  mykid = mykid.nextSibling) {
949                 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
950                     mykid.setReadOnly(readOnly,true);
951                 }
952             }
953         }
954     } // setReadOnly(boolean,boolean)
955

956     //
957
// Protected methods
958
//
959

960     /**
961      * Override this method in subclass to hook in efficient
962      * internal data structure.
963      */

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

985     void checkNormalizationAfterInsert(ChildNode insertedChild) {
986         // See if insertion caused this node to be unnormalized.
987
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
988             ChildNode prev = insertedChild.previousSibling();
989             ChildNode next = insertedChild.nextSibling;
990             // If an adjacent sibling of the new child is a text node,
991
// flag this node as unnormalized.
992
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
993                 (next != null && next.getNodeType() == Node.TEXT_NODE)) {
994                 isNormalized(false);
995             }
996         }
997         else {
998             // If the new child is not normalized,
999
// then this node is inherently not normalized.
1000
if (!insertedChild.isNormalized()) {
1001                isNormalized(false);
1002            }
1003        }
1004    } // checkNormalizationAfterInsert(ChildNode)
1005

1006    /**
1007     * Checks the normalized of this node after removing a child.
1008     * If the removed child causes this node to be unnormalized, then this
1009     * node is flagged accordingly.
1010     * The conditions for changing the normalized state are:
1011     * <ul>
1012     * <li>The removed child had two adjacent siblings that were text nodes.
1013     * </ul>
1014     *
1015     * @param previousSibling the previous sibling of the removed child, or
1016     * <code>null</code>
1017     */

1018    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1019        // See if removal caused this node to be unnormalized.
1020
// If the adjacent siblings of the removed child were both text nodes,
1021
// flag this node as unnormalized.
1022
if (previousSibling != null &&
1023            previousSibling.getNodeType() == Node.TEXT_NODE) {
1024
1025            ChildNode next = previousSibling.nextSibling;
1026            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1027                isNormalized(false);
1028            }
1029        }
1030    } // checkNormalizationAfterRemove(Node)
1031

1032    //
1033
// Serialization methods
1034
//
1035

1036    /** Serialize object. */
1037    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1038
1039        // synchronize chilren
1040
if (needsSyncChildren()) {
1041            synchronizeChildren();
1042        }
1043        // write object
1044
out.defaultWriteObject();
1045
1046    } // writeObject(ObjectOutputStream)
1047

1048    /** Deserialize object. */
1049    private void readObject(ObjectInputStream JavaDoc ois)
1050        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1051
1052        // perform default deseralization
1053
ois.defaultReadObject();
1054
1055        // hardset synchildren - so we don't try to sync- it does not make any sense
1056
// to try to synchildren when we just desealize object.
1057

1058        needsSyncChildren(false);
1059
1060        // initialize transients
1061
fCachedLength = -1;
1062        fCachedChildIndex = -1;
1063
1064    } // readObject(ObjectInputStream)
1065

1066} // class ParentNode
1067
Popular Tags