KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > editor > highlighting > support > PositionsBag


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.spi.editor.highlighting.support;
21
22 import java.util.ConcurrentModificationException JavaDoc;
23 import java.util.NoSuchElementException JavaDoc;
24 import java.util.logging.Level JavaDoc;
25 import java.util.logging.Logger JavaDoc;
26 import javax.swing.text.AttributeSet JavaDoc;
27 import javax.swing.text.Document JavaDoc;
28 import javax.swing.text.Position JavaDoc;
29 import org.netbeans.api.editor.settings.AttributesUtilities;
30 import org.netbeans.lib.editor.util.GapList;
31 import org.netbeans.spi.editor.highlighting.*;
32
33 /**
34  * A bag of highlighted areas specified by their document <code>Position</code>s.
35  *
36  * <p>The highlighted areas (highlights) are determined by their starting and ending
37  * positions in a document and the set of attributes that should be used for rendering
38  * that area. The highlights can be arbitrarily added to and remove from the bag.
39  *
40  * <p>The <code>PositionsBag</code> is designed to work over a single
41  * document, which is passed in to the constructor. All the <code>Position</code>s
42  * accepted by various methods in this class must refer to positions within
43  * this document. Since there is no way how this could be checked it is up to
44  * the users of this class to make sure that <code>Position</code>s they pass in
45  * to the bag are from the same <code>Document</code>, which they used for creating
46  * the bag.
47  *
48  * <p>The <code>PositionsBag</code> can operate in two modes depending on a
49  * value passed in its construtor. The mode determines how the bag will treat
50  * newly added highlights that overlap with existing highlights in the bag.
51  *
52  * <p><b>Trimming mode</b>:
53  * In the trimming mode the bag will <i>trim</i> any existing highlights that
54  * would overlap with a newly added highlight. In this mode the newly
55  * added highlights always replace the existing highlights if they overlap.
56  *
57  * <p><b>Merging mode</b>:
58  * In the merging mode the bag will <i>merge</i> attributes of the overlapping highlights.
59  * The area where the new highlight overlaps with some existing highlights will
60  * then constitute a new highlight and its attributes will be a composition of
61  * the attributes of both the new and existing highlight. Should there be attributes
62  * with the same name the attribute values from the newly added highlight will take
63  * precedence.
64  *
65  * @author Vita Stejskal
66  */

67 public final class PositionsBag extends AbstractHighlightsContainer {
68
69     private static final Logger JavaDoc LOG = Logger.getLogger(PositionsBag.class.getName());
70     
71     private Document JavaDoc document;
72     private boolean mergeHighlights;
73
74     private GapList<Position JavaDoc> marks = new GapList<Position JavaDoc>();
75     private GapList<AttributeSet JavaDoc> attributes = new GapList<AttributeSet JavaDoc>();
76     private long version = 0;
77     
78     /**
79      * Creates a new instance of <code>PositionsBag</code>, which trims highlights
80      * as they are added. It calls the {@link #PositionsBag(Document, boolean)} constructore
81      * passing <code>false</code> as a parameter.
82      *
83      * @param document The document that should be highlighted.
84      */

85     public PositionsBag(Document JavaDoc document) {
86         this(document, false);
87     }
88     
89     /**
90      * Creates a new instance of <code>PositionsBag</code>.
91      *
92      * @param document The document that should be highlighted.
93      * @param mergeHighlights Determines whether highlights should be merged
94      * or trimmed.
95      */

96     public PositionsBag(Document JavaDoc document, boolean mergeHighlights) {
97         this.document = document;
98         this.mergeHighlights = mergeHighlights;
99     }
100     
101     /**
102      * Adds a highlight to this bag. The highlight is specified by its staring
103      * and ending <code>Position</code> and by its attributes. Adding a highlight that overlaps
104      * with one or more existing highlights can have a different result depending
105      * on the value of the <code>mergingHighlights</code> parameter used for
106      * constructing this bag.
107      *
108      * @param startPosition The beginning of the highlighted area.
109      * @param endPosition The end of the highlighted area.
110      * @param attributes The attributes to use for highlighting.
111      */

112     public void addHighlight(Position JavaDoc startPosition, Position JavaDoc endPosition, AttributeSet JavaDoc attributes) {
113         int [] offsets;
114         
115         synchronized (marks) {
116             offsets = addHighlightImpl(startPosition, endPosition, attributes);
117             if (offsets != null) {
118                 version++;
119             }
120         }
121         
122         if (offsets != null) {
123             fireHighlightsChange(offsets[0], offsets[1]);
124         }
125     }
126     
127     /**
128      * Adds all highlights from the bag passed in. This method is equivalent
129      * to calling <code>addHighlight</code> for all the highlights in the
130      * <code>bag</code> except that the changes are done atomically.
131      *
132      * @param bag The highlights that will be atomically added to this bag.
133      */

134     public void addAllHighlights(PositionsBag bag) {
135         int [] offsets;
136         
137         synchronized (marks) {
138             offsets = addAllHighlightsImpl(bag);
139             if (offsets != null) {
140                 version++;
141             }
142         }
143         
144         if (offsets != null) {
145             fireHighlightsChange(offsets[0], offsets[1]);
146         }
147     }
148
149     /**
150      * Resets this sequence to use the new set of highlights. This method drops
151      * all the existing highlights in this bag and adds all highlights from
152      * the <code>bag</code> passed in as a parameter. The changes are done atomically.
153      *
154      * @param bag The new highlights.
155      */

156     public void setHighlights(PositionsBag bag) {
157         int changeStart = Integer.MAX_VALUE;
158         int changeEnd = Integer.MIN_VALUE;
159
160         synchronized (marks) {
161             int [] clearedArea = clearImpl();
162             int [] populatedArea = null;
163             
164             GapList<Position JavaDoc> newMarks = bag.getMarks();
165             GapList<AttributeSet JavaDoc> newAttributes = bag.getAttributes();
166
167             synchronized (newMarks) {
168                 for(Position JavaDoc mark : newMarks) {
169                     marks.add(marks.size(), mark);
170                 }
171
172                 for(AttributeSet JavaDoc attrib : newAttributes) {
173                     attributes.add(attributes.size(), attrib);
174                 }
175
176                 if (marks.size() > 0) {
177                     populatedArea = new int [] {
178                         marks.get(0).getOffset(),
179                         marks.get(marks.size() - 1).getOffset()
180                     };
181                 }
182             }
183             
184             if (clearedArea != null) {
185                 changeStart = clearedArea[0];
186                 changeEnd = clearedArea[1];
187             }
188
189             if (populatedArea != null) {
190                 if (changeStart == Integer.MAX_VALUE || changeStart > populatedArea[0]) {
191                     changeStart = populatedArea[0];
192                 }
193                 if (changeEnd == Integer.MIN_VALUE || changeEnd < populatedArea[1]) {
194                     changeEnd = populatedArea[1];
195                 }
196             }
197             
198             if (changeStart < changeEnd) {
199                 version++;
200             }
201         }
202         
203         if (changeStart < changeEnd) {
204             fireHighlightsChange(changeStart, changeEnd);
205         }
206     }
207     
208     /**
209      * Removes highlights in the specific area. All existing highlights
210      * that are positioned within the area specified by the <code>startOffset</code> and
211      * <code>endOffset</code> parameters are removed from this bag. The highlights
212      * that only partialy overlap with the area are treated according to the value
213      * of the <code>clip</code> parameter.
214      *
215      * <ul>
216      * <li>If <code>clip == true</code> : the overlapping highlights will remain
217      * in this sequence but will be clipped so that they do not overlap anymore
218      * <li>If <code>clip == false</code> : the overlapping highlights will be
219      * removed from this sequence
220      * </ul>
221      *
222      * @param startPosition The beginning of the area to clear.
223      * @param endPosition The end of the area to clear.
224      */

225     public void removeHighlights(Position JavaDoc startPosition, Position JavaDoc endPosition, boolean clip) {
226         if (!clip) {
227             removeHighlights(startPosition.getOffset(), endPosition.getOffset());
228             return;
229         }
230
231         int changeStart = Integer.MAX_VALUE;
232         int changeEnd = Integer.MIN_VALUE;
233
234         // Ignore empty areas
235
if (startPosition.getOffset() == endPosition.getOffset()) {
236             return;
237         } else {
238             assert startPosition.getOffset() < endPosition.getOffset() :
239                 "Start position must be before the end position"; //NOI18N
240
}
241         
242         synchronized (marks) {
243             if (marks.isEmpty()) {
244                 return;
245             }
246
247             int startIdx = indexBeforeOffset(startPosition.getOffset());
248             int endIdx = indexBeforeOffset(endPosition.getOffset(), startIdx < 0 ? 0 : startIdx, marks.size() - 1);
249
250 // System.out.println("removeHighlights(" + startOffset + ", " + endOffset + ", " + clip + ") : startIdx = " + startIdx + ", endIdx = " + endIdx);
251

252             if (startIdx == endIdx) {
253                 if (startIdx != -1 && attributes.get(startIdx) != null) {
254                     AttributeSet JavaDoc original = attributes.get(startIdx);
255
256                     if (marks.get(startIdx).getOffset() == startPosition.getOffset()) {
257                         marks.set(startIdx, endPosition);
258                         attributes.set(startIdx, original);
259                     } else {
260                         marks.add(startIdx + 1, startPosition);
261                         attributes.add(startIdx + 1, null);
262                         marks.add(startIdx + 2, endPosition);
263                         attributes.add(startIdx + 2, original);
264                     }
265
266                     changeStart = startPosition.getOffset();
267                     changeEnd = endPosition.getOffset();
268                 }
269             } else {
270                 assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx;
271
272                 if (attributes.get(endIdx) != null) {
273                     marks.set(endIdx, endPosition);
274                     changeEnd = endPosition.getOffset();
275                     endIdx--;
276                 }
277
278                 if (startIdx != -1 && attributes.get(startIdx) != null) {
279                     startIdx++;
280                     if (startIdx <= endIdx) {
281                         marks.set(startIdx, startPosition);
282                         attributes.set(startIdx, null);
283                     } else {
284                         marks.add(startIdx, startPosition);
285                         attributes.add(startIdx, null);
286                     }
287                     changeStart = startPosition.getOffset();
288                 }
289                 startIdx++;
290
291                 if (startIdx <= endIdx) {
292                     if (changeStart == Integer.MAX_VALUE) {
293                         changeStart = marks.get(startIdx).getOffset();
294                     }
295                     if (changeEnd == Integer.MIN_VALUE) {
296                         changeEnd = marks.get(endIdx).getOffset();
297                     }
298                     marks.remove(startIdx, endIdx - startIdx + 1);
299                     attributes.remove(startIdx, endIdx - startIdx + 1);
300                 }
301             }
302
303             if (changeStart < changeEnd) {
304                 version++;
305             }
306         }
307
308         if (changeStart < changeEnd) {
309             fireHighlightsChange(changeStart, changeEnd);
310         }
311     }
312
313     /**
314      * Removes highlights in the specific area. All existing highlights
315      * that are positioned within the area specified by the <code>startOffset</code> and
316      * <code>endOffset</code> parameters are removed from this sequence. The highlights
317      * that only partialy overlap with the area will be removed as well.
318      *
319      * @param startOffset The beginning of the area to clear.
320      * @param endOffset The end of the area to clear.
321      */

322     public void removeHighlights(int startOffset, int endOffset) {
323         int changeStart = Integer.MAX_VALUE;
324         int changeEnd = Integer.MIN_VALUE;
325
326         // Ignore empty areas
327
if (startOffset == endOffset) {
328             return;
329         } else {
330             assert startOffset < endOffset : "Start position must be before the end position"; //NOI18N
331
}
332         
333         synchronized (marks) {
334             if (marks.isEmpty()) {
335                 return;
336             }
337             
338             int startIdx = indexBeforeOffset(startOffset);
339             int endIdx = indexBeforeOffset(endOffset, startIdx < 0 ? 0 : startIdx, marks.size() - 1);
340             
341 // System.out.println("removeHighlights(" + startOffset + ", " + endOffset + ", " + clip + ") : startIdx = " + startIdx + ", endIdx = " + endIdx);
342

343             if (startIdx == -1 || attributes.get(startIdx) == null) {
344                 startIdx++;
345             }
346
347             if (endIdx != -1 && attributes.get(endIdx) != null) {
348                 endIdx++;
349             }
350             
351             if (startIdx < endIdx) {
352                 if (changeStart == Integer.MAX_VALUE) {
353                     changeStart = marks.get(startIdx).getOffset();
354                 }
355                 if (changeEnd == Integer.MIN_VALUE) {
356                     changeEnd = marks.get(endIdx).getOffset();
357                 }
358                 marks.remove(startIdx, endIdx - startIdx + 1);
359             }
360             
361             if (changeStart < changeEnd) {
362                 version++;
363             }
364         }
365         
366         if (changeStart < changeEnd) {
367             fireHighlightsChange(changeStart, changeEnd);
368         }
369     }
370
371     /**
372      * Gets highlights from an area of a document. The <code>HighlightsSequence</code> is
373      * computed using all the highlights present in this bag between the
374      * <code>startOffset</code> and <code>endOffset</code>.
375      *
376      * @param startOffset The beginning of the area.
377      * @param endOffset The end of the area.
378      *
379      * @return The <code>HighlightsSequence</code> which iterates through the
380      * highlights in the given area of this bag.
381      */

382     public HighlightsSequence getHighlights(int startOffset, int endOffset) {
383         if (LOG.isLoggable(Level.FINE) && !(startOffset < endOffset)) {
384             LOG.fine("startOffset must be less than endOffset: startOffset = " + //NOI18N
385
startOffset + " endOffset = " + endOffset); //NOI18N
386
}
387         
388         synchronized (marks) {
389             return new Seq(version, startOffset, endOffset);
390         }
391     }
392
393     /**
394      * Removes all highlights previously added to this bag.
395      */

396     public void clear() {
397         int [] clearedArea;
398         
399         synchronized (marks) {
400             clearedArea = clearImpl();
401             if (clearedArea != null) {
402                 version++;
403             }
404         }
405         
406         if (clearedArea != null) {
407             fireHighlightsChange(clearedArea[0], clearedArea[1]);
408         }
409     }
410     
411     // ----------------------------------------------------------------------
412
// Package private API
413
// ----------------------------------------------------------------------
414

415     /* package */ GapList<Position JavaDoc> getMarks() {
416         return marks;
417     }
418
419     /* package */ GapList<AttributeSet JavaDoc> getAttributes() {
420         return attributes;
421     }
422     
423     // ----------------------------------------------------------------------
424
// Private implementation
425
// ----------------------------------------------------------------------
426

427     private int [] addHighlightImpl(Position JavaDoc startPosition, Position JavaDoc endPosition, AttributeSet JavaDoc attributes) {
428         if (startPosition.getOffset() == endPosition.getOffset()) {
429             return null;
430         } else {
431             assert startPosition.getOffset() < endPosition.getOffset() :
432                 "Start position must be before the end position."; //NOI18N
433
assert attributes != null : "Highlight attributes must not be null."; //NOI18N
434
}
435
436         if (mergeHighlights) {
437             merge(startPosition, endPosition, attributes);
438         } else {
439             trim(startPosition, endPosition, attributes);
440         }
441
442         return new int [] { startPosition.getOffset(), endPosition.getOffset() };
443     }
444     
445     private void merge(Position JavaDoc startPosition, Position JavaDoc endPosition, AttributeSet JavaDoc attrSet) {
446         AttributeSet JavaDoc lastKnownAttributes = null;
447         int startIdx = indexBeforeOffset(startPosition.getOffset());
448         if (startIdx < 0) {
449             startIdx = 0;
450             marks.add(startIdx, startPosition);
451             attributes.add(startIdx, attrSet);
452         } else {
453             Position JavaDoc mark = marks.get(startIdx);
454             AttributeSet JavaDoc newAttribs = AttributesUtilities.createComposite(attrSet, attributes.get(startIdx));
455             lastKnownAttributes = attributes.get(startIdx);
456
457             if (mark.getOffset() == startPosition.getOffset()) {
458                 attributes.set(startIdx, newAttribs);
459             } else {
460                 startIdx++;
461                 marks.add(startIdx, startPosition);
462                 attributes.add(startIdx, newAttribs);
463             }
464         }
465
466         for(int idx = startIdx + 1; ; idx++) {
467             if (idx < marks.size()) {
468                 Position JavaDoc mark = marks.get(idx);
469
470                 if (mark.getOffset() < endPosition.getOffset()) {
471                     lastKnownAttributes = attributes.get(idx);
472                     attributes.set(idx, AttributesUtilities.createComposite(attrSet, lastKnownAttributes));
473                 } else {
474                     if (mark.getOffset() > endPosition.getOffset()) {
475                         marks.add(idx, endPosition);
476                         attributes.add(idx, lastKnownAttributes);
477                     }
478                     break;
479                 }
480             } else {
481                 marks.add(idx, endPosition);
482                 attributes.add(idx, lastKnownAttributes);
483                 break;
484             }
485         }
486     }
487
488     private void trim(Position JavaDoc startPosition, Position JavaDoc endPosition, AttributeSet JavaDoc attrSet) {
489         int startIdx = indexBeforeOffset(startPosition.getOffset());
490         int endIdx = indexBeforeOffset(endPosition.getOffset(), startIdx < 0 ? 0 : startIdx, marks.size() - 1);
491         
492 // System.out.println("trim(" + startOffset + ", " + endOffset + ") : startIdx = " + startIdx + ", endIdx = " + endIdx);
493

494         if (startIdx == endIdx) {
495             AttributeSet JavaDoc original = null;
496             if (startIdx != -1 && attributes.get(startIdx) != null) {
497                 original = attributes.get(startIdx);
498             }
499             
500             if (startIdx != -1 && marks.get(startIdx).getOffset() == startPosition.getOffset()) {
501                 attributes.set(startIdx, attrSet);
502             } else {
503                 startIdx++;
504                 marks.add(startIdx, startPosition);
505                 attributes.add(startIdx, attrSet);
506             }
507             
508             startIdx++;
509             marks.add(startIdx, endPosition);
510             attributes.add(startIdx, original);
511         } else {
512             assert endIdx != -1 : "Invalid range: startIdx = " + startIdx + " endIdx = " + endIdx;
513
514             marks.set(endIdx, endPosition);
515             attributes.set(endIdx, attributes.get(endIdx));
516             endIdx--;
517
518             startIdx++;
519             if (startIdx <= endIdx) {
520                 marks.set(startIdx, startPosition);
521                 attributes.set(startIdx, attrSet);
522             } else {
523                 marks.add(startIdx, startPosition);
524                 attributes.add(startIdx, attrSet);
525             }
526             startIdx++;
527             
528             if (startIdx <= endIdx) {
529                 marks.remove(startIdx, endIdx - startIdx + 1);
530                 attributes.remove(startIdx, endIdx - startIdx + 1);
531             }
532         }
533     }
534     
535     private int [] addAllHighlightsImpl(PositionsBag bag) {
536         int changeStart = Integer.MAX_VALUE;
537         int changeEnd = Integer.MIN_VALUE;
538
539         GapList<Position JavaDoc> newMarks = bag.getMarks();
540         GapList<AttributeSet JavaDoc> newAttributes = bag.getAttributes();
541         
542         for (int i = 0 ; i + 1 < newMarks.size(); i++) {
543             Position JavaDoc mark1 = newMarks.get(i);
544             Position JavaDoc mark2 = newMarks.get(i + 1);
545             AttributeSet JavaDoc attrSet = newAttributes.get(i);
546             
547             if (attrSet == null) {
548                 // skip empty highlight
549
continue;
550             }
551             
552             addHighlightImpl(mark1, mark2, attrSet);
553
554             if (changeStart == Integer.MAX_VALUE) {
555                 changeStart = mark1.getOffset();
556             }
557             changeEnd = mark2.getOffset();
558         }
559
560         if (changeStart != Integer.MAX_VALUE && changeEnd != Integer.MIN_VALUE) {
561             return new int [] { changeStart, changeEnd };
562         } else {
563             return null;
564         }
565     }
566     
567     private int [] clearImpl() {
568         if (!marks.isEmpty()) {
569             int changeStart = marks.get(0).getOffset();
570             int changeEnd = marks.get(marks.size() - 1).getOffset();
571
572             marks.clear();
573             attributes.clear();
574
575             return new int [] { changeStart, changeEnd };
576         } else {
577             return null;
578         }
579     }
580
581     private int indexBeforeOffset(int offset, int low, int high) {
582         int idx = findElementIndex(offset, low, high);
583         if (idx < 0) {
584             idx = -idx - 1; // the insertion point: <0, size()>
585
return idx - 1;
586         } else {
587             return idx;
588         }
589     }
590     
591     private int indexBeforeOffset(int offset) {
592         return indexBeforeOffset(offset, 0, marks.size() - 1);
593     }
594
595     private int findElementIndex(int offset, int lowIdx, int highIdx) {
596         if (lowIdx < 0 || highIdx > marks.size() - 1) {
597             throw new IndexOutOfBoundsException JavaDoc("lowIdx = " + lowIdx + ", highIdx = " + highIdx + ", size = " + marks.size());
598         }
599         
600         int low = lowIdx;
601         int high = highIdx;
602
603         while (low <= high) {
604             int index = (low + high) >> 1; // mid in the binary search
605
int elemOffset = marks.get(index).getOffset();
606
607             if (elemOffset < offset) {
608                 low = index + 1;
609
610             } else if (elemOffset > offset) {
611                 high = index - 1;
612
613             } else { // exact offset found at index
614
while (index > 0) {
615                     index--;
616                     if (marks.get(index).getOffset() < offset) {
617                         index++;
618                         break;
619                     }
620                 }
621                 return index;
622             }
623         }
624         
625         return -(low + 1);
626     }
627     
628     private final class Seq implements HighlightsSequence {
629
630         private long version;
631         private int startOffset;
632         private int endOffset;
633
634         private int idx = -1;
635
636         public Seq(long version, int startOffset, int endOffset) {
637             this.version = version;
638             this.startOffset = startOffset;
639             this.endOffset = endOffset;
640         }
641
642         public boolean moveNext() {
643             synchronized (PositionsBag.this.marks) {
644                 checkVersion();
645                 
646                 if (idx == -1) {
647                     idx = indexBeforeOffset(startOffset);
648                     if (idx == -1 && marks.size() > 0) {
649                         idx = 0;
650                     }
651                 } else {
652                     idx++;
653                 }
654
655                 while (isIndexValid(idx)) {
656                     if (attributes.get(idx) != null) {
657                         return true;
658                     }
659
660                     // Skip any empty areas
661
idx++;
662                 }
663
664                 return false;
665             }
666         }
667
668         public int getStartOffset() {
669             synchronized (PositionsBag.this.marks) {
670                 assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N
671
checkVersion();
672                 
673                 if (isIndexValid(idx)) {
674                     return Math.max(marks.get(idx).getOffset(), startOffset);
675                 } else {
676                     throw new NoSuchElementException JavaDoc();
677                 }
678             }
679         }
680
681         public int getEndOffset() {
682             synchronized (PositionsBag.this.marks) {
683                 assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N
684
checkVersion();
685                 
686                 if (isIndexValid(idx)) {
687                     return Math.min(marks.get(idx + 1).getOffset(), endOffset);
688                 } else {
689                     throw new NoSuchElementException JavaDoc();
690                 }
691             }
692         }
693
694         public AttributeSet JavaDoc getAttributes() {
695             synchronized (PositionsBag.this.marks) {
696                 assert idx != -1 : "Sequence not initialized, call moveNext() first."; //NOI18N
697
checkVersion();
698                 
699                 if (isIndexValid(idx)) {
700                     return attributes.get(idx);
701                 } else {
702                     throw new NoSuchElementException JavaDoc();
703                 }
704             }
705         }
706         
707         private boolean isIndexValid(int idx) {
708             return idx >= 0 &&
709                     idx + 1 < marks.size() &&
710                     marks.get(idx).getOffset() < endOffset &&
711                     marks.get(idx + 1).getOffset() > startOffset;
712         }
713         
714         private void checkVersion() {
715             if (PositionsBag.this.version != version) {
716                 throw new ConcurrentModificationException JavaDoc();
717             }
718         }
719     } // End of Seq class
720
}
721
Popular Tags