KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 1999-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.util.Vector JavaDoc;
20
21 import org.w3c.dom.CharacterData JavaDoc;
22 import org.w3c.dom.DOMException JavaDoc;
23 import org.w3c.dom.DocumentFragment JavaDoc;
24 import org.w3c.dom.Node JavaDoc;
25 import org.w3c.dom.ranges.Range;
26 import org.w3c.dom.ranges.RangeException;
27
28
29 /** The RangeImpl class implements the org.w3c.dom.range.Range interface.
30  * <p> Please see the API documentation for the interface classes
31  * and use the interfaces in your client programs.
32  *
33  * @xerces.internal
34  *
35  * @version $Id: RangeImpl.java,v 1.39 2005/03/07 23:28:40 mrglavas Exp $
36  */

37 public class RangeImpl implements Range {
38     
39     //
40
// Constants
41
//
42

43
44     //
45
// Data
46
//
47

48     DocumentImpl fDocument;
49     Node JavaDoc fStartContainer;
50     Node JavaDoc fEndContainer;
51     int fStartOffset;
52     int fEndOffset;
53     boolean fIsCollapsed;
54     boolean fDetach = false;
55     Node JavaDoc fInsertNode = null;
56     Node JavaDoc fDeleteNode = null;
57     Node JavaDoc fSplitNode = null;
58     // Was the Node inserted from the Range or the Document
59
boolean fInsertedFromRange = false;
60     
61     /** The constructor. Clients must use DocumentRange.createRange(),
62      * because it registers the Range with the document, so it can
63      * be fixed-up.
64      */

65     public RangeImpl(DocumentImpl document) {
66         fDocument = document;
67         fStartContainer = document;
68         fEndContainer = document;
69         fStartOffset = 0;
70         fEndOffset = 0;
71         fDetach = false;
72     }
73     
74     public Node JavaDoc getStartContainer() {
75         if ( fDetach ) {
76             throw new DOMException JavaDoc(
77                 DOMException.INVALID_STATE_ERR,
78                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
79         }
80         return fStartContainer;
81     }
82     
83     public int getStartOffset() {
84         if ( fDetach ) {
85             throw new DOMException JavaDoc(
86                 DOMException.INVALID_STATE_ERR,
87                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
88         }
89         return fStartOffset;
90     }
91     
92     public Node JavaDoc getEndContainer() {
93         if ( fDetach ) {
94             throw new DOMException JavaDoc(
95                 DOMException.INVALID_STATE_ERR,
96                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
97         }
98         return fEndContainer;
99     }
100     
101     public int getEndOffset() {
102         if ( fDetach ) {
103             throw new DOMException JavaDoc(
104                 DOMException.INVALID_STATE_ERR,
105                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
106         }
107         return fEndOffset;
108     }
109     
110     public boolean getCollapsed() {
111         if ( fDetach ) {
112             throw new DOMException JavaDoc(
113                 DOMException.INVALID_STATE_ERR,
114                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
115         }
116         return (fStartContainer == fEndContainer
117              && fStartOffset == fEndOffset);
118     }
119     
120     public Node JavaDoc getCommonAncestorContainer() {
121         if ( fDetach ) {
122             throw new DOMException JavaDoc(
123                 DOMException.INVALID_STATE_ERR,
124                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
125         }
126         Vector JavaDoc startV = new Vector JavaDoc();
127         Node JavaDoc node;
128         for (node=fStartContainer; node != null;
129              node=node.getParentNode())
130         {
131             startV.addElement(node);
132         }
133         Vector JavaDoc endV = new Vector JavaDoc();
134         for (node=fEndContainer; node != null;
135              node=node.getParentNode())
136         {
137             endV.addElement(node);
138         }
139         int s = startV.size()-1;
140         int e = endV.size()-1;
141         Object JavaDoc result = null;
142         while (s>=0 && e>=0) {
143             if (startV.elementAt(s) == endV.elementAt(e)) {
144                 result = startV.elementAt(s);
145             } else {
146                 break;
147             }
148             --s;
149             --e;
150         }
151         return (Node JavaDoc)result;
152     }
153     
154     
155     public void setStart(Node JavaDoc refNode, int offset)
156                          throws RangeException, DOMException JavaDoc
157     {
158         if (fDocument.errorChecking) {
159             if ( fDetach) {
160                 throw new DOMException JavaDoc(
161                         DOMException.INVALID_STATE_ERR,
162                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
163             }
164             if ( !isLegalContainer(refNode)) {
165                 throw new RangeExceptionImpl(
166                         RangeException.INVALID_NODE_TYPE_ERR,
167                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
168             }
169             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
170                 throw new DOMException JavaDoc(
171                         DOMException.WRONG_DOCUMENT_ERR,
172                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
173             }
174         }
175         
176         checkIndex(refNode, offset);
177         
178         fStartContainer = refNode;
179         fStartOffset = offset;
180         
181         // If one boundary-point of a Range is set to have a root container
182
// other
183
// than the current one for the Range, the Range should be collapsed to
184
// the new position.
185
// The start position of a Range should never be after the end position.
186
if (getCommonAncestorContainer() == null
187                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
188             collapse(true);
189         }
190     }
191     
192     public void setEnd(Node JavaDoc refNode, int offset)
193                        throws RangeException, DOMException JavaDoc
194     {
195         if (fDocument.errorChecking) {
196             if (fDetach) {
197                 throw new DOMException JavaDoc(
198                         DOMException.INVALID_STATE_ERR,
199                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
200             }
201             if ( !isLegalContainer(refNode)) {
202                 throw new RangeExceptionImpl(
203                         RangeException.INVALID_NODE_TYPE_ERR,
204                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
205             }
206             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
207                 throw new DOMException JavaDoc(
208                         DOMException.WRONG_DOCUMENT_ERR,
209                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
210             }
211         }
212         
213         checkIndex(refNode, offset);
214         
215         fEndContainer = refNode;
216         fEndOffset = offset;
217         
218         // If one boundary-point of a Range is set to have a root container
219
// other
220
// than the current one for the Range, the Range should be collapsed to
221
// the new position.
222
// The start position of a Range should never be after the end position.
223
if (getCommonAncestorContainer() == null
224                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
225             collapse(false);
226         }
227     }
228
229     public void setStartBefore(Node JavaDoc refNode)
230         throws RangeException
231     {
232         if (fDocument.errorChecking) {
233             if (fDetach) {
234                 throw new DOMException JavaDoc(
235                         DOMException.INVALID_STATE_ERR,
236                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
237             }
238             if ( !hasLegalRootContainer(refNode) ||
239                     !isLegalContainedNode(refNode) )
240             {
241                 throw new RangeExceptionImpl(
242                         RangeException.INVALID_NODE_TYPE_ERR,
243                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
244             }
245             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
246                 throw new DOMException JavaDoc(
247                         DOMException.WRONG_DOCUMENT_ERR,
248                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
249             }
250         }
251         
252         fStartContainer = refNode.getParentNode();
253         int i = 0;
254         for (Node JavaDoc n = refNode; n!=null; n = n.getPreviousSibling()) {
255             i++;
256         }
257         fStartOffset = i-1;
258         
259         // If one boundary-point of a Range is set to have a root container
260
// other
261
// than the current one for the Range, the Range should be collapsed to
262
// the new position.
263
// The start position of a Range should never be after the end position.
264
if (getCommonAncestorContainer() == null
265                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
266             collapse(true);
267         }
268     }
269     
270     public void setStartAfter(Node JavaDoc refNode)
271         throws RangeException
272     {
273         if (fDocument.errorChecking) {
274             if (fDetach) {
275                 throw new DOMException JavaDoc(
276                         DOMException.INVALID_STATE_ERR,
277                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
278             }
279             if ( !hasLegalRootContainer(refNode) ||
280                     !isLegalContainedNode(refNode)) {
281                 throw new RangeExceptionImpl(
282                         RangeException.INVALID_NODE_TYPE_ERR,
283                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
284             }
285             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
286                 throw new DOMException JavaDoc(
287                         DOMException.WRONG_DOCUMENT_ERR,
288                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
289             }
290         }
291         fStartContainer = refNode.getParentNode();
292         int i = 0;
293         for (Node JavaDoc n = refNode; n!=null; n = n.getPreviousSibling()) {
294             i++;
295         }
296         fStartOffset = i;
297         
298         // If one boundary-point of a Range is set to have a root container
299
// other
300
// than the current one for the Range, the Range should be collapsed to
301
// the new position.
302
// The start position of a Range should never be after the end position.
303
if (getCommonAncestorContainer() == null
304                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
305             collapse(true);
306         }
307     }
308     
309     public void setEndBefore(Node JavaDoc refNode)
310         throws RangeException
311     {
312         if (fDocument.errorChecking) {
313             if (fDetach) {
314                 throw new DOMException JavaDoc(
315                         DOMException.INVALID_STATE_ERR,
316                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
317             }
318             if ( !hasLegalRootContainer(refNode) ||
319                     !isLegalContainedNode(refNode)) {
320                 throw new RangeExceptionImpl(
321                         RangeException.INVALID_NODE_TYPE_ERR,
322                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
323             }
324             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
325                 throw new DOMException JavaDoc(
326                         DOMException.WRONG_DOCUMENT_ERR,
327                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
328             }
329         }
330         fEndContainer = refNode.getParentNode();
331         int i = 0;
332         for (Node JavaDoc n = refNode; n!=null; n = n.getPreviousSibling()) {
333             i++;
334         }
335         fEndOffset = i-1;
336
337         // If one boundary-point of a Range is set to have a root container
338
// other
339
// than the current one for the Range, the Range should be collapsed to
340
// the new position.
341
// The start position of a Range should never be after the end position.
342
if (getCommonAncestorContainer() == null
343                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
344             collapse(false);
345         }
346     }
347                                             
348     public void setEndAfter(Node JavaDoc refNode)
349         throws RangeException
350     {
351         if (fDocument.errorChecking) {
352             if( fDetach) {
353                 throw new DOMException JavaDoc(
354                         DOMException.INVALID_STATE_ERR,
355                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
356             }
357             if ( !hasLegalRootContainer(refNode) ||
358                     !isLegalContainedNode(refNode)) {
359                 throw new RangeExceptionImpl(
360                         RangeException.INVALID_NODE_TYPE_ERR,
361                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
362             }
363             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
364                 throw new DOMException JavaDoc(
365                         DOMException.WRONG_DOCUMENT_ERR,
366                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
367             }
368         }
369         fEndContainer = refNode.getParentNode();
370         int i = 0;
371         for (Node JavaDoc n = refNode; n!=null; n = n.getPreviousSibling()) {
372             i++;
373         }
374         fEndOffset = i;
375         
376         // If one boundary-point of a Range is set to have a root container
377
// other
378
// than the current one for the Range, the Range should be collapsed to
379
// the new position.
380
// The start position of a Range should never be after the end position.
381
if (getCommonAncestorContainer() == null
382                 || (fStartContainer == fEndContainer && fEndOffset < fStartOffset)) {
383             collapse(false);
384         }
385     }
386     
387     public void collapse(boolean toStart) {
388         
389         if( fDetach) {
390             throw new DOMException JavaDoc(
391             DOMException.INVALID_STATE_ERR,
392                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
393         }
394         
395         if (toStart) {
396             fEndContainer = fStartContainer;
397             fEndOffset = fStartOffset;
398         } else {
399             fStartContainer = fEndContainer;
400             fStartOffset = fEndOffset;
401         }
402     }
403     
404     public void selectNode(Node JavaDoc refNode)
405         throws RangeException
406     {
407         if (fDocument.errorChecking) {
408             if (fDetach) {
409                 throw new DOMException JavaDoc(
410                         DOMException.INVALID_STATE_ERR,
411                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
412             }
413             if ( !isLegalContainer( refNode.getParentNode() ) ||
414                     !isLegalContainedNode( refNode ) ) {
415                 throw new RangeExceptionImpl(
416                         RangeException.INVALID_NODE_TYPE_ERR,
417                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
418             }
419             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
420                 throw new DOMException JavaDoc(
421                         DOMException.WRONG_DOCUMENT_ERR,
422                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
423             }
424         }
425         Node JavaDoc parent = refNode.getParentNode();
426         if (parent != null ) // REVIST: what to do if it IS null?
427
{
428             fStartContainer = parent;
429             fEndContainer = parent;
430             int i = 0;
431             for (Node JavaDoc n = refNode; n!=null; n = n.getPreviousSibling()) {
432                 i++;
433             }
434             fStartOffset = i-1;
435             fEndOffset = fStartOffset+1;
436         }
437     }
438         
439     public void selectNodeContents(Node JavaDoc refNode)
440         throws RangeException
441     {
442         if (fDocument.errorChecking) {
443             if( fDetach) {
444                 throw new DOMException JavaDoc(
445                         DOMException.INVALID_STATE_ERR,
446                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
447             }
448             if ( !isLegalContainer(refNode)) {
449                 throw new RangeExceptionImpl(
450                         RangeException.INVALID_NODE_TYPE_ERR,
451                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
452             }
453             if ( fDocument != refNode.getOwnerDocument() && fDocument != refNode) {
454                 throw new DOMException JavaDoc(
455                         DOMException.WRONG_DOCUMENT_ERR,
456                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
457             }
458         }
459         fStartContainer = refNode;
460         fEndContainer = refNode;
461         Node JavaDoc first = refNode.getFirstChild();
462         fStartOffset = 0;
463         if (first == null) {
464             fEndOffset = 0;
465         } else {
466             int i = 0;
467             for (Node JavaDoc n = first; n!=null; n = n.getNextSibling()) {
468                 i++;
469             }
470             fEndOffset = i;
471         }
472         
473     }
474
475     public short compareBoundaryPoints(short how, Range sourceRange)
476         throws DOMException JavaDoc
477     {
478         if (fDocument.errorChecking) {
479             if( fDetach) {
480                 throw new DOMException JavaDoc(
481                         DOMException.INVALID_STATE_ERR,
482                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
483             }
484             // WRONG_DOCUMENT_ERR: Raised if the two Ranges are not in the same Document or DocumentFragment.
485
if ((fDocument != sourceRange.getStartContainer().getOwnerDocument()
486                     && fDocument != sourceRange.getStartContainer()
487                     && sourceRange.getStartContainer() != null)
488                     || (fDocument != sourceRange.getEndContainer().getOwnerDocument()
489                             && fDocument != sourceRange.getEndContainer()
490                             && sourceRange.getStartContainer() != null)) {
491                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
492                         DOMMessageFormatter.formatMessage( DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
493             }
494         }
495         
496         Node JavaDoc endPointA;
497         Node JavaDoc endPointB;
498         int offsetA;
499         int offsetB;
500         
501         if (how == START_TO_START) {
502             endPointA = sourceRange.getStartContainer();
503             endPointB = fStartContainer;
504             offsetA = sourceRange.getStartOffset();
505             offsetB = fStartOffset;
506         } else
507         if (how == START_TO_END) {
508             endPointA = sourceRange.getStartContainer();
509             endPointB = fEndContainer;
510             offsetA = sourceRange.getStartOffset();
511             offsetB = fEndOffset;
512         } else
513         if (how == END_TO_START) {
514             endPointA = sourceRange.getEndContainer();
515             endPointB = fStartContainer;
516             offsetA = sourceRange.getEndOffset();
517             offsetB = fStartOffset;
518         } else {
519             endPointA = sourceRange.getEndContainer();
520             endPointB = fEndContainer;
521             offsetA = sourceRange.getEndOffset();
522             offsetB = fEndOffset;
523         }
524
525         // The DOM Spec outlines four cases that need to be tested
526
// to compare two range boundary points:
527
// case 1: same container
528
// case 2: Child C of container A is ancestor of B
529
// case 3: Child C of container B is ancestor of A
530
// case 4: preorder traversal of context tree.
531

532         // case 1: same container
533
if (endPointA == endPointB) {
534             if (offsetA < offsetB) return 1;
535             if (offsetA == offsetB) return 0;
536             return -1;
537         }
538         // case 2: Child C of container A is ancestor of B
539
// This can be quickly tested by walking the parent chain of B
540
for ( Node JavaDoc c = endPointB, p = c.getParentNode();
541              p != null;
542              c = p, p = p.getParentNode())
543         {
544             if (p == endPointA) {
545                 int index = indexOf(c, endPointA);
546                 if (offsetA <= index) return 1;
547                 return -1;
548             }
549         }
550
551         // case 3: Child C of container B is ancestor of A
552
// This can be quickly tested by walking the parent chain of A
553
for ( Node JavaDoc c = endPointA, p = c.getParentNode();
554              p != null;
555              c = p, p = p.getParentNode())
556         {
557             if (p == endPointB) {
558                 int index = indexOf(c, endPointB);
559                 if (index < offsetB) return 1;
560                 return -1;
561             }
562         }
563
564         // case 4: preorder traversal of context tree.
565
// Instead of literally walking the context tree in pre-order,
566
// we use relative node depth walking which is usually faster
567

568         int depthDiff = 0;
569         for ( Node JavaDoc n = endPointA; n != null; n = n.getParentNode() )
570             depthDiff++;
571         for ( Node JavaDoc n = endPointB; n != null; n = n.getParentNode() )
572             depthDiff--;
573         while (depthDiff > 0) {
574             endPointA = endPointA.getParentNode();
575             depthDiff--;
576         }
577         while (depthDiff < 0) {
578             endPointB = endPointB.getParentNode();
579             depthDiff++;
580         }
581         for (Node JavaDoc pA = endPointA.getParentNode(),
582              pB = endPointB.getParentNode();
583              pA != pB;
584              pA = pA.getParentNode(), pB = pB.getParentNode() )
585         {
586             endPointA = pA;
587             endPointB = pB;
588         }
589         for ( Node JavaDoc n = endPointA.getNextSibling();
590              n != null;
591              n = n.getNextSibling() )
592         {
593             if (n == endPointB) {
594                 return 1;
595             }
596         }
597         return -1;
598     }
599     
600     public void deleteContents()
601         throws DOMException JavaDoc
602     {
603         traverseContents(DELETE_CONTENTS);
604     }
605         
606     public DocumentFragment JavaDoc extractContents()
607         throws DOMException JavaDoc
608     {
609         return traverseContents(EXTRACT_CONTENTS);
610     }
611         
612     public DocumentFragment JavaDoc cloneContents()
613         throws DOMException JavaDoc
614     {
615         return traverseContents(CLONE_CONTENTS);
616     }
617     
618     public void insertNode(Node JavaDoc newNode)
619         throws DOMException JavaDoc, RangeException
620     {
621         if ( newNode == null ) return; //throw exception?
622

623         int type = newNode.getNodeType();
624         
625         if (fDocument.errorChecking) {
626             if (fDetach) {
627                 throw new DOMException JavaDoc(
628                         DOMException.INVALID_STATE_ERR,
629                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
630             }
631             if ( fDocument != newNode.getOwnerDocument() ) {
632                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
633                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null));
634             }
635             
636             if (type == Node.ATTRIBUTE_NODE
637                     || type == Node.ENTITY_NODE
638                     || type == Node.NOTATION_NODE
639                     || type == Node.DOCUMENT_NODE)
640             {
641                 throw new RangeExceptionImpl(
642                         RangeException.INVALID_NODE_TYPE_ERR,
643                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
644             }
645         }
646         Node JavaDoc cloneCurrent;
647         Node JavaDoc current;
648         int currentChildren = 0;
649         fInsertedFromRange = true;
650         
651         //boolean MULTIPLE_MODE = false;
652
if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
653         
654             Node JavaDoc parent = fStartContainer.getParentNode();
655             currentChildren = parent.getChildNodes().getLength(); //holds number of kids before insertion
656
// split text node: results is 3 nodes..
657
cloneCurrent = fStartContainer.cloneNode(false);
658             ((TextImpl)cloneCurrent).setNodeValueInternal(
659                     (cloneCurrent.getNodeValue()).substring(fStartOffset));
660             ((TextImpl)fStartContainer).setNodeValueInternal(
661                     (fStartContainer.getNodeValue()).substring(0,fStartOffset));
662             Node JavaDoc next = fStartContainer.getNextSibling();
663             if (next != null) {
664                     if (parent != null) {
665                         parent.insertBefore(newNode, next);
666                         parent.insertBefore(cloneCurrent, next);
667                     }
668             } else {
669                     if (parent != null) {
670                         parent.appendChild(newNode);
671                         parent.appendChild(cloneCurrent);
672                     }
673             }
674              //update ranges after the insertion
675
if ( fEndContainer == fStartContainer) {
676                   fEndContainer = cloneCurrent; //endContainer is the new Node created
677
fEndOffset -= fStartOffset;
678              }
679              else if ( fEndContainer == parent ) { //endContainer was not a text Node.
680
//endOffset + = number_of_children_added
681
fEndOffset += (parent.getChildNodes().getLength() - currentChildren);
682              }
683
684              // signal other Ranges to update their start/end containers/offsets
685
signalSplitData(fStartContainer, cloneCurrent, fStartOffset);
686                 
687              
688         } else { // ! TEXT_NODE
689
if ( fEndContainer == fStartContainer ) //need to remember number of kids
690
currentChildren= fEndContainer.getChildNodes().getLength();
691
692             current = fStartContainer.getFirstChild();
693             int i = 0;
694             for(i = 0; i < fStartOffset && current != null; i++) {
695                 current=current.getNextSibling();
696             }
697             if (current != null) {
698                 fStartContainer.insertBefore(newNode, current);
699             } else {
700                 fStartContainer.appendChild(newNode);
701             }
702             //update fEndOffset. ex:<body><p/></body>. Range(start;end): body,0; body,1
703
// insert <h1>: <body></h1><p/></body>. Range(start;end): body,0; body,2
704
if ( fEndContainer == fStartContainer && fEndOffset != 0 ) { //update fEndOffset if not 0
705
fEndOffset += (fEndContainer.getChildNodes().getLength() - currentChildren);
706             }
707         }
708         fInsertedFromRange = false;
709     }
710     
711     public void surroundContents(Node JavaDoc newParent)
712         throws DOMException JavaDoc, RangeException
713     {
714         if (newParent==null) return;
715         int type = newParent.getNodeType();
716         
717         if (fDocument.errorChecking) {
718             if (fDetach) {
719                 throw new DOMException JavaDoc(
720                         DOMException.INVALID_STATE_ERR,
721                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
722             }
723             if (type == Node.ATTRIBUTE_NODE
724                     || type == Node.ENTITY_NODE
725                     || type == Node.NOTATION_NODE
726                     || type == Node.DOCUMENT_TYPE_NODE
727                     || type == Node.DOCUMENT_NODE
728                     || type == Node.DOCUMENT_FRAGMENT_NODE)
729             {
730                 throw new RangeExceptionImpl(
731                         RangeException.INVALID_NODE_TYPE_ERR,
732                         DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_NODE_TYPE_ERR", null));
733             }
734         }
735         
736         Node JavaDoc realStart = fStartContainer;
737         Node JavaDoc realEnd = fEndContainer;
738         if (fStartContainer.getNodeType() == Node.TEXT_NODE) {
739             realStart = fStartContainer.getParentNode();
740         }
741         if (fEndContainer.getNodeType() == Node.TEXT_NODE) {
742             realEnd = fEndContainer.getParentNode();
743         }
744             
745         if (realStart != realEnd) {
746             throw new RangeExceptionImpl(
747             RangeException.BAD_BOUNDARYPOINTS_ERR,
748                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "BAD_BOUNDARYPOINTS_ERR", null));
749         }
750
751         DocumentFragment JavaDoc frag = extractContents();
752         insertNode(newParent);
753         newParent.appendChild(frag);
754         selectNode(newParent);
755     }
756         
757     public Range cloneRange(){
758         if( fDetach) {
759             throw new DOMException JavaDoc(
760             DOMException.INVALID_STATE_ERR,
761                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
762         }
763         
764         Range range = fDocument.createRange();
765         range.setStart(fStartContainer, fStartOffset);
766         range.setEnd(fEndContainer, fEndOffset);
767         return range;
768     }
769     
770     public String JavaDoc toString(){
771         if( fDetach) {
772             throw new DOMException JavaDoc(
773             DOMException.INVALID_STATE_ERR,
774                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
775         }
776
777         Node JavaDoc node = fStartContainer;
778         Node JavaDoc stopNode = fEndContainer;
779         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
780         if (fStartContainer.getNodeType() == Node.TEXT_NODE
781          || fStartContainer.getNodeType() == Node.CDATA_SECTION_NODE
782         ) {
783             if (fStartContainer == fEndContainer) {
784                 sb.append(fStartContainer.getNodeValue().substring(fStartOffset, fEndOffset));
785                 return sb.toString();
786             }
787             sb.append(fStartContainer.getNodeValue().substring(fStartOffset));
788             node=nextNode (node,true); //fEndContainer!=fStartContainer
789

790         }
791         else { //fStartContainer is not a TextNode
792
node=node.getFirstChild();
793             if (fStartOffset>0) { //find a first node within a range, specified by fStartOffset
794
int counter=0;
795                while (counter<fStartOffset && node!=null) {
796                    node=node.getNextSibling();
797                    counter++;
798                }
799             }
800             if (node == null) {
801                    node = nextNode(fStartContainer,false);
802             }
803         }
804         if ( fEndContainer.getNodeType()!= Node.TEXT_NODE &&
805              fEndContainer.getNodeType()!= Node.CDATA_SECTION_NODE ){
806              int i=fEndOffset;
807              stopNode = fEndContainer.getFirstChild();
808              while( i>0 && stopNode!=null ){
809                  --i;
810                  stopNode = stopNode.getNextSibling();
811              }
812              if ( stopNode == null )
813                  stopNode = nextNode( fEndContainer, false );
814          }
815          while (node != stopNode) { //look into all kids of the Range
816
if (node == null) break;
817              if (node.getNodeType() == Node.TEXT_NODE
818              || node.getNodeType() == Node.CDATA_SECTION_NODE) {
819                  sb.append(node.getNodeValue());
820              }
821
822              node = nextNode(node, true);
823          }
824
825         if (fEndContainer.getNodeType() == Node.TEXT_NODE
826          || fEndContainer.getNodeType() == Node.CDATA_SECTION_NODE) {
827             sb.append(fEndContainer.getNodeValue().substring(0,fEndOffset));
828         }
829         return sb.toString();
830     }
831     
832     public void detach() {
833         if( fDetach) {
834             throw new DOMException JavaDoc(
835             DOMException.INVALID_STATE_ERR,
836                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
837         }
838         fDetach = true;
839         fDocument.removeRange(this);
840     }
841     
842     //
843
// Mutation functions
844
//
845

846     /** Signal other Ranges to update their start/end
847      * containers/offsets. The data has already been split
848      * into the two Nodes.
849      */

850     void signalSplitData(Node JavaDoc node, Node JavaDoc newNode, int offset) {
851         fSplitNode = node;
852         // notify document
853
fDocument.splitData(node, newNode, offset);
854         fSplitNode = null;
855     }
856     
857     /** Fix up this Range if another Range has split a Text Node
858      * into 2 Nodes.
859      */

860     void receiveSplitData(Node JavaDoc node, Node JavaDoc newNode, int offset) {
861         if (node == null || newNode == null) return;
862         if (fSplitNode == node) return;
863         
864         if (node == fStartContainer
865         && fStartContainer.getNodeType() == Node.TEXT_NODE) {
866             if (fStartOffset > offset) {
867                 fStartOffset = fStartOffset - offset;
868                 fStartContainer = newNode;
869             }
870         }
871         if (node == fEndContainer
872         && fEndContainer.getNodeType() == Node.TEXT_NODE) {
873             if (fEndOffset > offset) {
874                 fEndOffset = fEndOffset-offset;
875                 fEndContainer = newNode;
876             }
877         }
878         
879     }
880    
881     /** This function inserts text into a Node and invokes
882      * a method to fix-up all other Ranges.
883      */

884     void deleteData(CharacterData JavaDoc node, int offset, int count) {
885         fDeleteNode = node;
886         node.deleteData( offset, count);
887         fDeleteNode = null;
888     }
889     
890     
891     /** This function is called from DOM.
892      * The text has already beeen inserted.
893      * Fix-up any offsets.
894      */

895     void receiveDeletedText(Node JavaDoc node, int offset, int count) {
896         if (node == null) return;
897         if (fDeleteNode == node) return;
898         if (node == fStartContainer
899         && fStartContainer.getNodeType() == Node.TEXT_NODE) {
900             if (fStartOffset > offset+count) {
901                 fStartOffset = offset+(fStartOffset-(offset+count));
902             } else
903             if (fStartOffset > offset) {
904                 fStartOffset = offset;
905             }
906         }
907         if (node == fEndContainer
908         && fEndContainer.getNodeType() == Node.TEXT_NODE) {
909             if (fEndOffset > offset+count) {
910                 fEndOffset = offset+(fEndOffset-(offset+count));
911             } else
912             if (fEndOffset > offset) {
913                 fEndOffset = offset;
914             }
915         }
916         
917     }
918    
919     /** This function inserts text into a Node and invokes
920      * a method to fix-up all other Ranges.
921      */

922     void insertData(CharacterData JavaDoc node, int index, String JavaDoc insert) {
923         fInsertNode = node;
924         node.insertData( index, insert);
925         fInsertNode = null;
926     }
927     
928     
929     /** This function is called from DOM.
930      * The text has already beeen inserted.
931      * Fix-up any offsets.
932      */

933     void receiveInsertedText(Node JavaDoc node, int index, int len) {
934         if (node == null) return;
935         if (fInsertNode == node) return;
936         if (node == fStartContainer
937         && fStartContainer.getNodeType() == Node.TEXT_NODE) {
938             if (index < fStartOffset) {
939                 fStartOffset = fStartOffset+len;
940             }
941         }
942         if (node == fEndContainer
943         && fEndContainer.getNodeType() == Node.TEXT_NODE) {
944             if (index < fEndOffset) {
945                 fEndOffset = fEndOffset+len;
946             }
947         }
948         
949     }
950    
951     /** This function is called from DOM.
952      * The text has already beeen replaced.
953      * Fix-up any offsets.
954      */

955     void receiveReplacedText(Node JavaDoc node) {
956         if (node == null) return;
957         if (node == fStartContainer
958         && fStartContainer.getNodeType() == Node.TEXT_NODE) {
959             fStartOffset = 0;
960         }
961         if (node == fEndContainer
962         && fEndContainer.getNodeType() == Node.TEXT_NODE) {
963             fEndOffset = 0;
964         }
965         
966     }
967     
968     /** This function is called from the DOM.
969      * This node has already been inserted into the DOM.
970      * Fix-up any offsets.
971      */

972     public void insertedNodeFromDOM(Node JavaDoc node) {
973         if (node == null) return;
974         if (fInsertNode == node) return;
975         if (fInsertedFromRange) return; // Offsets are adjusted in Range.insertNode
976

977         Node JavaDoc parent = node.getParentNode();
978         
979         if (parent == fStartContainer) {
980             int index = indexOf(node, fStartContainer);
981             if (index < fStartOffset) {
982                 fStartOffset++;
983             }
984         }
985         
986         if (parent == fEndContainer) {
987             int index = indexOf(node, fEndContainer);
988             if (index < fEndOffset) {
989                 fEndOffset++;
990             }
991         }
992         
993     }
994     
995     /** This function is called within Range
996      * instead of Node.removeChild,
997      * so that the range can remember that it is actively
998      * removing this child.
999      */

1000     
1001    Node JavaDoc fRemoveChild = null;
1002    Node JavaDoc removeChild(Node JavaDoc parent, Node JavaDoc child) {
1003        fRemoveChild = child;
1004        Node JavaDoc n = parent.removeChild(child);
1005        fRemoveChild = null;
1006        return n;
1007    }
1008    
1009    /** This function must be called by the DOM _BEFORE_
1010     * a node is deleted, because at that time it is
1011     * connected in the DOM tree, which we depend on.
1012     */

1013    void removeNode(Node JavaDoc node) {
1014        if (node == null) return;
1015        if (fRemoveChild == node) return;
1016        
1017        Node JavaDoc parent = node.getParentNode();
1018        
1019        if (parent == fStartContainer) {
1020            int index = indexOf(node, fStartContainer);
1021            if (index < fStartOffset) {
1022                fStartOffset--;
1023            }
1024        }
1025        
1026        if (parent == fEndContainer) {
1027            int index = indexOf(node, fEndContainer);
1028            if (index < fEndOffset) {
1029                fEndOffset--;
1030            }
1031        }
1032        //startContainer or endContainer or both is/are the ancestor(s) of the Node to be deleted
1033
if (parent != fStartContainer
1034        || parent != fEndContainer) {
1035            if (isAncestorOf(node, fStartContainer)) {
1036                fStartContainer = parent;
1037                fStartOffset = indexOf( node, parent);
1038            }
1039            if (isAncestorOf(node, fEndContainer)) {
1040                fEndContainer = parent;
1041                fEndOffset = indexOf( node, parent);
1042            }
1043        }
1044        
1045    }
1046        
1047    //
1048
// Utility functions.
1049
//
1050

1051    // parameters for traverseContents(int)
1052
//REVIST: use boolean, since there are only 2 now...
1053
static final int EXTRACT_CONTENTS = 1;
1054    static final int CLONE_CONTENTS = 2;
1055    static final int DELETE_CONTENTS = 3;
1056    
1057    /**
1058     * This is the master routine invoked to visit the nodes
1059     * selected by this range. For each such node, different
1060     * actions are taken depending on the value of the
1061     * <code>how</code> argument.
1062     *
1063     * @param how Specifies what type of traversal is being
1064     * requested (extract, clone, or delete).
1065     * Legal values for this argument are:
1066     *
1067     * <ol>
1068     * <li><code>EXTRACT_CONTENTS</code> - will produce
1069     * a document fragment containing the range's content.
1070     * Partially selected nodes are copied, but fully
1071     * selected nodes are moved.
1072     *
1073     * <li><code>CLONE_CONTENTS</code> - will leave the
1074     * context tree of the range undisturbed, but sill
1075     * produced cloned content in a document fragment
1076     *
1077     * <li><code>DELETE_CONTENTS</code> - will delete from
1078     * the context tree of the range, all fully selected
1079     * nodes.
1080     * </ol>
1081     *
1082     * @return Returns a document fragment containing any
1083     * copied or extracted nodes. If the <code>how</code>
1084     * parameter was <code>DELETE_CONTENTS</code>, the
1085     * return value is null.
1086     */

1087    private DocumentFragment JavaDoc traverseContents( int how )
1088        throws DOMException JavaDoc
1089    {
1090        if (fStartContainer == null || fEndContainer == null) {
1091            return null; // REVIST: Throw exception?
1092
}
1093        
1094        //Check for a detached range.
1095
if( fDetach) {
1096            throw new DOMException JavaDoc(
1097                DOMException.INVALID_STATE_ERR,
1098                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
1099        }
1100
1101        /*
1102          Traversal is accomplished by first determining the
1103          relationship between the endpoints of the range.
1104          For each of four significant relationships, we will
1105          delegate the traversal call to a method that
1106          can make appropriate assumptions.
1107         */

1108
1109        // case 1: same container
1110
if ( fStartContainer == fEndContainer )
1111            return traverseSameContainer( how );
1112
1113
1114        // case 2: Child C of start container is ancestor of end container
1115
// This can be quickly tested by walking the parent chain of
1116
// end container
1117
int endContainerDepth = 0;
1118        for ( Node JavaDoc c = fEndContainer, p = c.getParentNode();
1119             p != null;
1120             c = p, p = p.getParentNode())
1121        {
1122            if (p == fStartContainer)
1123                return traverseCommonStartContainer( c, how );
1124            ++endContainerDepth;
1125        }
1126
1127        // case 3: Child C of container B is ancestor of A
1128
// This can be quickly tested by walking the parent chain of A
1129
int startContainerDepth = 0;
1130        for ( Node JavaDoc c = fStartContainer, p = c.getParentNode();
1131             p != null;
1132             c = p, p = p.getParentNode())
1133        {
1134            if (p == fEndContainer)
1135                return traverseCommonEndContainer( c, how );
1136            ++startContainerDepth;
1137        }
1138
1139        // case 4: There is a common ancestor container. Find the
1140
// ancestor siblings that are children of that container.
1141
int depthDiff = startContainerDepth - endContainerDepth;
1142
1143        Node JavaDoc startNode = fStartContainer;
1144        while (depthDiff > 0) {
1145            startNode = startNode.getParentNode();
1146            depthDiff--;
1147        }
1148
1149        Node JavaDoc endNode = fEndContainer;
1150        while (depthDiff < 0) {
1151            endNode = endNode.getParentNode();
1152            depthDiff++;
1153        }
1154
1155        // ascend the ancestor hierarchy until we have a common parent.
1156
for( Node JavaDoc sp = startNode.getParentNode(), ep = endNode.getParentNode();
1157             sp!=ep;
1158             sp = sp.getParentNode(), ep = ep.getParentNode() )
1159        {
1160            startNode = sp;
1161            endNode = ep;
1162        }
1163        return traverseCommonAncestors( startNode, endNode, how );
1164    }
1165    
1166    /**
1167     * Visits the nodes selected by this range when we know
1168     * a-priori that the start and end containers are the same.
1169     * This method is invoked by the generic <code>traverse</code>
1170     * method.
1171     *
1172     * @param how Specifies what type of traversal is being
1173     * requested (extract, clone, or delete).
1174     * Legal values for this argument are:
1175     *
1176     * <ol>
1177     * <li><code>EXTRACT_CONTENTS</code> - will produce
1178     * a document fragment containing the range's content.
1179     * Partially selected nodes are copied, but fully
1180     * selected nodes are moved.
1181     *
1182     * <li><code>CLONE_CONTENTS</code> - will leave the
1183     * context tree of the range undisturbed, but sill
1184     * produced cloned content in a document fragment
1185     *
1186     * <li><code>DELETE_CONTENTS</code> - will delete from
1187     * the context tree of the range, all fully selected
1188     * nodes.
1189     * </ol>
1190     *
1191     * @return Returns a document fragment containing any
1192     * copied or extracted nodes. If the <code>how</code>
1193     * parameter was <code>DELETE_CONTENTS</code>, the
1194     * return value is null.
1195     */

1196    private DocumentFragment JavaDoc traverseSameContainer( int how )
1197    {
1198        DocumentFragment JavaDoc frag = null;
1199        if ( how!=DELETE_CONTENTS)
1200            frag = fDocument.createDocumentFragment();
1201
1202        // If selection is empty, just return the fragment
1203
if ( fStartOffset==fEndOffset )
1204            return frag;
1205
1206        // Text node needs special case handling
1207
if ( fStartContainer.getNodeType()==Node.TEXT_NODE )
1208        {
1209            // get the substring
1210
String JavaDoc s = fStartContainer.getNodeValue();
1211            String JavaDoc sub = s.substring( fStartOffset, fEndOffset );
1212
1213            // set the original text node to its new value
1214
if ( how != CLONE_CONTENTS )
1215            {
1216                ((TextImpl)fStartContainer).deleteData(fStartOffset,
1217                     fEndOffset-fStartOffset) ;
1218                // Nothing is partially selected, so collapse to start point
1219
collapse( true );
1220            }
1221            if ( how==DELETE_CONTENTS)
1222                return null;
1223            frag.appendChild( fDocument.createTextNode(sub) );
1224            return frag;
1225        }
1226
1227        // Copy nodes between the start/end offsets.
1228
Node JavaDoc n = getSelectedNode( fStartContainer, fStartOffset );
1229        int cnt = fEndOffset - fStartOffset;
1230        while( cnt > 0 )
1231        {
1232            Node JavaDoc sibling = n.getNextSibling();
1233            Node JavaDoc xferNode = traverseFullySelected( n, how );
1234            if ( frag!=null )
1235                frag.appendChild( xferNode );
1236            --cnt;
1237            n = sibling;
1238        }
1239
1240        // Nothing is partially selected, so collapse to start point
1241
if ( how != CLONE_CONTENTS )
1242            collapse( true );
1243        return frag;
1244    }
1245
1246    /**
1247     * Visits the nodes selected by this range when we know
1248     * a-priori that the start and end containers are not the
1249     * same, but the start container is an ancestor of the
1250     * end container. This method is invoked by the generic
1251     * <code>traverse</code> method.
1252     *
1253     * @param endAncestor
1254     * The ancestor of the end container that is a direct child
1255     * of the start container.
1256     *
1257     * @param how Specifies what type of traversal is being
1258     * requested (extract, clone, or delete).
1259     * Legal values for this argument are:
1260     *
1261     * <ol>
1262     * <li><code>EXTRACT_CONTENTS</code> - will produce
1263     * a document fragment containing the range's content.
1264     * Partially selected nodes are copied, but fully
1265     * selected nodes are moved.
1266     *
1267     * <li><code>CLONE_CONTENTS</code> - will leave the
1268     * context tree of the range undisturbed, but sill
1269     * produced cloned content in a document fragment
1270     *
1271     * <li><code>DELETE_CONTENTS</code> - will delete from
1272     * the context tree of the range, all fully selected
1273     * nodes.
1274     * </ol>
1275     *
1276     * @return Returns a document fragment containing any
1277     * copied or extracted nodes. If the <code>how</code>
1278     * parameter was <code>DELETE_CONTENTS</code>, the
1279     * return value is null.
1280     */

1281    private DocumentFragment JavaDoc
1282        traverseCommonStartContainer( Node JavaDoc endAncestor, int how )
1283    {
1284        DocumentFragment JavaDoc frag = null;
1285        if ( how!=DELETE_CONTENTS)
1286            frag = fDocument.createDocumentFragment();
1287        Node JavaDoc n = traverseRightBoundary( endAncestor, how );
1288        if ( frag!=null )
1289            frag.appendChild( n );
1290
1291        int endIdx = indexOf( endAncestor, fStartContainer );
1292        int cnt = endIdx - fStartOffset;
1293        if ( cnt <=0 )
1294        {
1295            // Collapse to just before the endAncestor, which
1296
// is partially selected.
1297
if ( how != CLONE_CONTENTS )
1298            {
1299                setEndBefore( endAncestor );
1300                collapse( false );
1301            }
1302            return frag;
1303        }
1304
1305        n = endAncestor.getPreviousSibling();
1306        while( cnt > 0 )
1307        {
1308            Node JavaDoc sibling = n.getPreviousSibling();
1309            Node JavaDoc xferNode = traverseFullySelected( n, how );
1310            if ( frag!=null )
1311                frag.insertBefore( xferNode, frag.getFirstChild() );
1312            --cnt;
1313            n = sibling;
1314        }
1315        // Collapse to just before the endAncestor, which
1316
// is partially selected.
1317
if ( how != CLONE_CONTENTS )
1318        {
1319            setEndBefore( endAncestor );
1320            collapse( false );
1321        }
1322        return frag;
1323    }
1324    
1325    /**
1326     * Visits the nodes selected by this range when we know
1327     * a-priori that the start and end containers are not the
1328     * same, but the end container is an ancestor of the
1329     * start container. This method is invoked by the generic
1330     * <code>traverse</code> method.
1331     *
1332     * @param startAncestor
1333     * The ancestor of the start container that is a direct
1334     * child of the end container.
1335     *
1336     * @param how Specifies what type of traversal is being
1337     * requested (extract, clone, or delete).
1338     * Legal values for this argument are:
1339     *
1340     * <ol>
1341     * <li><code>EXTRACT_CONTENTS</code> - will produce
1342     * a document fragment containing the range's content.
1343     * Partially selected nodes are copied, but fully
1344     * selected nodes are moved.
1345     *
1346     * <li><code>CLONE_CONTENTS</code> - will leave the
1347     * context tree of the range undisturbed, but sill
1348     * produced cloned content in a document fragment
1349     *
1350     * <li><code>DELETE_CONTENTS</code> - will delete from
1351     * the context tree of the range, all fully selected
1352     * nodes.
1353     * </ol>
1354     *
1355     * @return Returns a document fragment containing any
1356     * copied or extracted nodes. If the <code>how</code>
1357     * parameter was <code>DELETE_CONTENTS</code>, the
1358     * return value is null.
1359     */

1360    private DocumentFragment JavaDoc
1361        traverseCommonEndContainer( Node JavaDoc startAncestor, int how )
1362    {
1363        DocumentFragment JavaDoc frag = null;
1364        if ( how!=DELETE_CONTENTS)
1365            frag = fDocument.createDocumentFragment();
1366        Node JavaDoc n = traverseLeftBoundary( startAncestor, how );
1367        if ( frag!=null )
1368            frag.appendChild( n );
1369        int startIdx = indexOf( startAncestor, fEndContainer );
1370        ++startIdx; // Because we already traversed it....
1371

1372        int cnt = fEndOffset - startIdx;
1373        n = startAncestor.getNextSibling();
1374        while( cnt > 0 )
1375        {
1376            Node JavaDoc sibling = n.getNextSibling();
1377            Node JavaDoc xferNode = traverseFullySelected( n, how );
1378            if ( frag!=null )
1379                frag.appendChild( xferNode );
1380            --cnt;
1381            n = sibling;
1382        }
1383
1384        if ( how != CLONE_CONTENTS )
1385        {
1386            setStartAfter( startAncestor );
1387            collapse( true );
1388        }
1389
1390        return frag;
1391    }
1392
1393    /**
1394     * Visits the nodes selected by this range when we know
1395     * a-priori that the start and end containers are not
1396     * the same, and we also know that neither the start
1397     * nor end container is an ancestor of the other.
1398     * This method is invoked by
1399     * the generic <code>traverse</code> method.
1400     *
1401     * @param startAncestor
1402     * Given a common ancestor of the start and end containers,
1403     * this parameter is the ancestor (or self) of the start
1404     * container that is a direct child of the common ancestor.
1405     *
1406     * @param endAncestor
1407     * Given a common ancestor of the start and end containers,
1408     * this parameter is the ancestor (or self) of the end
1409     * container that is a direct child of the common ancestor.
1410     *
1411     * @param how Specifies what type of traversal is being
1412     * requested (extract, clone, or delete).
1413     * Legal values for this argument are:
1414     *
1415     * <ol>
1416     * <li><code>EXTRACT_CONTENTS</code> - will produce
1417     * a document fragment containing the range's content.
1418     * Partially selected nodes are copied, but fully
1419     * selected nodes are moved.
1420     *
1421     * <li><code>CLONE_CONTENTS</code> - will leave the
1422     * context tree of the range undisturbed, but sill
1423     * produced cloned content in a document fragment
1424     *
1425     * <li><code>DELETE_CONTENTS</code> - will delete from
1426     * the context tree of the range, all fully selected
1427     * nodes.
1428     * </ol>
1429     *
1430     * @return Returns a document fragment containing any
1431     * copied or extracted nodes. If the <code>how</code>
1432     * parameter was <code>DELETE_CONTENTS</code>, the
1433     * return value is null.
1434     */

1435    private DocumentFragment JavaDoc
1436        traverseCommonAncestors( Node JavaDoc startAncestor, Node JavaDoc endAncestor, int how )
1437    {
1438        DocumentFragment JavaDoc frag = null;
1439        if ( how!=DELETE_CONTENTS)
1440            frag = fDocument.createDocumentFragment();
1441
1442        Node JavaDoc n = traverseLeftBoundary( startAncestor, how );
1443        if ( frag!=null )
1444            frag.appendChild( n );
1445
1446        Node JavaDoc commonParent = startAncestor.getParentNode();
1447        int startOffset = indexOf( startAncestor, commonParent );
1448        int endOffset = indexOf( endAncestor, commonParent );
1449        ++startOffset;
1450
1451        int cnt = endOffset - startOffset;
1452        Node JavaDoc sibling = startAncestor.getNextSibling();
1453
1454        while( cnt > 0 )
1455        {
1456            Node JavaDoc nextSibling = sibling.getNextSibling();
1457            n = traverseFullySelected( sibling, how );
1458            if ( frag!=null )
1459                frag.appendChild( n );
1460            sibling = nextSibling;
1461            --cnt;
1462        }
1463
1464        n = traverseRightBoundary( endAncestor, how );
1465        if ( frag!=null )
1466            frag.appendChild( n );
1467
1468        if ( how != CLONE_CONTENTS )
1469        {
1470            setStartAfter( startAncestor );
1471            collapse( true );
1472        }
1473        return frag;
1474    }
1475
1476    /**
1477     * Traverses the "right boundary" of this range and
1478     * operates on each "boundary node" according to the
1479     * <code>how</code> parameter. It is a-priori assumed
1480     * by this method that the right boundary does
1481     * not contain the range's start container.
1482     * <p>
1483     * A "right boundary" is best visualized by thinking
1484     * of a sample tree:<pre>
1485     * A
1486     * /|\
1487     * / | \
1488     * / | \
1489     * B C D
1490     * /|\ /|\
1491     * E F G H I J
1492     * </pre>
1493     * Imagine first a range that begins between the
1494     * "E" and "F" nodes and ends between the
1495     * "I" and "J" nodes. The start container is
1496     * "B" and the end container is "D". Given this setup,
1497     * the following applies:
1498     * <p>
1499     * Partially Selected Nodes: B, D<br>
1500     * Fully Selected Nodes: F, G, C, H, I
1501     * <p>
1502     * The "right boundary" is the highest subtree node
1503     * that contains the ending container. The root of
1504     * this subtree is always partially selected.
1505     * <p>
1506     * In this example, the nodes that are traversed
1507     * as "right boundary" nodes are: H, I, and D.
1508     *
1509     * @param root The node that is the root of the "right boundary" subtree.
1510     *
1511     * @param how Specifies what type of traversal is being
1512     * requested (extract, clone, or delete).
1513     * Legal values for this argument are:
1514     *
1515     * <ol>
1516     * <li><code>EXTRACT_CONTENTS</code> - will produce
1517     * a node containing the boundaries content.
1518     * Partially selected nodes are copied, but fully
1519     * selected nodes are moved.
1520     *
1521     * <li><code>CLONE_CONTENTS</code> - will leave the
1522     * context tree of the range undisturbed, but will
1523     * produced cloned content.
1524     *
1525     * <li><code>DELETE_CONTENTS</code> - will delete from
1526     * the context tree of the range, all fully selected
1527     * nodes within the boundary.
1528     * </ol>
1529     *
1530     * @return Returns a node that is the result of visiting nodes.
1531     * If the traversal operation is
1532     * <code>DELETE_CONTENTS</code> the return value is null.
1533     */

1534    private Node JavaDoc traverseRightBoundary( Node JavaDoc root, int how )
1535    {
1536        Node JavaDoc next = getSelectedNode( fEndContainer, fEndOffset-1 );
1537        boolean isFullySelected = ( next!=fEndContainer );
1538
1539        if ( next==root )
1540            return traverseNode( next, isFullySelected, false, how );
1541
1542        Node JavaDoc parent = next.getParentNode();
1543        Node JavaDoc clonedParent = traverseNode( parent, false, false, how );
1544
1545        while( parent!=null )
1546        {
1547            while( next!=null )
1548            {
1549                Node JavaDoc prevSibling = next.getPreviousSibling();
1550                Node JavaDoc clonedChild =
1551                    traverseNode( next, isFullySelected, false, how );
1552                if ( how!=DELETE_CONTENTS )
1553                {
1554                    clonedParent.insertBefore(
1555                        clonedChild,
1556                        clonedParent.getFirstChild()
1557                    );
1558                }
1559                isFullySelected = true;
1560                next = prevSibling;
1561            }
1562            if ( parent==root )
1563                return clonedParent;
1564
1565            next = parent.getPreviousSibling();
1566            parent = parent.getParentNode();
1567            Node JavaDoc clonedGrandParent = traverseNode( parent, false, false, how );
1568            if ( how!=DELETE_CONTENTS )
1569                clonedGrandParent.appendChild( clonedParent );
1570            clonedParent = clonedGrandParent;
1571
1572        }
1573
1574        // should never occur
1575
return null;
1576    }
1577
1578    /**
1579     * Traverses the "left boundary" of this range and
1580     * operates on each "boundary node" according to the
1581     * <code>how</code> parameter. It is a-priori assumed
1582     * by this method that the left boundary does
1583     * not contain the range's end container.
1584     * <p>
1585     * A "left boundary" is best visualized by thinking
1586     * of a sample tree:<pre>
1587     *
1588     * A
1589     * /|\
1590     * / | \
1591     * / | \
1592     * B C D
1593     * /|\ /|\
1594     * E F G H I J
1595     * </pre>
1596     * Imagine first a range that begins between the
1597     * "E" and "F" nodes and ends between the
1598     * "I" and "J" nodes. The start container is
1599     * "B" and the end container is "D". Given this setup,
1600     * the following applies:
1601     * <p>
1602     * Partially Selected Nodes: B, D<br>
1603     * Fully Selected Nodes: F, G, C, H, I
1604     * <p>
1605     * The "left boundary" is the highest subtree node
1606     * that contains the starting container. The root of
1607     * this subtree is always partially selected.
1608     * <p>
1609     * In this example, the nodes that are traversed
1610     * as "left boundary" nodes are: F, G, and B.
1611     *
1612     * @param root The node that is the root of the "left boundary" subtree.
1613     *
1614     * @param how Specifies what type of traversal is being
1615     * requested (extract, clone, or delete).
1616     * Legal values for this argument are:
1617     *
1618     * <ol>
1619     * <li><code>EXTRACT_CONTENTS</code> - will produce
1620     * a node containing the boundaries content.
1621     * Partially selected nodes are copied, but fully
1622     * selected nodes are moved.
1623     *
1624     * <li><code>CLONE_CONTENTS</code> - will leave the
1625     * context tree of the range undisturbed, but will
1626     * produced cloned content.
1627     *
1628     * <li><code>DELETE_CONTENTS</code> - will delete from
1629     * the context tree of the range, all fully selected
1630     * nodes within the boundary.
1631     * </ol>
1632     *
1633     * @return Returns a node that is the result of visiting nodes.
1634     * If the traversal operation is
1635     * <code>DELETE_CONTENTS</code> the return value is null.
1636     */

1637    private Node JavaDoc traverseLeftBoundary( Node JavaDoc root, int how )
1638    {
1639        Node JavaDoc next = getSelectedNode( getStartContainer(), getStartOffset() );
1640        boolean isFullySelected = ( next!=getStartContainer() );
1641
1642        if ( next==root )
1643            return traverseNode( next, isFullySelected, true, how );
1644
1645        Node JavaDoc parent = next.getParentNode();
1646        Node JavaDoc clonedParent = traverseNode( parent, false, true, how );
1647
1648        while( parent!=null )
1649        {
1650            while( next!=null )
1651            {
1652                Node JavaDoc nextSibling = next.getNextSibling();
1653                Node JavaDoc clonedChild =
1654                    traverseNode( next, isFullySelected, true, how );
1655                if ( how!=DELETE_CONTENTS )
1656                    clonedParent.appendChild(clonedChild);
1657                isFullySelected = true;
1658                next = nextSibling;
1659            }
1660            if ( parent==root )
1661                return clonedParent;
1662
1663            next = parent.getNextSibling();
1664            parent = parent.getParentNode();
1665            Node JavaDoc clonedGrandParent = traverseNode( parent, false, true, how );
1666            if ( how!=DELETE_CONTENTS )
1667                clonedGrandParent.appendChild( clonedParent );
1668            clonedParent = clonedGrandParent;
1669
1670        }
1671
1672        // should never occur
1673
return null;
1674
1675    }
1676
1677    /**
1678     * Utility method for traversing a single node.
1679     * Does not properly handle a text node containing both the
1680     * start and end offsets. Such nodes should
1681     * have been previously detected and been routed to traverseTextNode.
1682     *
1683     * @param n The node to be traversed.
1684     *
1685     * @param isFullySelected
1686     * Set to true if the node is fully selected. Should be
1687     * false otherwise.
1688     * Note that although the DOM 2 specification says that a
1689     * text node that is boththe start and end container is not
1690     * selected, we treat it here as if it were partially
1691     * selected.
1692     *
1693     * @param isLeft Is true if we are traversing the node as part of navigating
1694     * the "left boundary" of the range. If this value is false,
1695     * it implies we are navigating the "right boundary" of the
1696     * range.
1697     *
1698     * @param how Specifies what type of traversal is being
1699     * requested (extract, clone, or delete).
1700     * Legal values for this argument are:
1701     *
1702     * <ol>
1703     * <li><code>EXTRACT_CONTENTS</code> - will simply
1704     * return the original node.
1705     *
1706     * <li><code>CLONE_CONTENTS</code> - will leave the
1707     * context tree of the range undisturbed, but will
1708     * return a cloned node.
1709     *
1710     * <li><code>DELETE_CONTENTS</code> - will delete the
1711     * node from it's parent, but will return null.
1712     * </ol>
1713     *
1714     * @return Returns a node that is the result of visiting the node.
1715     * If the traversal operation is
1716     * <code>DELETE_CONTENTS</code> the return value is null.
1717     */

1718    private Node JavaDoc traverseNode( Node JavaDoc n, boolean isFullySelected, boolean isLeft, int how )
1719    {
1720        if ( isFullySelected )
1721            return traverseFullySelected( n, how );
1722        if ( n.getNodeType()==Node.TEXT_NODE )
1723            return traverseTextNode( n, isLeft, how );
1724        return traversePartiallySelected( n, how );
1725    }
1726
1727    /**
1728     * Utility method for traversing a single node when
1729     * we know a-priori that the node if fully
1730     * selected.
1731     *
1732     * @param n The node to be traversed.
1733     *
1734     * @param how Specifies what type of traversal is being
1735     * requested (extract, clone, or delete).
1736     * Legal values for this argument are:
1737     *
1738     * <ol>
1739     * <li><code>EXTRACT_CONTENTS</code> - will simply
1740     * return the original node.
1741     *
1742     * <li><code>CLONE_CONTENTS</code> - will leave the
1743     * context tree of the range undisturbed, but will
1744     * return a cloned node.
1745     *
1746     * <li><code>DELETE_CONTENTS</code> - will delete the
1747     * node from it's parent, but will return null.
1748     * </ol>
1749     *
1750     * @return Returns a node that is the result of visiting the node.
1751     * If the traversal operation is
1752     * <code>DELETE_CONTENTS</code> the return value is null.
1753     */

1754    private Node JavaDoc traverseFullySelected( Node JavaDoc n, int how )
1755    {
1756        switch( how )
1757        {
1758        case CLONE_CONTENTS:
1759            return n.cloneNode( true );
1760        case EXTRACT_CONTENTS:
1761            if ( n.getNodeType()==Node.DOCUMENT_TYPE_NODE )
1762            {
1763                // TBD: This should be a HIERARCHY_REQUEST_ERR
1764
throw new DOMException JavaDoc(
1765                        DOMException.HIERARCHY_REQUEST_ERR,
1766                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null));
1767            }
1768            return n;
1769        case DELETE_CONTENTS:
1770            n.getParentNode().removeChild(n);
1771            return null;
1772        }
1773        return null;
1774    }
1775
1776    /**
1777     * Utility method for traversing a single node when
1778     * we know a-priori that the node if partially
1779     * selected and is not a text node.
1780     *
1781     * @param n The node to be traversed.
1782     *
1783     * @param how Specifies what type of traversal is being
1784     * requested (extract, clone, or delete).
1785     * Legal values for this argument are:
1786     *
1787     * <ol>
1788     * <li><code>EXTRACT_CONTENTS</code> - will simply
1789     * return the original node.
1790     *
1791     * <li><code>CLONE_CONTENTS</code> - will leave the
1792     * context tree of the range undisturbed, but will
1793     * return a cloned node.
1794     *
1795     * <li><code>DELETE_CONTENTS</code> - will delete the
1796     * node from it's parent, but will return null.
1797     * </ol>
1798     *
1799     * @return Returns a node that is the result of visiting the node.
1800     * If the traversal operation is
1801     * <code>DELETE_CONTENTS</code> the return value is null.
1802     */

1803    private Node JavaDoc traversePartiallySelected( Node JavaDoc n, int how )
1804    {
1805        switch( how )
1806        {
1807        case DELETE_CONTENTS:
1808            return null;
1809        case CLONE_CONTENTS:
1810        case EXTRACT_CONTENTS:
1811            return n.cloneNode( false );
1812        }
1813        return null;
1814    }
1815
1816    /**
1817     * Utility method for traversing a text node that we know
1818     * a-priori to be on a left or right boundary of the range.
1819     * This method does not properly handle text nodes that contain
1820     * both the start and end points of the range.
1821     *
1822     * @param n The node to be traversed.
1823     *
1824     * @param isLeft Is true if we are traversing the node as part of navigating
1825     * the "left boundary" of the range. If this value is false,
1826     * it implies we are navigating the "right boundary" of the
1827     * range.
1828     *
1829     * @param how Specifies what type of traversal is being
1830     * requested (extract, clone, or delete).
1831     * Legal values for this argument are:
1832     *
1833     * <ol>
1834     * <li><code>EXTRACT_CONTENTS</code> - will simply
1835     * return the original node.
1836     *
1837     * <li><code>CLONE_CONTENTS</code> - will leave the
1838     * context tree of the range undisturbed, but will
1839     * return a cloned node.
1840     *
1841     * <li><code>DELETE_CONTENTS</code> - will delete the
1842     * node from it's parent, but will return null.
1843     * </ol>
1844     *
1845     * @return Returns a node that is the result of visiting the node.
1846     * If the traversal operation is
1847     * <code>DELETE_CONTENTS</code> the return value is null.
1848     */

1849    private Node JavaDoc traverseTextNode( Node JavaDoc n, boolean isLeft, int how )
1850    {
1851        String JavaDoc txtValue = n.getNodeValue();
1852        String JavaDoc newNodeValue;
1853        String JavaDoc oldNodeValue;
1854
1855        if ( isLeft )
1856        {
1857            int offset = getStartOffset();
1858            newNodeValue = txtValue.substring( offset );
1859            oldNodeValue = txtValue.substring( 0, offset );
1860        }
1861        else
1862        {
1863            int offset = getEndOffset();
1864            newNodeValue = txtValue.substring( 0, offset );
1865            oldNodeValue = txtValue.substring( offset );
1866        }
1867
1868        if ( how != CLONE_CONTENTS )
1869            n.setNodeValue( oldNodeValue );
1870        if ( how==DELETE_CONTENTS )
1871            return null;
1872        Node JavaDoc newNode = n.cloneNode( false );
1873        newNode.setNodeValue( newNodeValue );
1874        return newNode;
1875    }
1876
1877    void checkIndex(Node JavaDoc refNode, int offset) throws DOMException JavaDoc
1878    {
1879        if (offset < 0) {
1880            throw new DOMException JavaDoc(
1881                DOMException.INDEX_SIZE_ERR,
1882                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null));
1883        }
1884
1885        int type = refNode.getNodeType();
1886        
1887        // If the node contains text, ensure that the
1888
// offset of the range is <= to the length of the text
1889
if (type == Node.TEXT_NODE
1890            || type == Node.CDATA_SECTION_NODE
1891            || type == Node.COMMENT_NODE
1892            || type == Node.PROCESSING_INSTRUCTION_NODE) {
1893            if (offset > refNode.getNodeValue().length()) {
1894                throw new DOMException JavaDoc(DOMException.INDEX_SIZE_ERR,
1895                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null));
1896            }
1897        }
1898        else {
1899            // Since the node is not text, ensure that the offset
1900
// is valid with respect to the number of child nodes
1901
if (offset > refNode.getChildNodes().getLength()) {
1902            throw new DOMException JavaDoc(DOMException.INDEX_SIZE_ERR,
1903                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null));
1904            }
1905        }
1906    }
1907
1908    /**
1909     * Given a node, calculate what the Range's root container
1910     * for that node would be.
1911     */

1912    private Node JavaDoc getRootContainer( Node JavaDoc node )
1913    {
1914        if ( node==null )
1915            return null;
1916
1917        while( node.getParentNode()!=null )
1918            node = node.getParentNode();
1919        return node;
1920    }
1921
1922    /**
1923     * Returns true IFF the given node can serve as a container
1924     * for a range's boundary points.
1925     */

1926    private boolean isLegalContainer( Node JavaDoc node )
1927    {
1928        if ( node==null )
1929            return false;
1930
1931        while( node!=null )
1932        {
1933            switch( node.getNodeType() )
1934            {
1935            case Node.ENTITY_NODE:
1936            case Node.NOTATION_NODE:
1937            case Node.DOCUMENT_TYPE_NODE:
1938                return false;
1939            }
1940            node = node.getParentNode();
1941        }
1942
1943        return true;
1944    }
1945
1946
1947    /**
1948     * Finds the root container for the given node and determines
1949     * if that root container is legal with respect to the
1950     * DOM 2 specification. At present, that means the root
1951     * container must be either an attribute, a document,
1952     * or a document fragment.
1953     */

1954    private boolean hasLegalRootContainer( Node JavaDoc node )
1955    {
1956        if ( node==null )
1957            return false;
1958
1959        Node JavaDoc rootContainer = getRootContainer( node );
1960        switch( rootContainer.getNodeType() )
1961        {
1962        case Node.ATTRIBUTE_NODE:
1963        case Node.DOCUMENT_NODE:
1964        case Node.DOCUMENT_FRAGMENT_NODE:
1965            return true;
1966        }
1967        return false;
1968    }
1969
1970    /**
1971     * Returns true IFF the given node can be contained by
1972     * a range.
1973     */

1974    private boolean isLegalContainedNode( Node JavaDoc node )
1975    {
1976        if ( node==null )
1977            return false;
1978        switch( node.getNodeType() )
1979        {
1980        case Node.DOCUMENT_NODE:
1981        case Node.DOCUMENT_FRAGMENT_NODE:
1982        case Node.ATTRIBUTE_NODE:
1983        case Node.ENTITY_NODE:
1984        case Node.NOTATION_NODE:
1985            return false;
1986        }
1987        return true;
1988    }
1989
1990    Node JavaDoc nextNode(Node JavaDoc node, boolean visitChildren) {
1991            
1992        if (node == null) return null;
1993
1994        Node JavaDoc result;
1995        if (visitChildren) {
1996            result = node.getFirstChild();
1997            if (result != null) {
1998                return result;
1999            }
2000        }
2001            
2002        // if hasSibling, return sibling
2003
result = node.getNextSibling();
2004        if (result != null) {
2005            return result;
2006        }
2007        
2008                
2009        // return parent's 1st sibling.
2010
Node JavaDoc parent = node.getParentNode();
2011        while (parent != null
2012               && parent != fDocument
2013                ) {
2014            result = parent.getNextSibling();
2015            if (result != null) {
2016                return result;
2017            } else {
2018                parent = parent.getParentNode();
2019            }
2020                            
2021        } // while (parent != null && parent != fRoot) {
2022

2023        // end of list, return null
2024
return null;
2025    }
2026    
2027    /** is a an ancestor of b ? */
2028    boolean isAncestorOf(Node JavaDoc a, Node JavaDoc b) {
2029        for (Node JavaDoc node=b; node != null; node=node.getParentNode()) {
2030            if (node == a) return true;
2031        }
2032        return false;
2033    }
2034
2035    /** what is the index of the child in the parent */
2036    int indexOf(Node JavaDoc child, Node JavaDoc parent) {
2037        if (child.getParentNode() != parent) return -1;
2038        int i = 0;
2039        for(Node JavaDoc node = parent.getFirstChild(); node!= child; node=node.getNextSibling()) {
2040            i++;
2041        }
2042        return i;
2043    }
2044
2045    /**
2046     * Utility method to retrieve a child node by index. This method
2047     * assumes the caller is trying to find out which node is
2048     * selected by the given index. Note that if the index is
2049     * greater than the number of children, this implies that the
2050     * first node selected is the parent node itself.
2051     *
2052     * @param container A container node
2053     *
2054     * @param offset An offset within the container for which a selected node should
2055     * be computed. If the offset is less than zero, or if the offset
2056     * is greater than the number of children, the container is returned.
2057     *
2058     * @return Returns either a child node of the container or the
2059     * container itself.
2060     */

2061    private Node JavaDoc getSelectedNode( Node JavaDoc container, int offset )
2062    {
2063        if ( container.getNodeType() == Node.TEXT_NODE )
2064            return container;
2065
2066        // This case is an important convenience for
2067
// traverseRightBoundary()
2068
if ( offset<0 )
2069            return container;
2070
2071        Node JavaDoc child = container.getFirstChild();
2072        while( child!=null && offset > 0 )
2073        {
2074            --offset;
2075            child = child.getNextSibling();
2076        }
2077        if ( child!=null )
2078            return child;
2079        return container;
2080    }
2081
2082}
2083
Popular Tags