1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47:
48: import ;
49: import ;
50: import ;
51: import ;
52:
53:
64: public class GapContent
65: implements AbstractDocument.Content, Serializable
66: {
67:
68:
71: class GapContentPosition
72: implements Position, Comparable
73: {
74:
75:
76: int mark;
77:
78:
83: GapContentPosition(int mark)
84: {
85: this.mark = mark;
86: }
87:
88:
101: public int compareTo(Object o)
102: {
103: if (o instanceof Integer)
104: {
105: int otherMark = ((Integer) o).intValue();
106: return mark - otherMark;
107: }
108: else
109: {
110: GapContentPosition other = (GapContentPosition) o;
111: return mark - other.mark;
112: }
113: }
114:
115:
120: public int getOffset()
121: {
122:
123: assert mark <= gapStart || mark >= gapEnd : "mark: " + mark
124: + ", gapStart: " + gapStart
125: + ", gapEnd: " + gapEnd;
126: if (mark <= gapStart)
127: return mark;
128: else
129: return mark - (gapEnd - gapStart);
130: }
131: }
132:
133: class InsertUndo extends AbstractUndoableEdit
134: {
135: public int where, length;
136: String text;
137: public InsertUndo(int start, int len)
138: {
139: where = start;
140: length = len;
141: }
142:
143: public void undo () throws CannotUndoException
144: {
145: super.undo();
146: try
147: {
148: text = getString(where, length);
149: remove(where, length);
150: }
151: catch (BadLocationException ble)
152: {
153: throw new CannotUndoException();
154: }
155: }
156:
157: public void redo () throws CannotUndoException
158: {
159: super.redo();
160: try
161: {
162: insertString(where, text);
163: }
164: catch (BadLocationException ble)
165: {
166: throw new CannotRedoException();
167: }
168: }
169:
170: }
171:
172: class UndoRemove extends AbstractUndoableEdit
173: {
174: public int where;
175: String text;
176: public UndoRemove(int start, String removedText)
177: {
178: where = start;
179: text = removedText;
180: }
181:
182: public void undo () throws CannotUndoException
183: {
184: super.undo();
185: try
186: {
187: insertString(where, text);
188: }
189: catch (BadLocationException ble)
190: {
191: throw new CannotUndoException();
192: }
193: }
194:
195: public void redo () throws CannotUndoException
196: {
197: super.redo();
198: try
199: {
200: remove(where, text.length());
201: }
202: catch (BadLocationException ble)
203: {
204: throw new CannotRedoException();
205: }
206: }
207:
208: }
209:
210:
211: private static final long serialVersionUID = -6226052713477823730L;
212:
213:
217: static final int DEFAULT_BUFSIZE = 10;
218:
219:
222: char[] buffer;
223:
224:
227: int gapStart;
228:
229:
232: int gapEnd;
233:
234:
238: ArrayList positions;
239:
240:
243: public GapContent()
244: {
245: this(DEFAULT_BUFSIZE);
246: }
247:
248:
253: public GapContent(int size)
254: {
255: buffer = (char[]) allocateArray(size);
256: gapStart = 1;
257: gapEnd = size;
258: buffer[0] = '\n';
259: positions = new ArrayList();
260: }
261:
262:
270: protected Object allocateArray(int size)
271: {
272: return new char[size];
273: }
274:
275:
280: protected int getArrayLength()
281: {
282: return buffer.length;
283: }
284:
285:
290: public int length()
291: {
292: return buffer.length - (gapEnd - gapStart);
293: }
294:
295:
306: public UndoableEdit insertString(int where, String str)
307: throws BadLocationException
308: {
309:
310: int length = length();
311: int strLen = str.length();
312:
313: if (where >= length)
314: throw new BadLocationException("the where argument cannot be greater"
315: + " than the content length", where);
316:
317: replace(where, 0, str.toCharArray(), strLen);
318:
319: return new InsertUndo(where, strLen);
320: }
321:
322:
333: public UndoableEdit remove(int where, int nitems) throws BadLocationException
334: {
335:
336: int length = length();
337:
338: if (where >= length)
339: throw new BadLocationException("the where argument cannot be greater"
340: + " than the content length", where);
341: if ((where + nitems) > length)
342: throw new BadLocationException("where + nitems cannot be greater"
343: + " than the content length", where + nitems);
344:
345: String removedText = getString(where, nitems);
346: replace(where, nitems, null, 0);
347:
348: return new UndoRemove(where, removedText);
349: }
350:
351:
360: public String getString(int where, int len) throws BadLocationException
361: {
362: Segment seg = new Segment();
363: try
364: {
365: getChars(where, len, seg);
366: return new String(seg.array, seg.offset, seg.count);
367: }
368: catch (StringIndexOutOfBoundsException ex)
369: {
370: int invalid = 0;
371: if (seg.offset < 0 || seg.offset >= seg.array.length)
372: invalid = seg.offset;
373: else
374: invalid = seg.offset + seg.count;
375: throw new BadLocationException("Illegal location: array.length = "
376: + seg.array.length + ", offset = "
377: + seg.offset + ", count = "
378: + seg.count, invalid);
379: }
380: }
381:
382:
396: public void getChars(int where, int len, Segment txt)
397: throws BadLocationException
398: {
399:
400: int length = length();
401: if (where >= length)
402: throw new BadLocationException("the where argument cannot be greater"
403: + " than the content length", where);
404: if ((where + len) > length)
405: throw new BadLocationException("len plus where cannot be greater"
406: + " than the content length", len + where);
407:
408:
409: if ((where < gapStart) && ((gapStart - where) < len))
410: {
411:
412: char[] copy = new char[len];
413: int lenFirst = gapStart - where;
414: System.arraycopy(buffer, where, copy, 0, lenFirst);
415: System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst);
416: txt.array = copy;
417: txt.offset = 0;
418: txt.count = len;
419: }
420: else
421: {
422:
423:
424: txt.array = buffer;
425: if (where < gapStart)
426: txt.offset = where;
427: else
428: txt.offset = where + (gapEnd - gapStart);
429: txt.count = len;
430: }
431: }
432:
433:
443: public Position createPosition(final int offset) throws BadLocationException
444: {
445: if (offset < 0 || offset > length())
446: throw new BadLocationException("The offset was out of the bounds of this"
447: + " buffer", offset);
448:
449:
450:
451: int mark = offset;
452: if (offset >= gapStart)
453: mark += gapEnd - gapStart;
454: GapContentPosition pos = new GapContentPosition(mark);
455:
456:
457: int index = Collections.binarySearch(positions, pos);
458: if (index < 0)
459: index = -(index + 1);
460: positions.add(index, pos);
461: return pos;
462: }
463:
464:
472: protected void shiftEnd(int newSize)
473: {
474: assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
475: + "than the old gap size";
476:
477: int delta = newSize - gapEnd + gapStart;
478:
479: adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta);
480:
481:
482: char[] newBuf = (char[]) allocateArray(length() + newSize);
483: System.arraycopy(buffer, 0, newBuf, 0, gapStart);
484: System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
485: - gapEnd);
486: gapEnd = gapStart + newSize;
487: buffer = newBuf;
488:
489: }
490:
491:
496: protected void shiftGap(int newGapStart)
497: {
498: if (newGapStart == gapStart)
499: return;
500:
501: int newGapEnd = newGapStart + gapEnd - gapStart;
502: if (newGapStart < gapStart)
503: {
504:
505:
506: adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart);
507: System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
508: - newGapStart);
509: gapStart = newGapStart;
510: gapEnd = newGapEnd;
511: }
512: else
513: {
514:
515:
516: adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart));
517: System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
518: - gapStart);
519: gapStart = newGapStart;
520: gapEnd = newGapEnd;
521: }
522: if (gapStart == 0)
523: resetMarksAtZero();
524: }
525:
526:
534: protected void shiftGapStartDown(int newGapStart)
535: {
536: if (newGapStart == gapStart)
537: return;
538:
539: assert newGapStart < gapStart : "The new gap start must be less than the "
540: + "old gap start.";
541: setPositionsInRange(newGapStart, gapStart - newGapStart, gapStart);
542: gapStart = newGapStart;
543: }
544:
545:
553: protected void shiftGapEndUp(int newGapEnd)
554: {
555: if (newGapEnd == gapEnd)
556: return;
557:
558: assert newGapEnd > gapEnd : "The new gap end must be greater than the "
559: + "old gap end.";
560: setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1);
561: gapEnd = newGapEnd;
562: }
563:
564:
569: protected Object getArray()
570: {
571: return buffer;
572: }
573:
574:
582: protected void replace(int position, int rmSize, Object addItems,
583: int addSize)
584: {
585: if (gapStart != position)
586: shiftGap(position);
587:
588:
589: if (rmSize > 0)
590: shiftGapEndUp(gapEnd + rmSize);
591:
592:
593: if ((gapEnd - gapStart) <= addSize)
594: shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE);
595:
596:
597: if (addItems != null)
598: {
599: System.arraycopy(addItems, 0, buffer, gapStart, addSize);
600: gapStart += addSize;
601: }
602: }
603:
604:
609: protected final int getGapStart()
610: {
611: return gapStart;
612: }
613:
614:
619: protected final int getGapEnd()
620: {
621: return gapEnd;
622: }
623:
624:
634: protected Vector getPositionsInRange(Vector v, int offset, int length)
635: {
636: Vector res = v;
637: if (res == null)
638: res = new Vector();
639: else
640: res.clear();
641:
642: int endOffset = offset + length;
643:
644: int index1 = Collections.binarySearch(positions,
645: new GapContentPosition(offset));
646: if (index1 < 0)
647: index1 = -(index1 + 1);
648:
649:
650:
651: while (index1 > 0
652: && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
653: index1--;
654:
655: for (ListIterator i = positions.listIterator(index1); i.hasNext();)
656: {
657: GapContentPosition p = (GapContentPosition) i.next();
658: if (p.mark > endOffset)
659: break;
660: if (p.mark >= offset && p.mark <= endOffset)
661: res.add(p);
662: }
663: return res;
664: }
665:
666:
675: void setPositionsInRange(int offset, int length, int value)
676: {
677: int endOffset = offset + length;
678:
679: int index1 = Collections.binarySearch(positions,
680: new GapContentPosition(offset));
681: if (index1 < 0)
682: index1 = -(index1 + 1);
683:
684:
685:
686: while (index1 > 0
687: && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
688: index1--;
689:
690: for (ListIterator i = positions.listIterator(index1); i.hasNext();)
691: {
692: GapContentPosition p = (GapContentPosition) i.next();
693: if (p.mark > endOffset)
694: break;
695:
696: if (p.mark >= offset && p.mark <= endOffset)
697: p.mark = value;
698: }
699: }
700:
701:
710: void adjustPositionsInRange(int offset, int length, int incr)
711: {
712: int endOffset = offset + length;
713:
714: int index1 = Collections.binarySearch(positions,
715: new GapContentPosition(offset));
716: if (index1 < 0)
717: index1 = -(index1 + 1);
718:
719:
720:
721: while (index1 > 0
722: && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
723: index1--;
724: for (ListIterator i = positions.listIterator(index1); i.hasNext();)
725: {
726: GapContentPosition p = (GapContentPosition) i.next();
727: if (p.mark > endOffset)
728: break;
729:
730: if (p.mark >= offset && p.mark <= endOffset)
731: p.mark += incr;
732: }
733: }
734:
735:
741: protected void resetMarksAtZero()
742: {
743: if (gapStart != 0)
744: return;
745:
746: setPositionsInRange(gapEnd, 0, 0);
747: }
748:
749:
754: private void dump()
755: {
756: System.err.println("GapContent debug information");
757: System.err.println("buffer length: " + buffer.length);
758: System.err.println("gap start: " + gapStart);
759: System.err.println("gap end: " + gapEnd);
760: for (int i = 0; i < buffer.length; i++)
761: {
762: if (i == gapStart)
763: System.err.print('<');
764: if (i == gapEnd)
765: System.err.print('>');
766:
767: if (!Character.isISOControl(buffer[i]))
768: System.err.print(buffer[i]);
769: else
770: System.err.print('.');
771: }
772: System.err.println();
773: }
774:
775: private void dumpPositions()
776: {
777: for (Iterator i = positions.iterator(); i.hasNext();)
778: {
779: GapContentPosition pos = (GapContentPosition) i.next();
780: System.err.println("position at: " + pos.mark);
781: }
782: }
783: }