1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51:
52:
61: public class GapContent
62: implements AbstractDocument.Content, Serializable
63: {
64:
65:
68: private class GapContentPosition
69: implements Position
70: {
71:
72:
76: int index;
77:
78:
83: GapContentPosition(int mark)
84: {
85:
86:
87: synchronized (GapContent.this)
88: {
89: int i = binarySearch(positionMarks, mark, numMarks);
90: if (i >= 0)
91: {
92: index = i;
93: }
94: else
95: {
96: index = -i - 1;
97: insertMark(index, mark);
98: }
99: }
100: }
101:
102:
107: public int getOffset()
108: {
109: synchronized (GapContent.this)
110: {
111:
112: int mark = positionMarks[index];
113:
114: assert mark <= gapStart || mark >= gapEnd : "mark: " + mark
115: + ", gapStart: " + gapStart
116: + ", gapEnd: " + gapEnd;
117: int res = mark;
118: if (mark > gapStart)
119: res -= (gapEnd - gapStart);
120: return res;
121: }
122: }
123: }
124:
125: private class InsertUndo extends AbstractUndoableEdit
126: {
127: public int where, length;
128: String text;
129: public InsertUndo(int start, int len)
130: {
131: where = start;
132: length = len;
133: }
134:
135: public void undo () throws CannotUndoException
136: {
137: super.undo();
138: try
139: {
140: text = getString(where, length);
141: remove(where, length);
142: }
143: catch (BadLocationException ble)
144: {
145: throw new CannotUndoException();
146: }
147: }
148:
149: public void redo () throws CannotUndoException
150: {
151: super.redo();
152: try
153: {
154: insertString(where, text);
155: }
156: catch (BadLocationException ble)
157: {
158: throw new CannotRedoException();
159: }
160: }
161:
162: }
163:
164: private class UndoRemove extends AbstractUndoableEdit
165: {
166: public int where;
167: String text;
168: public UndoRemove(int start, String removedText)
169: {
170: where = start;
171: text = removedText;
172: }
173:
174: public void undo () throws CannotUndoException
175: {
176: super.undo();
177: try
178: {
179: insertString(where, text);
180: }
181: catch (BadLocationException ble)
182: {
183: throw new CannotUndoException();
184: }
185: }
186:
187: public void redo () throws CannotUndoException
188: {
189: super.redo();
190: try
191: {
192: remove(where, text.length());
193: }
194: catch (BadLocationException ble)
195: {
196: throw new CannotRedoException();
197: }
198: }
199:
200: }
201:
202:
203: private static final long serialVersionUID = -6226052713477823730L;
204:
205:
209: static final int DEFAULT_BUFSIZE = 10;
210:
211:
214: char[] buffer;
215:
216:
219: int gapStart;
220:
221:
224: int gapEnd;
225:
226:
227:
228:
229:
230:
234: int[] positionMarks;
235:
236:
240: int numMarks;
241:
242:
245: WeakHashMap positions;
246:
247:
250: public GapContent()
251: {
252: this(DEFAULT_BUFSIZE);
253: }
254:
255:
260: public GapContent(int size)
261: {
262: size = Math.max(size, 2);
263: buffer = (char[]) allocateArray(size);
264: gapStart = 1;
265: gapEnd = size;
266: buffer[0] = '\n';
267: positions = new WeakHashMap();
268: positionMarks = new int[10];
269: numMarks = 0;
270: }
271:
272:
280: protected Object allocateArray(int size)
281: {
282: return new char[size];
283: }
284:
285:
290: protected int getArrayLength()
291: {
292: return buffer.length;
293: }
294:
295:
300: public int length()
301: {
302: return buffer.length - (gapEnd - gapStart);
303: }
304:
305:
316: public UndoableEdit insertString(int where, String str)
317: throws BadLocationException
318: {
319:
320: int length = length();
321: int strLen = str.length();
322:
323: if (where < 0)
324: throw new BadLocationException("The where argument cannot be smaller"
325: + " than the zero", where);
326:
327: if (where > length)
328: throw new BadLocationException("The where argument cannot be greater"
329: + " than the content length", where);
330:
331: replace(where, 0, str.toCharArray(), strLen);
332:
333: return new InsertUndo(where, strLen);
334: }
335:
336:
347: public UndoableEdit remove(int where, int nitems) throws BadLocationException
348: {
349:
350: int length = length();
351:
352: if ((where + nitems) >= length)
353: throw new BadLocationException("where + nitems cannot be greater"
354: + " than the content length", where + nitems);
355:
356: String removedText = getString(where, nitems);
357: replace(where, nitems, null, 0);
358:
359: return new UndoRemove(where, removedText);
360: }
361:
362:
371: public String getString(int where, int len) throws BadLocationException
372: {
373: Segment seg = new Segment();
374: try
375: {
376: getChars(where, len, seg);
377: return new String(seg.array, seg.offset, seg.count);
378: }
379: catch (StringIndexOutOfBoundsException ex)
380: {
381: int invalid = 0;
382: if (seg.offset < 0 || seg.offset >= seg.array.length)
383: invalid = seg.offset;
384: else
385: invalid = seg.offset + seg.count;
386: throw new BadLocationException("Illegal location: array.length = "
387: + seg.array.length + ", offset = "
388: + seg.offset + ", count = "
389: + seg.count, invalid);
390: }
391: }
392:
393:
407: public void getChars(int where, int len, Segment txt)
408: throws BadLocationException
409: {
410:
411: int length = length();
412: if (where < 0)
413: throw new BadLocationException("the where argument may not be below zero", where);
414: if (where >= length)
415: throw new BadLocationException("the where argument cannot be greater"
416: + " than the content length", where);
417: if ((where + len) > length)
418: throw new BadLocationException("len plus where cannot be greater"
419: + " than the content length", len + where);
420:
421:
422: if ((where < gapStart) && ((gapStart - where) < len))
423: {
424:
425: char[] copy = new char[len];
426: int lenFirst = gapStart - where;
427: System.arraycopy(buffer, where, copy, 0, lenFirst);
428: System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst);
429: txt.array = copy;
430: txt.offset = 0;
431: txt.count = len;
432: }
433: else
434: {
435:
436:
437: txt.array = buffer;
438: if (where < gapStart)
439: txt.offset = where;
440: else
441: txt.offset = where + (gapEnd - gapStart);
442: txt.count = len;
443: }
444: }
445:
446:
456: public Position createPosition(final int offset) throws BadLocationException
457: {
458:
459:
460: GapContentPosition pos = null;
461: Set positionSet = positions.keySet();
462: for (Iterator i = positionSet.iterator(); i.hasNext();)
463: {
464: GapContentPosition p = (GapContentPosition) i.next();
465: if (p.getOffset() == offset)
466: {
467: pos = p;
468: break;
469: }
470: }
471:
472:
473: if (pos == null)
474: {
475: int mark = offset;
476: if (mark >= gapStart)
477: mark += (gapEnd - gapStart);
478: pos = new GapContentPosition(mark);
479: positions.put(pos, null);
480: }
481:
482: return pos;
483: }
484:
485:
493: protected void shiftEnd(int newSize)
494: {
495: assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
496: + "than the old gap size";
497:
498: int delta = newSize - gapEnd + gapStart;
499:
500: adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta);
501:
502:
503: char[] newBuf = (char[]) allocateArray(length() + newSize);
504: System.arraycopy(buffer, 0, newBuf, 0, gapStart);
505: System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
506: - gapEnd);
507: gapEnd = gapStart + newSize;
508: buffer = newBuf;
509:
510: }
511:
512:
517: protected void shiftGap(int newGapStart)
518: {
519: if (newGapStart == gapStart)
520: return;
521: int newGapEnd = newGapStart + gapEnd - gapStart;
522: if (newGapStart < gapStart)
523: {
524:
525:
526: adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart);
527: System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
528: - newGapStart);
529: gapStart = newGapStart;
530: gapEnd = newGapEnd;
531: }
532: else
533: {
534:
535:
536: adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart));
537: System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
538: - gapStart);
539: gapStart = newGapStart;
540: gapEnd = newGapEnd;
541: }
542: if (gapStart == 0)
543: resetMarksAtZero();
544: }
545:
546:
554: protected void shiftGapStartDown(int newGapStart)
555: {
556: if (newGapStart == gapStart)
557: return;
558:
559: assert newGapStart < gapStart : "The new gap start must be less than the "
560: + "old gap start.";
561: setPositionsInRange(newGapStart, gapStart, false);
562: gapStart = newGapStart;
563: }
564:
565:
573: protected void shiftGapEndUp(int newGapEnd)
574: {
575: if (newGapEnd == gapEnd)
576: return;
577:
578: assert newGapEnd > gapEnd : "The new gap end must be greater than the "
579: + "old gap end.";
580: setPositionsInRange(gapEnd, newGapEnd, false);
581: gapEnd = newGapEnd;
582: }
583:
584:
589: protected final Object getArray()
590: {
591: return buffer;
592: }
593:
594:
602: protected void replace(int position, int rmSize, Object addItems,
603: int addSize)
604: {
605: if (gapStart != position)
606: shiftGap(position);
607:
608:
609: if (rmSize > 0)
610: shiftGapEndUp(gapEnd + rmSize);
611:
612:
613: if ((gapEnd - gapStart) <= addSize)
614: shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE);
615:
616:
617: if (addItems != null)
618: {
619: System.arraycopy(addItems, 0, buffer, gapStart, addSize);
620:
621:
622: resetMarksAtZero();
623:
624: gapStart += addSize;
625: }
626: }
627:
628:
633: protected final int getGapStart()
634: {
635: return gapStart;
636: }
637:
638:
643: protected final int getGapEnd()
644: {
645: return gapEnd;
646: }
647:
648:
658: protected Vector getPositionsInRange(Vector v, int offset, int length)
659: {
660: Vector res = v;
661: if (res == null)
662: res = new Vector();
663: else
664: res.clear();
665:
666: int endOffs = offset + length;
667:
668: Set positionSet = positions.keySet();
669: for (Iterator i = positionSet.iterator(); i.hasNext();)
670: {
671: GapContentPosition p = (GapContentPosition) i.next();
672: int offs = p.getOffset();
673: if (offs >= offset && offs < endOffs)
674: res.add(p);
675: }
676:
677: return res;
678: }
679:
680:
690: private void setPositionsInRange(int start, int end, boolean toStart)
691: {
692:
693:
694:
695:
696:
697:
698:
699: synchronized (this)
700: {
701: int startIndex = binarySearch(positionMarks, start, numMarks);
702: if (startIndex < 0)
703: startIndex = - startIndex - 1;
704: int endIndex = binarySearch(positionMarks, end, numMarks);
705: if (endIndex < 0)
706: endIndex = - endIndex - 2;
707:
708:
709:
710:
711: int removed = endIndex - startIndex;
712: if (removed <= 0)
713: return;
714: System.arraycopy(positionMarks, endIndex + 1, positionMarks,
715: startIndex + 1, positionMarks.length - endIndex - 1);
716: numMarks -= removed;
717: if (toStart)
718: {
719: positionMarks[startIndex] = start;
720: }
721: else
722: {
723: positionMarks[startIndex] = end;
724: }
725:
726:
727:
728:
729: Set positionSet = positions.keySet();
730: for (Iterator i = positionSet.iterator(); i.hasNext();)
731: {
732: GapContentPosition p = (GapContentPosition) i.next();
733: if (p.index > startIndex || p.index <= endIndex)
734: p.index = startIndex;
735: else if (p.index > endIndex)
736: p.index -= removed;
737: }
738: }
739: }
740:
741:
750: private void adjustPositionsInRange(int offset, int length, int incr)
751: {
752: int endMark = offset + length;
753:
754: synchronized (this)
755: {
756:
757: int startIndex = binarySearch(positionMarks, offset, numMarks);
758: if (startIndex < 0)
759: startIndex = - startIndex - 1;
760: int endIndex = binarySearch(positionMarks, endMark, numMarks);
761: if (endIndex < 0)
762: endIndex = - endIndex - 2;
763:
764:
765:
766: assert (startIndex <= 0
767: || positionMarks[startIndex - 1]
768: <= positionMarks [startIndex] + incr)
769: && (endIndex >= numMarks - 1
770: || positionMarks[endIndex + 1]
771: >= positionMarks[endIndex] + incr)
772: : "Adjusting the marks must not change their order";
773:
774:
775:
776: if (startIndex > 0 && positionMarks[startIndex - 1]
777: == positionMarks[startIndex] + incr)
778: System.err.println("DEBUG: We could coalesce the start of the region"
779: + " in GapContent.adjustPositionsInRange()");
780: if (endIndex < numMarks - 1 && positionMarks[endIndex + 1]
781: == positionMarks[endIndex] + incr)
782: System.err.println("DEBUG: We could coalesce the end of the region"
783: + " in GapContent.adjustPositionsInRange()");
784:
785:
786: for (int i = startIndex; i <= endIndex; i++)
787: positionMarks[i] += incr;
788: }
789:
790: }
791:
792:
798: protected void resetMarksAtZero()
799: {
800: if (gapStart != 0)
801: return;
802:
803: positionMarks[0] = 0;
804: }
805:
806:
812: protected void updateUndoPositions(Vector positions, int offset, int length)
813: {
814:
815: }
816:
817:
822: private void dump()
823: {
824: System.err.println("GapContent debug information");
825: System.err.println("buffer length: " + buffer.length);
826: System.err.println("gap start: " + gapStart);
827: System.err.println("gap end: " + gapEnd);
828: for (int i = 0; i < buffer.length; i++)
829: {
830: if (i == gapStart)
831: System.err.print('<');
832: if (i == gapEnd)
833: System.err.print('>');
834:
835: if (!Character.isISOControl(buffer[i]))
836: System.err.print(buffer[i]);
837: else
838: System.err.print('.');
839: }
840: System.err.println();
841: }
842:
843:
846: private void dumpMarks()
847: {
848: System.err.print("positionMarks: ");
849: for (int i = 0; i < numMarks; i++)
850: System.err.print(positionMarks[i] + ", ");
851: System.err.println();
852: }
853:
854:
863: void insertMark(int insertionPoint, int mark)
864: {
865: synchronized (this)
866: {
867:
868: Set positionSet = positions.keySet();
869: for (Iterator i = positionSet.iterator(); i.hasNext();)
870: {
871: GapContentPosition p = (GapContentPosition) i.next();
872: if (p.index >= insertionPoint)
873: p.index++;
874: }
875:
876:
877: if (positionMarks.length <= numMarks)
878: {
879: int[] newMarks = new int[positionMarks.length + 10];
880: System.arraycopy(positionMarks, 0, newMarks, 0, insertionPoint);
881: newMarks[insertionPoint] = mark;
882: System.arraycopy(positionMarks, insertionPoint, newMarks,
883: insertionPoint + 1,
884: numMarks - insertionPoint);
885: positionMarks = newMarks;
886: }
887: else
888: {
889: System.arraycopy(positionMarks, insertionPoint, positionMarks,
890: insertionPoint + 1,
891: numMarks - insertionPoint);
892: positionMarks[insertionPoint] = mark;
893: }
894: numMarks++;
895: }
896: }
897:
898:
914: int binarySearch(int[] a, int key, int maxIndex)
915: {
916: int low = 0;
917: int hi = maxIndex - 1;
918: int mid = 0;
919: while (low <= hi)
920: {
921: mid = (low + hi) >>> 1;
922: final int d = a[mid];
923: if (d == key)
924: return mid;
925: else if (d > key)
926: hi = mid - 1;
927: else
928:
929: low = ++mid;
930: }
931: return -mid - 1;
932: }
933: }