001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.util.Map;
017:
018: /**
019: * Document marks enable to store position and line information to simplify
020: * orientation in the document. They are stored in the array with a gap similar
021: * like in {@link javax.swing.text.GapContent}.
022: *
023: * @author Miloslav Metelka
024: * @version 1.00
025: */
026:
027: final class DocMarks {
028:
029: /** Empty array of marks used initially as marks array. */
030: private static final Mark[] EMPTY = new Mark[0];
031:
032: /** Marks array with the gap. */
033: private Mark[] marks;
034:
035: /** Length of the marks array minus one. */
036: private int marksLengthM1;
037:
038: /** Starting index of the gap. */
039: private int gapStart;
040:
041: /** End of the gap (first index after the end of the gap). */
042: private int gapEnd;
043:
044: /** Starting offset of the fictional document's data gap. */
045: private int dataGapStart;
046:
047: /** Ending offset of the fictional document's data gap. */
048: private int dataGapEnd;
049:
050: /**
051: * Number of line separators corresponding to the fictional document's data
052: * gap.
053: */
054: private int dataGapLineCount;
055:
056: /**
057: * Number of marks that are still in the marks array but that have the valid
058: * flag set to false.
059: */
060: int unusedMarksCount;
061:
062: Mark startMark; // for compatibility
063:
064: DocMarks() {
065: marks = EMPTY;
066: marksLengthM1 = -1;
067: dataGapEnd = Integer.MAX_VALUE / 2;
068: dataGapLineCount = Integer.MAX_VALUE / 2;
069:
070: startMark = new Mark(true);
071: insert(startMark);
072: }
073:
074: synchronized int getMarksCount() {
075: return marks.length - (gapEnd - gapStart);
076: }
077:
078: /**
079: * Insert mark.
080: *
081: * @param mark
082: * mark to insert. The mark must have the valid offset and the
083: * line filled in.
084: */
085: synchronized void insert(Mark mark) {
086: int offset = mark.offset;
087: boolean backwardBias = mark.backwardBias;
088:
089: int ind = findInsertIndex(offset, backwardBias);
090:
091: if (gapStart == gapEnd) {
092: increaseAndMoveGap(1, ind);
093: ind = gapStart;
094:
095: } else if (ind != gapStart) {
096: moveGap(ind);
097: ind = gapStart;
098: }
099:
100: if (offset > dataGapStart
101: || (offset == dataGapStart && !backwardBias)) {
102: mark.offset += dataGapEnd - dataGapStart;
103: mark.line += dataGapLineCount;
104: }
105:
106: marks[gapStart++] = mark;
107: mark.valid = true;
108: }
109:
110: synchronized void dispose(Mark mark) {
111: mark.valid = false;
112: unusedMarksCount++;
113:
114: if (unusedMarksCount > Math.max(5, marks.length
115: - (gapEnd - gapStart))) { // GC
116: // rule
117: removeDisposedMarks();
118: }
119: }
120:
121: synchronized void remove(Mark mark) {
122: int ind = findIndex(mark);
123: if (ind < 0) {
124: throw new IllegalStateException("Invalid mark " + mark);
125: }
126:
127: if (ind == gapEnd) {
128: marks[gapEnd++] = null; // enable GC
129:
130: } else if (ind == gapStart - 1) {
131: marks[--gapStart] = null; // enable GC
132:
133: } else { // need to move the gap
134: moveGap(ind + 1);
135: marks[--gapStart] = null; // enable GC
136: }
137:
138: mark.valid = false;
139: }
140:
141: private void moveGap(int ind) {
142: if (ind <= gapStart) { // move gap down
143: int moveSize = gapStart - ind;
144: gapEnd -= moveSize;
145: System.arraycopy(marks, ind, marks, gapEnd, moveSize);
146: gapStart = ind;
147:
148: } else { // above gap
149: int moveSize = ind - gapEnd;
150: System.arraycopy(marks, gapEnd, marks, gapStart, moveSize);
151: gapStart += moveSize;
152: gapEnd += moveSize;
153: }
154: }
155:
156: /**
157: * Increase the size of the gap.
158: *
159: * @param reqSize
160: * the additional size requested. Typically equals to 1.
161: * @param ind
162: * index to which the gap is moved.
163: * @return relocated index in the new marks array that corresponds to the
164: * original index.
165: */
166: private void increaseAndMoveGap(int reqSize, int ind) {
167: int newSize = marks.length * 3 / 2 + reqSize;
168: Mark[] newMarks = new Mark[newSize];
169: marksLengthM1 = newSize - 1;
170: if (ind <= gapStart) {
171: int moveSize = marks.length - gapEnd;
172: if (moveSize > 0) {
173: newSize -= moveSize;
174: System.arraycopy(marks, gapEnd, newMarks, newSize,
175: moveSize);
176: }
177: moveSize = gapStart - ind;
178: if (moveSize > 0) {
179: newSize -= moveSize;
180: System.arraycopy(marks, ind, newMarks, newSize,
181: moveSize);
182: }
183: if (ind > 0) {
184: System.arraycopy(marks, 0, newMarks, 0, ind);
185: }
186: gapStart = ind;
187: gapEnd = newSize;
188:
189: } else { // ind above gap
190: if (gapStart > 0) {
191: System.arraycopy(marks, 0, newMarks, 0, gapStart);
192: }
193: int moveSize = ind - gapEnd;
194: if (moveSize > 0) {
195: System.arraycopy(marks, gapEnd, newMarks, gapStart,
196: moveSize);
197: gapStart += moveSize;
198: }
199: moveSize = marks.length - ind;
200: if (moveSize > 0) {
201: newSize -= moveSize;
202: System.arraycopy(marks, ind, newMarks, newSize,
203: moveSize);
204: }
205: gapEnd = newSize;
206: ind += newMarks.length - marks.length;
207: }
208: marks = newMarks;
209: }
210:
211: /**
212: * Document was modified. This means that the ficitonal data gap must be
213: * updated by the modifiaction. The data gap can possibly be moved and the
214: * marks in the moved area must be updated accordingly.
215: *
216: * @param offset
217: * offset of the modification
218: * @param line
219: * line of modification. It is populated for removals only.
220: * @param length
221: * length of added/removed data. If the length is positive then
222: * the insert occured. If the length is negative then the removal
223: * has occured.
224: * @param lineCount
225: * number of line separators in the added/removed part. It is
226: * <0 for removals.
227: */
228: synchronized void update(int offset, int line, int length,
229: int lineCount) {
230:
231: if (length < 0) { // removal occured
232: offset -= length; // move offset after end of removal (length < 0)
233: }
234:
235: // First move the gap if necessary
236: int dataGapSize = dataGapEnd - dataGapStart;
237: int ind; // index of the first mark with offset over the data gap
238: if (dataGapStart < offset) { // current data gap below offset
239: ind = findInsertIndex(dataGapStart, true);
240:
241: int offsetAfterGap = offset + dataGapSize;
242: int bound = (ind <= gapStart) ? (gapStart - 1)
243: : marksLengthM1;
244: while (true) {
245: while (ind <= bound) {
246: Mark mark = marks[ind];
247: if (mark.offset > offsetAfterGap
248: || (mark.offset == offsetAfterGap && !mark.backwardBias)) {
249: bound = marksLengthM1; // break to shifting of data gap
250: break;
251: }
252:
253: // Move mark before future data gap
254: mark.offset -= dataGapSize;
255: mark.line -= dataGapLineCount;
256:
257: ind++;
258: }
259:
260: if (bound < marksLengthM1) { // shift the bound
261: bound = marksLengthM1;
262: ind = gapEnd;
263:
264: } else { // update gap bounds and break the infinite loop
265: break;
266: }
267: }
268:
269: dataGapEnd += (offset - dataGapStart);
270: dataGapStart = offset;
271:
272: } else if (dataGapStart > offset) { // current data gap over offset
273: ind = findInsertIndex(dataGapStart, true);
274:
275: // Update marks possibly in two passes - above and below gap
276: int bound = (ind >= gapEnd) ? gapEnd : 0; // boundary for the
277: // first pass
278: while (true) { // possibly two passes (above and under gap)
279: while (ind > bound) {
280: Mark mark = marks[--ind];
281: if (mark.offset < offset
282: || (mark.offset == offset && mark.backwardBias)) {
283: bound = 0; // break to shifting of data gap
284: ind++;
285: break;
286: }
287:
288: // Move mark after future data gap
289: mark.offset += dataGapSize;
290: mark.line += dataGapLineCount;
291: }
292:
293: if (bound > 0 && gapStart > 0) {
294: ind = gapStart;
295: bound = 0;
296:
297: } else { // bound == 0
298: break;
299: }
300: }
301:
302: dataGapEnd -= dataGapStart - offset;
303: dataGapStart = offset;
304:
305: } else { // no move of the gap
306: ind = (length < 0) ? findInsertIndex(offset, true) : 0; // index no
307: // important
308: // for
309: // inserts
310: }
311:
312: dataGapStart += length; // shrink the gap
313: // possibly decrease number of lines represented by gap
314: dataGapLineCount -= lineCount;
315:
316: if (length < 0) { // remove performed
317: /*
318: * Marks with backward bias within the whole removed area will be
319: * moved to the offset (before data gap). It is necessary that all
320: * the backwardBias marks within the removed area must be placed
321: * before all the forwardBias marks within the removed area. The
322: * marks with the forward bias are placed after data gap.
323: */
324: int lastBBMInd = -1; // index of last backward bias mark
325: int bound = (ind >= gapEnd) ? gapEnd : 0; // boundary for the
326: // first pass
327: offset += length; // offset back to original value
328: int offsetAfterGap = offset + (dataGapEnd - dataGapStart); // use
329: // current
330: // (updated)
331: // gap
332: // size
333: int lineAfterGap = line + dataGapLineCount;
334:
335: while (true) { // possibly two passes (above and under gap)
336: while (ind > bound) {
337: Mark mark = marks[--ind];
338: if (mark.offset < offset
339: || (mark.offset == offset && mark.backwardBias)) {
340: bound = 0; // break to shifting of data gap
341: break;
342: }
343:
344: // service the potential move because of the biases ordering
345: if (mark.backwardBias) {
346: // Move mark before data gap
347: mark.offset = offset;
348: mark.line = line;
349:
350: if (lastBBMInd < 0) {
351: lastBBMInd = ind;
352: }
353:
354: } else { // forward bias
355: // Move mark after data gap
356: mark.offset = offsetAfterGap;
357: mark.line = lineAfterGap;
358:
359: if (lastBBMInd >= 0) { // backward bias mark(s) exist
360: // exchange this fb mark with last bb mark
361: marks[ind] = marks[lastBBMInd];
362: marks[lastBBMInd] = mark;
363: if (lastBBMInd == gapEnd) {
364: lastBBMInd = gapStart;
365: }
366: lastBBMInd--;
367: }
368: }
369: }
370:
371: if (bound > 0 && gapStart > 0) {
372: ind = gapStart;
373: bound = 0;
374:
375: } else { // bound == 0
376: break;
377: }
378: }
379:
380: }
381: }
382:
383: private void removeDisposedMarks() {
384: int ind = 0;
385: int validInd = 0;
386:
387: while (ind < gapStart) {
388: Mark mark = marks[ind];
389: if (!mark.valid) {
390: mark.removeDisposed();
391:
392: } else {
393: if (ind != validInd) {
394: marks[validInd++] = mark;
395: }
396: }
397: ind++;
398: }
399: gapStart = validInd;
400:
401: ind = marksLengthM1;
402: validInd = ind + 1;
403: while (ind >= gapEnd) {
404: Mark mark = marks[ind];
405: if (!mark.valid) {
406: mark.removeDisposed();
407:
408: } else {
409: if (ind != --validInd) {
410: marks[validInd] = mark;
411: }
412: }
413: ind--;
414: }
415: gapEnd = validInd;
416: }
417:
418: synchronized int getOffset(Mark mark) {
419: int offset = mark.offset;
420: // offset == dataGapStart for backward bias marks
421: // forward bias marks == dataGapEnd instead
422: return (offset <= dataGapStart) ? offset
423: : (offset - (dataGapEnd - dataGapStart));
424: }
425:
426: synchronized int getLine(Mark mark) {
427: // offset == dataGapStart for backward bias marks
428: // forward bias marks == dataGapEnd instead
429: return (mark.offset <= dataGapStart) ? mark.line
430: : (mark.line - dataGapLineCount);
431: }
432:
433: /**
434: * Find the index of the given mark.
435: *
436: * @param mark
437: * for which the index is being searched.
438: * @return index >=0 or <0 if the mark was not found.
439: */
440: int findIndex(Mark mark) {
441: int offset = getOffset(mark);
442: int ind = findInsertIndex(offset, mark.backwardBias);
443: int bound = (ind >= gapEnd) ? gapEnd : 0; // boundary for the first
444: // pass
445: while (true) { // possibly two passes (above and under gap)
446: while (ind > bound) {
447: if (marks[--ind] == mark) {
448: return ind;
449: }
450: }
451:
452: if (bound > 0 && gapStart > 0) {
453: ind = gapStart;
454: bound = 0;
455:
456: } else { // bound == 0
457: break;
458: }
459: }
460: return -1; // not found
461: }
462:
463: /**
464: * Find the index at which it's valid to perform an insert of the new mark.
465: *
466: * @param offset
467: * offset of the mark
468: * @param backwardBias
469: * whether the mark has backward or forward bias.
470: * @return index >= 0 and <=<CODE>marks.length</CODE> in the marks
471: * array where the insert of the mark with the given offset and bias
472: * can be done.<BR>
473: * If there is more marks with the same offset and bias as the
474: * requested ones then the index after these marks is returned. The
475: * gapStart is preferred by the method over the gapEnd
476: * automatically.
477: */
478: int findInsertIndex(int offset, boolean backwardBias) {
479: if (!backwardBias) { // search one offset higher for forward bias
480: offset++;
481: }
482:
483: boolean belowGap = (gapStart > 0 && getOffset(marks[gapStart - 1]) >= offset);
484:
485: // Find the index by using binary search
486: int low;
487: int high;
488:
489: if (belowGap) {
490: low = 0;
491: high = gapStart - 1;
492:
493: } else { // over gap
494: low = gapEnd;
495: high = marksLengthM1;
496: }
497:
498: while (low <= high) {
499: int ind = (low + high) / 2; // mid in the binary search
500: Mark mark = marks[ind];
501: int markOffset = getOffset(mark);
502:
503: if (markOffset < offset) {
504: low = ind + 1;
505:
506: } else if (markOffset > offset) {
507: high = ind - 1;
508:
509: } else { // exact offset found
510: if (!backwardBias) { // forward bias required
511: /*
512: * Searched for offset increased by one so go to begining of
513: * all marks with (offset + 1)
514: */
515: int bound = belowGap ? 0 : gapEnd;
516: while (true) {
517: while (--ind >= bound) {
518: mark = marks[ind];
519: if (getOffset(mark) < offset) {
520: bound = 0;
521: break;
522: }
523: }
524:
525: if (bound > 0) {
526: bound = 0;
527: ind = gapStart;
528:
529: } else {
530: break;
531: }
532: }
533: ind++;
534:
535: } else { // backward bias required
536: if (!mark.backwardBias) { // mark from bin-search has fwd
537: // bias
538: /*
539: * Find (backward) the first mark with lower offset or
540: * same offset but backward bias and go one index back.
541: */
542: int bound = belowGap ? 0 : gapEnd;
543: while (true) {
544: while (--ind >= bound) {
545: mark = marks[ind];
546: if (getOffset(mark) < offset
547: || mark.backwardBias) {
548: bound = 0;
549: break;
550: }
551: }
552:
553: if (bound > 0) {
554: bound = 0;
555: ind = gapStart;
556:
557: } else {
558: break;
559: }
560: }
561: ind++;
562:
563: } else { // the mark from bin-search has bwd bias
564: /*
565: * Goto the end of the marks with the same offset and
566: * backward bias by finding the first mark with the same
567: * offset but forward bias or mark with greater offset.
568: */
569: int bound = belowGap ? (gapStart - 1)
570: : marksLengthM1;
571: while (true) {
572: while (++ind <= bound) {
573: mark = marks[ind];
574: if (getOffset(mark) > offset
575: || !mark.backwardBias) {
576: bound = Integer.MAX_VALUE;
577: break;
578: }
579: }
580:
581: if (bound < marksLengthM1) {
582: bound = marksLengthM1;
583: ind = gapEnd - 1;
584:
585: } else {
586: break;
587: }
588: }
589: }
590: }
591:
592: if (ind == gapEnd) {
593: ind = gapStart; // prefer gapStart over gapEnd
594: }
595: return ind;
596: }
597: }
598:
599: // not exact offset found
600: if (!belowGap && low == gapEnd) {
601: low = gapStart; // prefer gapStart over gapEnd
602: }
603:
604: return low;
605: }
606:
607: /**
608: * Get the mark with offset lower than the parameter.
609: *
610: * @param offset
611: * requested offset
612: * @param markClass
613: * class of the mark to be found. It can be null to accept any
614: * mark.
615: */
616: synchronized Mark getLeftMark(int offset, Class markClass) {
617: Mark ret = null;
618: ;
619: boolean done = false; // to have just one ret stmt
620:
621: if (offset > 0) {
622: int ind = findInsertIndex(offset - 1, false);
623: if (ind > marksLengthM1) {
624: if (gapEnd <= marksLengthM1) {
625: ind--;
626: } else {
627: ind = gapStart;
628: }
629: }
630:
631: if (ind == gapStart) {
632: if (gapStart > 0) {
633: ind--;
634: } else {
635: ret = (markClass == null) ? startMark : null;
636: done = true;
637: }
638:
639: } else { // valid index
640: if (ind == gapEnd) {
641: ind = gapStart;
642: }
643: ind--; // move to startable index
644: if (ind < 0) {
645: ret = (markClass == null) ? startMark : null;
646: done = true;
647: }
648: }
649:
650: if (!done) {
651: int bound = (ind <= gapStart) ? 0 : gapEnd;
652: while (true) {
653: while (ind >= bound) {
654: Mark mark = marks[ind--];
655: if (markClass == null
656: || markClass.isInstance(mark)) {
657: ret = mark;
658: done = true;
659: bound = 0;
660: break;
661: }
662: }
663:
664: if (bound > 0) {
665: bound = 0;
666: ind = gapStart - 1;
667:
668: } else {
669: break;
670: }
671: }
672: }
673: }
674:
675: if (!done) {
676: ret = (markClass == null) ? startMark : null;
677: }
678:
679: // System.err.println("getLeftMark() offset=" + offset + ", markClass="
680: // + markClass + ", found mark=" + ret);
681: // if (ret != null) {
682: // System.err.println(" markOffset=" + getOffset(ret) + ", line=" +
683: // getLine(ret));
684: // }
685: if (ret != null && !ret.valid) {
686: throw new IllegalStateException("Invalid mark");
687: }
688: return ret;
689: }
690:
691: /**
692: * Get mark that is right at given offset or null.
693: *
694: * @param offset
695: * offset where the mark should be found.
696: * @param markClass
697: * class of the mark to be found. It can be null to accept any
698: * mark.
699: */
700: synchronized Mark getOffsetMark(int offset, Class markClass) {
701: int ind = findInsertIndex(offset, false);
702:
703: int bound = (ind <= gapStart) ? 0 : gapEnd;
704: while (true) {
705: while (--ind >= bound) {
706: Mark mark = marks[ind];
707: if (getOffset(mark) != offset) {
708: return null;
709: }
710: if (markClass == null || markClass.isInstance(mark)) {
711: return mark;
712: }
713: }
714:
715: if (bound > 0) {
716: bound = 0;
717: ind = gapStart;
718:
719: } else {
720: break;
721: }
722: }
723:
724: return null;
725: }
726:
727: /**
728: * Render marks by some <CODE>Renderer</CODE>. It is the most efficient
729: * way to handle especially multiple adjacent marks. Rendering function is
730: * called in synchronized manner, so no one will modify mark array while
731: * executing this function.
732: */
733: public synchronized void render(Renderer r) {
734: r.marks = this ;
735: r.render();
736: r.marks = null;
737: }
738:
739: /**
740: * Gets the nearest lower position for specified line. This method can be
741: * used when the only line information is available and the position is
742: * needed (i.e. setting breakpoints, going to line with error etc).
743: *
744: * @param line
745: * line offset for which we want mark
746: * @return mark with lower or equal line. Caution! When the caller gets the
747: * mark and it usually tries to get position of returned mark.
748: * However the mark can be removed meantime and call <CODE>getOffset()</CODE>
749: * will throw <CODE>InvalidMarkException</CODE>. In that case
750: * caller should call <CODE>getMarkFromLine()</CODE> again to get
751: * another mark and retry.
752: */
753: synchronized Mark getMarkFromLine(int line) {
754: boolean aboveGap = (gapEnd <= marksLengthM1 && getLine(marks[gapEnd]) <= line);
755:
756: // Find the index by using binary search
757: int low;
758: int high;
759:
760: if (!aboveGap) {
761: low = 0;
762: high = gapStart - 1;
763:
764: } else { // over gap
765: low = gapEnd;
766: high = marksLengthM1;
767: }
768:
769: while (low <= high) {
770: int ind = (low + high) / 2; // mid in the binary search
771: Mark mark = marks[ind];
772: int markLine = getLine(mark);
773:
774: if (markLine < line) {
775: low = ind + 1;
776:
777: } else if (markLine > line) {
778: high = ind - 1;
779:
780: } else { // exact line found
781: return mark;
782: }
783: }
784:
785: // mark with exact line not found
786: return (high >= 0) ? marks[high] : startMark;
787: }
788:
789: /**
790: * More efficient way of handling marks especially if there is a need to
791: * work with more than one mark at the moment.
792: */
793: static abstract class Renderer {
794:
795: DocMarks marks;
796:
797: Renderer() {
798: }
799:
800: DocMarks getMarks() {
801: return marks;
802: }
803:
804: /** Create array of all marks */
805: Mark[] copyAllMarks() {
806: Mark[] ret = new Mark[marks.getMarksCount()];
807: System.arraycopy(marks.marks, 0, ret, 0, marks.gapStart);
808: System.arraycopy(marks.marks, marks.gapEnd, ret,
809: marks.gapStart, marks.marks.length - marks.gapEnd);
810: return ret;
811: }
812:
813: Mark[] getMarkArray() {
814: return marks.marks;
815: }
816:
817: int getMarkArrayLength() {
818: return marks.marks.length;
819: }
820:
821: int getMarkIndex(Mark mark) {
822: if (!mark.valid) {
823: throw new IllegalStateException();
824: }
825: return marks.findIndex(mark);
826: }
827:
828: int getMarkOffset(Mark mark) {
829: if (!mark.valid) {
830: throw new IllegalStateException();
831: }
832: return marks.getOffset(mark);
833: }
834:
835: int getNextIndex(int index) {
836: if (++index == marks.gapStart) {
837: index = marks.gapEnd;
838: }
839: return index;
840: }
841:
842: abstract void render();
843:
844: }
845:
846: /**
847: * Check whether the marks offsets and lines are sorted correctly.
848: */
849: void check() {
850: int ind = 0;
851: int bound = gapStart - 1;
852: int lastOffset = 0;
853: int lastLine = 0;
854: boolean lastBackwardBias = true;
855:
856: while (true) {
857: while (ind <= bound) {
858: Mark mark = marks[ind++];
859:
860: if (mark.offset < lastOffset
861: || (mark.offset == lastOffset && (mark.backwardBias && !lastBackwardBias))) {
862: consistencyError(true, ind - 1); // offset error
863: }
864:
865: if (mark.line < lastLine) {
866: consistencyError(false, ind - 1);
867: }
868:
869: lastOffset = mark.offset;
870: lastLine = mark.line;
871: lastBackwardBias = mark.backwardBias;
872:
873: }
874:
875: if (bound < marksLengthM1) { // shift the bound
876: bound = marksLengthM1;
877: ind = gapEnd;
878:
879: } else { // update gap bounds and break the infinite loop
880: break;
881: }
882: }
883: }
884:
885: private void consistencyError(boolean offsetError, int ind) {
886: throw new IllegalStateException("DocMarks.check(): "
887: + (offsetError ? "Offset" : "Line")
888: + " inconsistency found at ind=" + ind + ", mark="
889: + marks[ind]);
890: }
891:
892: /** List all the marks into string. */
893: public String toStringDetail() {
894: return toStringDetail(null);
895: }
896:
897: String toStringDetail(Map testMarksMap) {
898: StringBuffer sb = new StringBuffer(toString());
899: sb.append('\n');
900:
901: boolean beforeGap = true;
902: while (true) {
903: int i = beforeGap ? 0 : gapEnd;
904: int bound = beforeGap ? gapStart : marks.length;
905: sb.append("Marks ");
906: sb.append(beforeGap ? "before" : "after");
907: sb.append(" gap:\n");
908:
909: while (i < bound) {
910: Mark mark = marks[i];
911: Mark testMark = null;
912: if (testMarksMap != null) {
913: testMark = (Mark) testMarksMap.get(mark);
914: if (testMark == null) {
915: throw new IllegalStateException(
916: "No test mark for mark=" + mark);
917: }
918: }
919:
920: try {
921: sb.append("[");
922: sb.append(i);
923: sb.append("]: (");
924: sb.append(mark.offset);
925: sb.append(", ");
926: sb.append(mark.line);
927: sb.append(", ");
928: sb.append(mark.backwardBias ? 'B' : 'F');
929: sb.append(") -> (");
930: sb.append(mark.getOffset());
931: sb.append(", ");
932: sb.append(mark.getLine());
933: sb.append(')');
934:
935: if (testMark != null) {
936: sb.append(" testMark: (");
937: sb.append(testMark.offset);
938: sb.append(", ");
939: sb.append(testMark.line);
940: sb.append(", ");
941: sb.append(testMark.backwardBias ? 'B' : 'F');
942: sb.append(')');
943: }
944:
945: sb.append('\n');
946:
947: } catch (InvalidMarkException e) {
948: e.printStackTrace();
949: throw new IllegalStateException();
950: }
951:
952: i++;
953: }
954:
955: beforeGap = !beforeGap;
956: if (beforeGap) {
957: break;
958: }
959: }
960: return sb.toString();
961: }
962:
963: /** Get info about <CODE>DocMarks</CODE>. */
964: public String toString() {
965: return "marksCount=" + getMarksCount() + ", gapStart="
966: + gapStart + ", gapEnd=" + gapEnd + ", dataLen="
967: + (Integer.MAX_VALUE - (dataGapEnd - dataGapStart))
968: + ", dataGapStart=" + dataGapStart + ", dataGapEnd="
969: + dataGapEnd + ", dataGapLineCount=" + dataGapLineCount;
970: }
971:
972: }
|