1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49:
50: import ;
51: import ;
52: import ;
53: import ;
54:
55:
64: public class GapContent
65: implements AbstractDocument.Content, Serializable
66: {
67:
68:
71: class GapContentPosition
72: implements Position
73: {
74:
75:
79: Mark mark;
80:
81:
86: public int getOffset()
87: {
88: return mark.getOffset();
89: }
90: }
91:
92:
99: private class Mark
100: extends WeakReference
101: {
102:
105: int mark;
106:
107:
112: Mark(int offset)
113: {
114: super(null);
115: mark = offset;
116: }
117:
118: Mark(int offset, GapContentPosition pos, ReferenceQueue queue)
119: {
120: super(pos, queue);
121: mark = offset;
122: }
123:
124:
129: int getOffset()
130: {
131: int res = mark;
132: if (mark >= gapStart)
133: res -= (gapEnd - gapStart);
134: return Math.max(0, res);
135: }
136:
137:
143: GapContentPosition getPosition()
144: {
145: return (GapContentPosition) get();
146: }
147:
148: }
149:
150:
154: private class UndoPosRef
155: {
156:
159: private Mark mark;
160:
161:
164: private int undoOffset;
165:
166:
171: UndoPosRef(Mark m)
172: {
173: mark = m;
174: undoOffset = mark.getOffset();
175: }
176:
177:
181: void reset()
182: {
183: if (undoOffset <= gapStart)
184: mark.mark = undoOffset;
185: else
186: mark.mark = (gapEnd - gapStart) + undoOffset;
187: }
188: }
189:
190: private class InsertUndo extends AbstractUndoableEdit
191: {
192: public int where, length;
193: String text;
194: private Vector positions;
195:
196: public InsertUndo(int start, int len)
197: {
198: where = start;
199: length = len;
200: }
201:
202: public void undo () throws CannotUndoException
203: {
204: super.undo();
205: try
206: {
207: positions = getPositionsInRange(null, where, length);
208: text = getString(where, length);
209: remove(where, length);
210: }
211: catch (BadLocationException ble)
212: {
213: throw new CannotUndoException();
214: }
215: }
216:
217: public void redo () throws CannotUndoException
218: {
219: super.redo();
220: try
221: {
222: insertString(where, text);
223: if (positions != null)
224: {
225: updateUndoPositions(positions, where, length);
226: positions = null;
227: }
228: }
229: catch (BadLocationException ble)
230: {
231: throw new CannotRedoException();
232: }
233: }
234:
235: }
236:
237: private class UndoRemove extends AbstractUndoableEdit
238: {
239: public int where;
240: String text;
241:
242:
245: private Vector positions;
246:
247: public UndoRemove(int start, String removedText)
248: {
249: where = start;
250: text = removedText;
251: positions = getPositionsInRange(null, start, removedText.length());
252: }
253:
254: public void undo () throws CannotUndoException
255: {
256: super.undo();
257: try
258: {
259: insertString(where, text);
260: if (positions != null)
261: updateUndoPositions(positions, where, text.length());
262: }
263: catch (BadLocationException ble)
264: {
265: throw new CannotUndoException();
266: }
267: }
268:
269: public void redo () throws CannotUndoException
270: {
271: super.redo();
272: try
273: {
274: text = getString(where, text.length());
275: positions = getPositionsInRange(null, where, text.length());
276: remove(where, text.length());
277: }
278: catch (BadLocationException ble)
279: {
280: throw new CannotRedoException();
281: }
282: }
283:
284: }
285:
286:
287: private static final long serialVersionUID = -6226052713477823730L;
288:
289:
293: static final int DEFAULT_BUFSIZE = 10;
294:
295:
298: char[] buffer;
299:
300:
303: int gapStart;
304:
305:
308: int gapEnd;
309:
310:
311:
312:
313:
314:
320: ArrayList marks;
321:
322:
325: private int garbageMarks;
326:
327:
330: private Mark searchMark = new Mark(0);
331:
332:
339: ReferenceQueue queueOfDeath;
340:
341:
344: public GapContent()
345: {
346: this(DEFAULT_BUFSIZE);
347: }
348:
349:
354: public GapContent(int size)
355: {
356: size = Math.max(size, 2);
357: buffer = (char[]) allocateArray(size);
358: gapStart = 1;
359: gapEnd = size;
360: buffer[0] = '\n';
361: marks = new ArrayList();
362: queueOfDeath = new ReferenceQueue();
363: }
364:
365:
373: protected Object allocateArray(int size)
374: {
375: return new char[size];
376: }
377:
378:
383: protected int getArrayLength()
384: {
385: return buffer.length;
386: }
387:
388:
393: public int length()
394: {
395: return buffer.length - (gapEnd - gapStart);
396: }
397:
398:
409: public UndoableEdit insertString(int where, String str)
410: throws BadLocationException
411: {
412:
413: int length = length();
414: int strLen = str.length();
415:
416: if (where < 0)
417: throw new BadLocationException("The where argument cannot be smaller"
418: + " than the zero", where);
419:
420: if (where > length)
421: throw new BadLocationException("The where argument cannot be greater"
422: + " than the content length", where);
423:
424: InsertUndo undo = new InsertUndo(where, strLen);
425: replace(where, 0, str.toCharArray(), strLen);
426:
427: return undo;
428: }
429:
430:
441: public UndoableEdit remove(int where, int nitems) throws BadLocationException
442: {
443:
444: int length = length();
445:
446: if ((where + nitems) >= length)
447: throw new BadLocationException("where + nitems cannot be greater"
448: + " than the content length", where + nitems);
449:
450: String removedText = getString(where, nitems);
451: UndoRemove undoRemove = new UndoRemove(where, removedText);
452: replace(where, nitems, null, 0);
453:
454: return undoRemove;
455: }
456:
457:
466: public String getString(int where, int len) throws BadLocationException
467: {
468: Segment seg = new Segment();
469: try
470: {
471: getChars(where, len, seg);
472: return new String(seg.array, seg.offset, seg.count);
473: }
474: catch (StringIndexOutOfBoundsException ex)
475: {
476: int invalid = 0;
477: if (seg.offset < 0 || seg.offset >= seg.array.length)
478: invalid = seg.offset;
479: else
480: invalid = seg.offset + seg.count;
481: throw new BadLocationException("Illegal location: array.length = "
482: + seg.array.length + ", offset = "
483: + seg.offset + ", count = "
484: + seg.count, invalid);
485: }
486: }
487:
488:
502: public void getChars(int where, int len, Segment txt)
503: throws BadLocationException
504: {
505:
506: int length = length();
507: if (where < 0)
508: throw new BadLocationException("the where argument may not be below zero", where);
509: if (where >= length)
510: throw new BadLocationException("the where argument cannot be greater"
511: + " than the content length", where);
512: if ((where + len) > length)
513: throw new BadLocationException("len plus where cannot be greater"
514: + " than the content length", len + where);
515: if (len < 0)
516: throw new BadLocationException("negative length not allowed: ", len);
517:
518:
519: if (where + len <= gapStart)
520: {
521:
522: txt.array = buffer;
523: txt.offset = where;
524: txt.count = len;
525: }
526: else if (where > gapStart)
527: {
528:
529: txt.array = buffer;
530: txt.offset = gapEnd + where - gapStart;
531: txt.count = len;
532: }
533: else
534: {
535:
536: int beforeGap = gapStart - where;
537: if (txt.isPartialReturn())
538: {
539:
540: txt.array = buffer;
541: txt.offset = where;
542: txt.count = beforeGap;
543: }
544: else
545: {
546:
547: txt.array = new char[len];
548: txt.offset = 0;
549: System.arraycopy(buffer, where, txt.array, 0, beforeGap);
550: System.arraycopy(buffer, gapEnd, txt.array, beforeGap,
551: len - beforeGap);
552: txt.count = len;
553: }
554: }
555: }
556:
557:
567: public Position createPosition(final int offset) throws BadLocationException
568: {
569:
570:
571:
572:
573:
574:
575: while (queueOfDeath.poll() != null)
576: garbageMarks++;
577: if (garbageMarks > Math.max(5, marks.size() / 10))
578: garbageCollect();
579:
580:
581:
582: Mark m;
583: GapContentPosition pos;
584: int index = offset;
585: if (offset >= gapStart)
586: index += (gapEnd - gapStart);
587: searchMark.mark = index;
588: int insertIndex = search(searchMark);
589: if (!(insertIndex < marks.size()
590: && (m = (Mark) marks.get(insertIndex)).mark == index
591: && (pos = m.getPosition()) != null))
592: {
593:
594: pos = new GapContentPosition();
595: m = new Mark(index, pos, queueOfDeath);
596: pos.mark = m;
597: marks.add(insertIndex, m);
598: }
599:
600:
601: return pos;
602: }
603:
604:
612: protected void shiftEnd(int newSize)
613: {
614: assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
615: + "than the old gap size";
616:
617: int oldEnd = getGapEnd();
618: int oldSize = getArrayLength();
619: int upper = oldSize - oldEnd;
620: int size = (newSize + 1) * 2;
621: int newEnd = size - upper;
622:
623:
624: char[] newBuf = (char[]) allocateArray(size);
625: System.arraycopy(buffer, 0, newBuf, 0, Math.min(size, oldSize));
626: buffer = newBuf;
627: gapEnd = newEnd;
628: if (upper != 0)
629: System.arraycopy(buffer, oldEnd, buffer, newEnd, upper);
630:
631:
632: int delta = gapEnd - oldEnd;
633: int adjIndex = searchFirst(oldEnd);
634: int count = marks.size();
635: for (int i = adjIndex; i < count; i++)
636: {
637: Mark m = (Mark) marks.get(i);
638: m.mark += delta;
639: }
640: }
641:
642:
647: protected void shiftGap(int newGapStart)
648: {
649: int oldStart = gapStart;
650: int delta = newGapStart - oldStart;
651: int oldEnd = gapEnd;
652: int newGapEnd = oldEnd + delta;
653: int size = oldEnd - oldStart;
654:
655:
656: gapStart = newGapStart;
657: gapEnd = newGapEnd;
658: if (delta > 0)
659: System.arraycopy(buffer, oldEnd, buffer, oldStart, delta);
660: else
661: System.arraycopy(buffer, newGapStart, buffer, newGapEnd, -delta);
662:
663:
664: if (delta > 0)
665: {
666: int adjIndex = searchFirst(oldStart);
667: int count = marks.size();
668: for (int i = adjIndex; i < count; i++)
669: {
670: Mark m = (Mark) marks.get(i);
671: if (m.mark >= newGapEnd)
672: break;
673: m.mark -= size;
674: }
675: }
676: else if (delta < 0)
677: {
678: int adjIndex = searchFirst(newGapStart);
679: int count = marks.size();
680: for (int i = adjIndex; i < count; i++)
681: {
682: Mark m = (Mark) marks.get(i);
683: if (m.mark >= oldEnd)
684: break;
685: m.mark += size;
686: }
687: }
688: resetMarksAtZero();
689: }
690:
691:
699: protected void shiftGapStartDown(int newGapStart)
700: {
701: if (newGapStart == gapStart)
702: return;
703:
704: assert newGapStart < gapStart : "The new gap start must be less than the "
705: + "old gap start.";
706:
707:
708: int adjIndex = searchFirst(newGapStart);
709: int count = marks.size();
710: for (int i = adjIndex; i < count; i++)
711: {
712: Mark m = (Mark) marks.get(i);
713: if (m.mark > gapStart)
714: break;
715: m.mark = gapEnd;
716: }
717:
718: gapStart = newGapStart;
719: resetMarksAtZero();
720: }
721:
722:
730: protected void shiftGapEndUp(int newGapEnd)
731: {
732: if (newGapEnd == gapEnd)
733: return;
734:
735: assert newGapEnd > gapEnd : "The new gap end must be greater than the "
736: + "old gap end.";
737:
738:
739: int adjIndex = searchFirst(gapEnd);
740: int count = marks.size();
741: for (int i = adjIndex; i < count; i++)
742: {
743: Mark m = (Mark) marks.get(i);
744: if (m.mark >= newGapEnd)
745: break;
746: m.mark = newGapEnd;
747: }
748:
749:
750: gapEnd = newGapEnd;
751: resetMarksAtZero();
752: }
753:
754:
759: protected final Object getArray()
760: {
761: return buffer;
762: }
763:
764:
772: protected void replace(int position, int rmSize, Object addItems,
773: int addSize)
774: {
775: if (addSize == 0)
776: {
777: removeImpl(position, rmSize);
778: return;
779: }
780: else if (rmSize > addSize)
781: {
782: removeImpl(position + addSize, rmSize - addSize);
783: }
784: else
785: {
786: int endSize = addSize - rmSize;
787: int end = addImpl(position + rmSize, endSize);
788: System.arraycopy(addItems, rmSize, buffer, end, endSize);
789: addSize = rmSize;
790: }
791: System.arraycopy(addItems, 0, buffer, position, addSize);
792: }
793:
794:
800: private void removeImpl(int pos, int num)
801: {
802: if (num > 0)
803: {
804: int end = pos + num;
805: int newGapSize = (gapEnd - gapStart) + num;
806: if (end <= gapStart)
807: {
808: if (gapStart != end)
809: {
810: shiftGap(end);
811: }
812: shiftGapStartDown(gapStart - num);
813: }
814: else if (pos >= gapStart)
815: {
816: if (gapStart != pos)
817: {
818: shiftGap(pos);
819: }
820: shiftGapEndUp(gapStart + newGapSize);
821: }
822: else
823: {
824: shiftGapStartDown(pos);
825: shiftGapEndUp(gapStart + newGapSize);
826: }
827: }
828: }
829:
830:
838: private int addImpl(int pos, int num)
839: {
840: int size = gapEnd - gapStart;
841: if (num == 0)
842: {
843: if (pos > gapStart)
844: pos += size;
845: return pos;
846: }
847:
848: shiftGap(pos);
849: if (num >= size)
850: {
851: shiftEnd(getArrayLength() - size + num);
852: size = gapEnd - gapStart;
853: }
854:
855: gapStart += num;
856: return pos;
857: }
858:
859:
864: protected final int getGapStart()
865: {
866: return gapStart;
867: }
868:
869:
874: protected final int getGapEnd()
875: {
876: return gapEnd;
877: }
878:
879:
889: protected Vector getPositionsInRange(Vector v, int offset, int length)
890: {
891: int end = offset + length;
892: int startIndex;
893: int endIndex;
894: if (offset < gapStart)
895: {
896: if (offset == 0)
897: startIndex = 0;
898: else
899: startIndex = searchFirst(offset);
900: if (end >= gapStart)
901: endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
902: else
903: endIndex = searchFirst(end + 1);
904: }
905: else
906: {
907: startIndex = searchFirst(offset + (gapEnd - gapStart));
908: endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
909: }
910: if (v == null)
911: v = new Vector();
912: for (int i = startIndex; i < endIndex; i++)
913: {
914: v.add(new UndoPosRef((Mark) marks.get(i)));
915: }
916: return v;
917: }
918:
919:
925: protected void resetMarksAtZero()
926: {
927: if (gapStart != 0)
928: return;
929:
930: for (int i = 0; i < marks.size(); i++)
931: {
932: Mark m = (Mark) marks.get(i);
933: if (m.mark <= gapEnd)
934: m.mark = 0;
935: }
936: }
937:
938:
949: protected void updateUndoPositions(Vector positions, int offset, int length)
950: {
951: for (Iterator i = positions.iterator(); i.hasNext();)
952: {
953: UndoPosRef undoPosRef = (UndoPosRef) i.next();
954: undoPosRef.reset();
955: }
956:
957:
958: Collections.sort(marks);
959: }
960:
961:
966: private void dump()
967: {
968: System.err.println("GapContent debug information");
969: System.err.println("buffer length: " + buffer.length);
970: System.err.println("gap start: " + gapStart);
971: System.err.println("gap end: " + gapEnd);
972: for (int i = 0; i < buffer.length; i++)
973: {
974: if (i == gapStart)
975: System.err.print('<');
976: if (i == gapEnd)
977: System.err.print('>');
978:
979: if (!Character.isISOControl(buffer[i]))
980: System.err.print(buffer[i]);
981: else
982: System.err.print('.');
983: }
984: System.err.println();
985: }
986:
987:
990: private void dumpMarks()
991: {
992: System.out.print("positionMarks: ");
993: for (int i = 0; i < marks.size(); i++)
994: System.out.print(((Mark) marks.get(i)).mark + ", ");
995: System.out.println();
996: }
997:
998:
1010: int search(Mark o)
1011: {
1012: int foundInd = 0;
1013: boolean found = false;
1014: int low = 0;
1015: int up = marks.size() - 1;
1016: int mid = 0;
1017: if (up > -1)
1018: {
1019: int cmp = 0;
1020: Mark last = (Mark) marks.get(up);
1021: cmp = compare(o, last);
1022: if (cmp > 0)
1023: {
1024: foundInd = up + 1;
1025: found = true;
1026: }
1027: else
1028: {
1029: while (low <= up && ! found)
1030: {
1031: mid = low + (up - low) / 2;
1032: Mark m = (Mark) marks.get(mid);
1033: cmp = compare(o, m);
1034: if (cmp == 0)
1035: {
1036: foundInd = mid;
1037: found = true;
1038: }
1039: else if (cmp < 0)
1040: up = mid - 1;
1041: else
1042: low = mid + 1;
1043: }
1044:
1045: if (! found)
1046: foundInd = cmp < 0 ? mid : mid + 1;
1047: }
1048: }
1049: return foundInd;
1050: }
1051:
1052: private int searchFirst(int index)
1053: {
1054: searchMark.mark = Math.max(index, 1);
1055: int i = search(searchMark);
1056: for (int j = i - 1; j >= 0; j--)
1057: {
1058: Mark m = (Mark) marks.get(j);
1059: if (m.mark != index)
1060: break;
1061: i--;
1062: }
1063: return i;
1064: }
1065:
1066:
1074: private int compare(Mark m1, Mark m2)
1075: {
1076: return m1.mark - m2.mark;
1077: }
1078:
1079:
1082: private void garbageCollect()
1083: {
1084: int count = marks.size();
1085: ArrayList clean = new ArrayList();
1086: for (int i = 0; i < count; i++)
1087: {
1088: Mark m = (Mark) marks.get(i);
1089: if (m.get() != null)
1090: clean.add(m);
1091: }
1092: marks = clean;
1093: garbageMarks = 0;
1094: }
1095: }