KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > MarkVector


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.editor;
21
22 import java.util.List JavaDoc;
23 import javax.swing.text.Position JavaDoc;
24
25 /**
26  * Container for {@link MultiMark} for the document.
27  * The structure is array with a gap together with an offset gap
28  * for the abstract storage of the document text.
29  *
30  * @author Miloslav Metelka
31  * @version 1.00
32  */

33
34 final class MarkVector {
35
36     /** Empty array of marks used initially as markArray. */
37     private static final MultiMark[] EMPTY = new MultiMark[0];
38
39     /** Default size of the offset gap. */
40     private static final int INITIAL_OFFSET_GAP_SIZE = (Integer.MAX_VALUE >> 1);
41     
42     /** Array of the marks with the gap. */
43     private MultiMark[] markArray;
44     
45     /** Starting index of the gap in the markArray. */
46     private int gapStart;
47     
48     /** Length of the gap in the markArray.
49      * The length of the gap must always be >0.
50      */

51     private int gapLength;
52     
53     /** Starting offset of the offset gap */
54     private int offsetGapStart;
55     
56     /** Length of the offset gap */
57     private int offsetGapLength;
58     
59     /** Number of marks that are still in the markArray but
60      * that are no longer valid.
61      */

62     private int disposedMarkCount;
63     
64     MarkVector() {
65         markArray = EMPTY;
66         offsetGapLength = INITIAL_OFFSET_GAP_SIZE;
67     }
68     
69     /** Create mark with the given bias. The mark is not automatically
70      * inserted into the vector.
71      * @param offset offset of the mark.
72      * @param bias bias of the mark.
73      */

74     MultiMark createBiasMark(int offset, Position.Bias JavaDoc bias) {
75         return new MultiMark(this, offset, bias);
76     }
77     
78     /** Create swing-compatible mark. The mark is not automatically
79      * inserted into the vector.
80      * @param offset offset of the mark.
81      */

82     MultiMark createMark(int offset) {
83         return new MultiMark(this, offset);
84     }
85     
86     /** @return total count of marks in the vector.
87      */

88     int getMarkCount() {
89         return markArray.length - gapLength;
90     }
91     
92     MultiMark getMark(int index) {
93         return markArray[getRawIndex(index)];
94     }
95     
96     int getMarkOffsetInternal(int index) {
97         return getOffset(getMark(index).rawOffset);
98     }
99
100     /** Insert mark previously created by <CODE>createBiasMark()</CODE>
101      * or <CODE>createMark()</CODE>.
102      * @param mark mark to insert. The mark must have the valid
103      * offset and flags filled in.
104      */

105     synchronized MultiMark insert(MultiMark mark) {
106         int flags = mark.flags;
107         if ((flags & MultiMark.VALID) != 0) { // trying to re-insert valid mark
108
throw new IllegalStateException JavaDoc();
109         }
110
111         int offset = mark.rawOffset;
112         int index = findInsertIndex(offset);
113         if (gapLength == 0) {
114             enlargeGap(1);
115         }
116         if (index != gapStart) {
117             moveGap(index);
118         }
119
120         if (offset > offsetGapStart
121             || (offset == offsetGapStart
122                 && ((flags & MultiMark.BACKWARD_BIAS) == 0))
123         ) { // above offset gap
124
mark.rawOffset += offsetGapLength;
125         }
126
127         markArray[gapStart++] = mark;
128         gapLength--;
129         mark.flags |= MultiMark.VALID;
130
131         return mark;
132     }
133     
134     /** Insert list of marks at once. When inserting
135      * a large number of sorted marks (at least partially) this method offers
136      * better performance.
137      * @param markList list of MultiMarks to insert.
138      */

139     synchronized void insertList(List JavaDoc markList) {
140         int lastOffset = Integer.MAX_VALUE;
141         boolean lastBackwardBias = true;
142         int upperOffset = 0;
143         boolean upperBackwardBias = false;
144         int markCount = getMarkCount();
145         int insertMarkCount = markList.size();
146         
147         if (gapLength < insertMarkCount) {
148             enlargeGap(insertMarkCount); // enough space for all the marks
149
}
150         
151         for (int i = 0; i < insertMarkCount; i++ ) {
152             MultiMark mark = (MultiMark)markList.get(i);
153             int flags = mark.flags;
154             if ((flags & MultiMark.VALID) != 0) { // trying to re-insert valid mark
155
throw new IllegalStateException JavaDoc();
156             }
157             boolean backwardBias = ((flags & MultiMark.BACKWARD_BIAS) != 0);
158
159             int offset = mark.rawOffset;
160             if ((offset < lastOffset)
161                 || (offset == lastOffset && backwardBias && !lastBackwardBias)
162                 || (offset > upperOffset)
163                 || (offset == upperOffset && !backwardBias && upperBackwardBias)
164             ) {
165                 // Find the index and inspect previous/next marks
166
int index = findInsertIndex(offset);
167                 if (index != gapStart) {
168                     moveGap(index);
169                 }
170                 
171                 if (index < markCount) {
172                     MultiMark m = markArray[getRawIndex(index)];
173                     upperOffset = getOffset(m.rawOffset);
174                     upperBackwardBias = ((m.flags & MultiMark.BACKWARD_BIAS) != 0);
175
176                 } else { // was last mark
177
upperOffset = Integer.MAX_VALUE;
178                     upperBackwardBias = false;
179                 }
180             }
181                 
182             if (offset > offsetGapStart
183                 || (offset == offsetGapStart
184                     && ((flags & MultiMark.BACKWARD_BIAS) == 0))
185             ) { // above offset gap
186
mark.rawOffset += offsetGapLength;
187             }
188             
189             markArray[gapStart++] = mark;
190             gapLength--;
191             mark.flags |= MultiMark.VALID;
192
193             lastOffset = offset;
194             lastBackwardBias = backwardBias;
195             markCount++;
196         }
197     }
198     
199     synchronized void notifyMarkDisposed() {
200         disposedMarkCount++;
201         
202         if (disposedMarkCount > Math.max(5, getMarkCount() / 10)) {
203             removeDisposedMarks();
204         }
205     }
206
207     synchronized void compact() {
208         if (gapLength > 0) {
209             int newLength = markArray.length - gapLength;
210             MultiMark[] newMarkArray = new MultiMark[newLength];
211             int gapEnd = gapStart + gapLength;
212             System.arraycopy(markArray, 0, newMarkArray, 0, gapStart);
213             System.arraycopy(markArray, gapEnd, newMarkArray, gapStart,
214                 markArray.length - gapEnd);
215             markArray = newMarkArray;
216             gapStart = markArray.length;
217             gapLength = 0;
218         }
219     }
220
221     /** Document was modified. This means that the ficitonal
222      * offset gap must be updated by the modifiaction. The offset gap can
223      * possibly be moved and the marks in the moved area must
224      * be updated accordingly.
225      * @param offset offset of the modification
226      * @param length length of added/removed data. If the length is positive
227      * then the insert occured. If the length is negative then
228      * the removal has occured.
229      * @param undo undo information to be processed. It can be null
230      * if there is no undo information for this update operation.
231      * @return non-null undo record or null in case an insert was done
232      * or there are no marks to be undone.
233      */

234     synchronized Undo update(int offset, int length, Undo undo) {
235         if (length < 0) { // removal occured - temporarily increase offset var
236
offset -= length; // move offset after end of removal (length < 0)
237
}
238
239         // First move the gap if necessary
240
int offsetGapIndex = findInsertIndex(offset);
241
242         moveOffsetGap(offsetGapIndex, offset);
243         offsetGapStart += length;
244         offsetGapLength -= length;
245
246         if (length >= 0) { // insert performed
247
if (undo != null) { // in undo or redo
248
/* It's necessary to restore the offsets
249                  * of the marks contained in the linked list of undo items.
250                  * The 'logicalNext' will allow to find whether
251                  * there were some marks added or not
252                  * since the time the items being undone were created.
253                  * The 'next' determines the original
254                  * order of the marks.
255                  * It's possible that some marks were already
256                  * disposed so these will be ignored
257                  * (they are possibly no longer in the array).
258                  */

259                 
260                 // Process fwd bias marks first
261
UndoItem dirFirstItem = undo.fbItem;
262                 int fbUndoMarkCount = 0;
263
264                 /* Eliminate the items (by clearing the mark in them)
265                  * of marks that were removed from the vector.
266                  */

267                 while (dirFirstItem != null) {
268                     if ((dirFirstItem.mark.flags & MultiMark.REMOVED) == 0) { // valid
269
fbUndoMarkCount++;
270                         UndoItem item = dirFirstItem.logicalNext;
271                         // Eliminate marks removed from vector
272
while (item != null) {
273                             if ((item.mark.flags & MultiMark.REMOVED) == 0) { // valid
274
fbUndoMarkCount++;
275                             } else { // mark removed from vector
276
item.mark = null;
277                             }
278
279                             item = item.logicalNext;
280                         }
281                         break;
282
283                     } else { // mark removed from vector
284
dirFirstItem.mark = null;
285                     }
286
287                     dirFirstItem = dirFirstItem.logicalNext;
288                 }
289                 
290                 if (dirFirstItem != null) { // some fwd marks to undo
291
// Find the dirFirstItem mark's index
292
MultiMark firstItemMark = dirFirstItem.mark;
293                     int index = offsetGapIndex;
294                     
295                     while (markArray[getRawIndex(index)] != firstItemMark) {
296                         index++;
297                     }
298                     
299                     while (--index >= offsetGapIndex) {
300                         markArray[getRawIndex(index + fbUndoMarkCount)]
301                              = markArray[getRawIndex(index)];
302                     }
303                 }
304                 
305                 dirFirstItem = undo.bbItem;
306                 int bbUndoMarkCount = 0;
307                 
308                 /* Eliminate the items (by clearing the mark in them)
309                  * of marks that were removed from the vector.
310                  */

311                 while (dirFirstItem != null) {
312                     if ((dirFirstItem.mark.flags & MultiMark.REMOVED) == 0) { // valid
313
bbUndoMarkCount++;
314                         UndoItem item = dirFirstItem.logicalNext;
315                         // Eliminate marks removed from vector
316
while (item != null) {
317                             if ((item.mark.flags & MultiMark.REMOVED) == 0) { // valid
318
bbUndoMarkCount++;
319                             } else { // mark removed from vector
320
item.mark = null;
321                             }
322                             item = item.logicalNext;
323                         }
324                         break;
325                         
326                     } else { // mark removed from vector
327
dirFirstItem.mark = null;
328                     }
329                         
330                     dirFirstItem = dirFirstItem.logicalNext;
331                 }
332
333                 if (dirFirstItem != null) { // some marks to undo
334
// Find the dirFirstItem's mark
335
MultiMark firstItemMark = dirFirstItem.mark;
336                     int index = offsetGapIndex;
337                     
338                     while (markArray[getRawIndex(--index)] != firstItemMark) {
339                     }
340                     index++;
341                     
342                     while (index < offsetGapIndex) {
343                         markArray[getRawIndex(index - bbUndoMarkCount)]
344                              = markArray[getRawIndex(index)];
345                         index++;
346                     }
347                 }
348
349
350                 UndoItem origItem = undo.firstItem;
351                 /* Rewrite the current bwd and fwd bias marks
352                  * by the original ones in the right order
353                  */

354                 offsetGapIndex -= bbUndoMarkCount;
355                 while (origItem != null) {
356                     MultiMark mark = origItem.mark;
357                     if (mark != null) {
358                         // Undo the offset
359
mark.rawOffset = origItem.undoOffset;
360                         markArray[getRawIndex(offsetGapIndex++)] = mark;
361                     }
362
363                     origItem = origItem.next;
364                 }
365                 
366                 /* Special case offset == 0 requires handling
367                  * of compatible marks.
368                  */

369                 if (offset == 0) {
370                     ZeroUndoItem zeroItem = undo.zeroItem;
371                     while (zeroItem != null) {
372                         MultiMark mark = zeroItem.mark;
373                         if ((mark.flags & MultiMark.REMOVED) == 0) {
374                             mark.flags &= ~MultiMark.ZERO;
375                         }
376                         zeroItem = zeroItem.next;
377                     }
378                 }
379             }
380             
381             undo = null;
382             
383         } else { // remove performed
384
/* Marks with backward bias within the whole removed area
385              * will be moved to the offset (before offset gap).
386              * It is necessary that all the backwardBias marks
387              * within the removed area must be placed before all
388              * the forwardBias marks within the removed area
389              * in order for the structure to work properly.
390              * Additionally the marks with the forward bias are placed
391              * after current offset gap.
392              * The algorithm goes through the marks in the backward direction.
393              * It uses the last-backward-bias-mark-index that
394              * is -1 unless the first backward-bias-mark is found
395              * then it stores its index. If the forward-bias mark
396              * is then found it must be exchanged with the currently
397              * last backward-bias mark (if upperBBMIndex != -1) and so on
398              * for every forward mark found.
399              * In order to perform undo later the algorithm generates
400              * the linked list of undo items. As the algorithm goes from the end
401              * back (each item has next field) the list of items will
402              * finally be ordered ascendingly by offset.
403              */

404             offset += length; // offset back to original value (length < 0)
405
UndoItem item = null; // current item in the undo list
406
UndoItem fbItem = null; // current forward bias undo item
407
UndoItem bbItem = null; // current backward bias undo item
408
UndoItem upperBBItem = null; // upper backward bias undo item
409
int upperBBMIndex = -1; // index of last backward bias mark
410
int offsetAboveGap = offset + offsetGapLength; // use current (updated) gap size
411
ZeroUndoItem zeroItem = null;
412             
413             if (offset == 0) {
414                 int offsetGapIndexCopy = offsetGapIndex;
415                 int markCount = getMarkCount();
416                 while (offsetGapIndexCopy < markCount) {
417                     MultiMark mark = markArray[getRawIndex(offsetGapIndexCopy++)];
418                     if (mark.rawOffset == offsetAboveGap) {
419                         if ((mark.flags & (MultiMark.COMPATIBLE | MultiMark.ZERO))
420                             == MultiMark.COMPATIBLE
421                         ) {
422                             mark.flags |= MultiMark.ZERO;
423                             zeroItem = new ZeroUndoItem(mark, zeroItem);
424                         }
425
426                     } else { // higher offset
427
break;
428                     }
429                 }
430             }
431                 
432             
433             while (offsetGapIndex > 0) {
434                 MultiMark mark = markArray[getRawIndex(--offsetGapIndex)];
435                 int markOffset = mark.rawOffset; // under offset gap -> real offset
436
boolean backwardBias = ((mark.flags & MultiMark.BACKWARD_BIAS) != 0);
437                 if (markOffset < offset // (all the marks below gap)
438
|| (mark.rawOffset == offset && backwardBias)
439                 ) { // stop at < offset or == offset and backward bias
440
break;
441                 }
442                 
443                 item = new UndoItem(mark, markOffset, item);
444
445                 if (backwardBias) {
446                     if (bbItem != null) {
447                         bbItem.logicalNext = item;
448                     } else { // bbItem is null
449
upperBBItem = item;
450                         upperBBMIndex = offsetGapIndex;
451                     }
452                     bbItem = item;
453
454                     // Move mark to offset - will be before offset gap
455
mark.rawOffset = offset;
456
457                 } else { // forward bias
458
item.logicalNext = fbItem;
459                     fbItem = item;
460
461                     // Mark will be positioned over the offset gap
462
mark.rawOffset = offsetAboveGap;
463
464                     if (upperBBMIndex >= 0) { // backward bias mark(s) exist
465
// exchange this fb mark with upper bb mark
466
int upperBBMRawIndex = getRawIndex(upperBBMIndex--);
467                         markArray[getRawIndex(offsetGapIndex)]
468                             = markArray[upperBBMRawIndex];
469
470                         markArray[upperBBMRawIndex] = mark;
471
472                         UndoItem upperNext = upperBBItem.logicalNext;
473                         if (upperNext != null) {
474                             bbItem.logicalNext = upperBBItem;
475                             bbItem = upperBBItem;
476                             upperBBItem.logicalNext = null;
477                             upperBBItem = upperNext;
478                         }
479                     }
480                 }
481             }
482             
483             /* It's necessary to handle compatible marks
484              * that haven't the ZERO flag set yet
485              * and generate ZeroUndoItem for each of them.
486              */

487             if (offset == 0 && item != null) {
488                 UndoItem i = item;
489                 while (i != null) {
490                     MultiMark mark = i.mark;
491                     if ((mark.flags & (MultiMark.COMPATIBLE | MultiMark.ZERO))
492                         == MultiMark.COMPATIBLE
493                     ) {
494                         mark.flags |= MultiMark.ZERO;
495                         zeroItem = new ZeroUndoItem(mark, zeroItem);
496                     }
497                     i = i.next;
498                 }
499             }
500                 
501             undo = (item != null || zeroItem != null)
502                 ? new Undo(item, fbItem, upperBBItem, zeroItem)
503                 : null;
504
505         }
506         
507         return undo;
508     }
509     
510     private void removeDisposedMarks() {
511         int rawIndex = 0;
512         int validInd = -1;
513         int gapEnd = gapStart + gapLength;
514
515         while (rawIndex < gapStart) {
516             MultiMark mark = markArray[rawIndex];
517             if ((mark.flags & MultiMark.VALID) != 0) { // valid mark
518
if (rawIndex != ++validInd) {
519                     markArray[validInd] = mark;
520                 }
521
522             } else { // mark not valid
523
mark.flags |= MultiMark.REMOVED;
524             }
525             rawIndex++;
526         }
527         gapStart = validInd + 1;
528         
529         rawIndex = markArray.length;
530         validInd = rawIndex;
531         while (--rawIndex >= gapEnd) {
532             MultiMark mark = markArray[rawIndex];
533             if ((mark.flags & MultiMark.VALID) != 0) { // valid mark
534
if (rawIndex != --validInd) {
535                     markArray[validInd] = mark;
536                 }
537
538             } else { // mark not valid
539
mark.flags |= MultiMark.REMOVED;
540             }
541         }
542         gapLength = validInd - gapStart;
543         
544         disposedMarkCount = 0;
545     }
546
547     int getOffset(int rawOffset) {
548         /* rawOffset == offsetGapStart for backward bias marks
549          * forward bias marks go typically over the end of the offset gap.
550          * The offsetGapLength must always stay to be >0.
551          */

552         return (rawOffset <= offsetGapStart)
553             ? rawOffset
554             : (rawOffset - offsetGapLength);
555     }
556
557     private int getRawIndex(int index) {
558         return (index < gapStart)
559             ? index
560             : index + gapLength;
561     }
562     
563     /** Find the index at which it's valid to perform an insert of the new mark.
564      * @param offset offset of the mark
565      * @return index &gt;= 0 and &lt;=<CODE>getMarkCount()</CODE>
566      * in the markArray where the insert of the mark with the given
567      * offset can be done.
568      * <BR>If there are marks with the same offset as the given one
569      * then there are first all the marks with the backward bias
570      * and then all the marks with the forward bias. The method
571      * will return the index of the first mark with forward bias.
572      */

573     private int findInsertIndex(int offset) {
574         int low = 0;
575         int high = getMarkCount() - 1;
576         
577         while (low <= high) {
578             int index = (low + high) / 2; // mid in the binary search
579
MultiMark mark = markArray[getRawIndex(index)];
580             int markOffset = getOffset(mark.rawOffset);
581
582             if (markOffset < offset) {
583                 low = index + 1;
584             
585             } else if (markOffset > offset) {
586                 high = index - 1;
587             
588             } else { // exact offset found - use bias for positioning
589
if ((mark.flags & MultiMark.BACKWARD_BIAS) != 0) { // bwd bias
590
low = index + 1;
591                 } else { // fwd bias
592
high = index - 1;
593                 }
594             }
595         }
596
597         return low;
598     }
599         
600     private void moveGap(int index) {
601         if (index <= gapStart) { // move gap down
602
int moveSize = gapStart - index;
603             System.arraycopy(markArray, index, markArray,
604                 gapStart + gapLength - moveSize, moveSize);
605             gapStart = index;
606
607         } else { // above gap
608
int moveSize = index - gapStart;
609             System.arraycopy(markArray, gapStart + gapLength, markArray, gapStart, moveSize);
610             gapStart += moveSize;
611         }
612     }
613     
614     private void moveOffsetGap(int index, int newOffsetGapStart) {
615         int rawIndex = getRawIndex(index);
616         int markArrayLength = markArray.length;
617         int offset = offsetGapStart;
618         offsetGapStart = newOffsetGapStart;
619         int length = offsetGapLength;
620         
621         if (rawIndex == markArrayLength
622             || markArray[rawIndex].rawOffset > offset
623         ) { // go down to check and fix the marks are below the offset
624

625             int bound = (rawIndex < gapStart)
626                 ? 0
627                 : (gapStart + gapLength);
628
629             while (true) {
630                 while (--rawIndex >= bound) {
631                     MultiMark mark = markArray[rawIndex];
632                     if (mark.rawOffset > offset) {
633                         mark.rawOffset -= length;
634                     }
635                 }
636
637                 if (bound > 0) { // shift the bound
638
bound = 0;
639                     rawIndex = gapStart;
640
641                 } else { // update gap bounds and break the loop
642
break;
643                 }
644             }
645             
646         } else { // go up to check and fix the marks are above the offset
647

648             int bound = (rawIndex < gapStart)
649                 ? gapStart
650                 : markArrayLength;
651
652             while (true) {
653                 while (rawIndex < bound) {
654                     MultiMark mark = markArray[rawIndex];
655                     if (mark.rawOffset <= offset) {
656                         mark.rawOffset += length;
657                     }
658                     rawIndex++;
659                 }
660
661                 if (bound < markArrayLength) { // shift the bound
662
bound = markArrayLength;
663                     rawIndex += gapLength;
664
665                 } else { // update gap bounds and break the loop
666
break;
667                 }
668             }
669             
670         }
671     }
672
673     private void enlargeGap(int extraLength) {
674         int newLength = Math.max(8, markArray.length * 3 / 2 + extraLength);
675         int gapEnd = gapStart + gapLength;
676         int afterGapLength = (markArray.length - gapEnd);
677         int newGapEnd = newLength - afterGapLength;
678         MultiMark[] newMarkArray = new MultiMark[newLength];
679         System.arraycopy(markArray, 0, newMarkArray, 0, gapStart);
680         System.arraycopy(markArray, gapEnd, newMarkArray, newGapEnd, afterGapLength);
681         markArray = newMarkArray;
682         gapLength = newGapEnd - gapStart;
683     }
684     
685     /** @return the array of objects describing
686      * the state of this object for testing and debug purposes.
687      * They are returned in the following order:<pre>
688      * markArray
689      * gapStart
690      * gapLength
691      * offsetGapStart
692      * offsetGapLength
693      * <pre>
694      */

695     Object JavaDoc[] toObjects() {
696         return new Object JavaDoc[] {
697             markArray.clone(),
698             new Integer JavaDoc(gapStart),
699             new Integer JavaDoc(gapLength),
700             new Integer JavaDoc(offsetGapStart),
701             new Integer JavaDoc(offsetGapLength)
702         };
703     }
704
705     /** Get info about this mark vector. */
706     public String JavaDoc toString() {
707         return "markCount=" + getMarkCount() // NOI18N
708
+ ", gapStart=" + gapStart // NOI18N
709
+ ", gapLength=" + gapLength // NOI18N
710
+ ", offsetGapStart=" + offsetGapStart // NOI18N
711
+ ", offsetGapLength=" + offsetGapLength; // NOI18N
712
}
713     
714     /** Undo record holding the info about undo items.
715      */

716     static final class Undo {
717         
718         Undo(UndoItem firstItem, UndoItem fbItem, UndoItem bbItem,
719         ZeroUndoItem zeroItem) {
720
721             this.firstItem = firstItem;
722             this.fbItem = fbItem;
723             this.bbItem = bbItem;
724             this.zeroItem = zeroItem;
725         }
726         
727         UndoItem firstItem;
728         
729         UndoItem fbItem;
730         
731         UndoItem bbItem;
732         
733         ZeroUndoItem zeroItem;
734
735     }
736
737     /** Class used for holding the offset to which the mark
738      * will be restored in case it was inside the area
739      * that was removed.
740      */

741     static final class UndoItem {
742         
743         UndoItem(MultiMark mark, int undoOffset, UndoItem next) {
744             this.mark = mark;
745             this.undoOffset = undoOffset;
746             this.next = next;
747         }
748         
749         MultiMark mark;
750         
751         int undoOffset;
752         
753         UndoItem next;
754         
755         UndoItem logicalNext;
756
757     }
758     
759     static final class ZeroUndoItem {
760         
761         ZeroUndoItem(MultiMark mark, ZeroUndoItem next) {
762             this.mark = mark;
763             this.next = next;
764         }
765         
766         final MultiMark mark;
767         
768         final ZeroUndoItem next;
769
770     }
771
772 }
773
Popular Tags