diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/GapContent.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/GapContent.java | 382 |
1 files changed, 325 insertions, 57 deletions
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 1dd46c4b0f4..4c65de0f5f4 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -39,10 +39,15 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; +import java.util.Iterator; import java.util.ListIterator; +import java.util.Vector; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoableEdit; /** @@ -59,7 +64,6 @@ import javax.swing.undo.UndoableEdit; public class GapContent implements AbstractDocument.Content, Serializable { - /** * A {@link Position} implementation for <code>GapContent</code>. */ @@ -114,6 +118,11 @@ public class GapContent */ public int getOffset() { + // Check precondition. + assert mark <= gapStart || mark >= gapEnd : "mark: " + mark + + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + if (mark <= gapStart) return mark; else @@ -121,13 +130,91 @@ public class GapContent } } - private static final long serialVersionUID = 8374645204155842629L; + class UndoInsertString extends AbstractUndoableEdit + { + public int where, length; + String text; + public UndoInsertString(int start, int len) + { + where = start; + length = len; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + text = getString(where, length); + remove(where, length); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + class UndoRemove extends AbstractUndoableEdit + { + public int where; + String text; + public UndoRemove(int start, String removedText) + { + where = start; + text = removedText; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + remove(where, text.length()); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6226052713477823730L; /** * This is the default buffer size and the amount of bytes that a buffer is * extended if it is full. */ - static final int DEFAULT_BUFSIZE = 64; + static final int DEFAULT_BUFSIZE = 10; /** * The text buffer. @@ -148,7 +235,7 @@ public class GapContent * The positions generated by this GapContent. They are kept in an ordered * fashion, so they can be looked up easily. */ - LinkedList positions; + ArrayList positions; /** * Creates a new GapContent object. @@ -166,10 +253,10 @@ public class GapContent public GapContent(int size) { buffer = (char[]) allocateArray(size); - gapStart = 0; - gapEnd = size - 1; - buffer[size - 1] = '\n'; - positions = new LinkedList(); + gapStart = 1; + gapEnd = size; + buffer[0] = '\n'; + positions = new ArrayList(); } /** @@ -211,8 +298,7 @@ public class GapContent * @param where the position where the string is inserted * @param str the string that is to be inserted * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -228,9 +314,9 @@ public class GapContent throw new BadLocationException("the where argument cannot be greater" + " than the content length", where); - replace(where, 0, str.toCharArray(), str.length()); + replace(where, 0, str.toCharArray(), strLen); - return null; + return new UndoInsertString(where, strLen); } /** @@ -239,8 +325,7 @@ public class GapContent * @param where the position where the content is to be removed * @param nitems number of characters to be removed * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -257,9 +342,10 @@ public class GapContent throw new BadLocationException("where + nitems cannot be greater" + " than the content length", where + nitems); + String removedText = getString(where, nitems); replace(where, nitems, null, 0); - return null; + return new UndoRemove(where, removedText); } /** @@ -372,7 +458,6 @@ public class GapContent if (index < 0) index = -(index + 1); positions.add(index, pos); - return pos; } @@ -386,7 +471,14 @@ public class GapContent */ protected void shiftEnd(int newSize) { - int delta = (gapEnd - gapStart) - newSize; + assert newSize > (gapEnd - gapStart) : "The new gap size must be greater " + + "than the old gap size"; + + int delta = newSize - gapEnd + gapStart; + // Update the marks after the gapEnd. + adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta); + + // Copy the data around. char[] newBuf = (char[]) allocateArray(length() + newSize); System.arraycopy(buffer, 0, newBuf, 0, gapStart); System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length @@ -394,18 +486,6 @@ public class GapContent gapEnd = gapStart + newSize; buffer = newBuf; - // Update the marks after the gapEnd. - int index = Collections.binarySearch(positions, new GapContentPosition( - gapEnd)); - if (index < 0) - { - index = -(index + 1); - } - for (ListIterator i = positions.listIterator(index); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += delta; - } } /** @@ -415,32 +495,15 @@ public class GapContent */ protected void shiftGap(int newGapStart) { - int newGapEnd = newGapStart + (gapEnd - gapStart); - - // Update the positions between newGapEnd and (old) gapEnd. The marks - // must be shifted by (gapEnd - newGapEnd). - int index1 = Collections.binarySearch(positions, - new GapContentPosition(gapEnd)); - int index2 = Collections.binarySearch(positions, - new GapContentPosition(newGapEnd)); - if (index1 > 0 && index2 > 0) - { - int i1 = Math.min(index1, index2); - int i2 = Math.max(index1, index2); - for (ListIterator i = positions.listIterator(i1); i.hasNext();) - { - if (i.nextIndex() > i2) - break; - - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += gapEnd - newGapEnd; - } - } - if (newGapStart == gapStart) return; - else if (newGapStart < gapStart) + + int newGapEnd = newGapStart + gapEnd - gapStart; + if (newGapStart < gapStart) { + // Update the positions between newGapStart and (old) gapStart. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart); System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -448,11 +511,54 @@ public class GapContent } else { + // Update the positions between newGapEnd and (old) gapEnd. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart)); System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; gapEnd = newGapEnd; } + if (gapStart == 0) + resetMarksAtZero(); + } + + /** + * Shifts the gap start downwards. This does not affect the content of the + * buffer. This only updates the gap start and all the marks that are between + * the old gap start and the new gap start. They all are squeezed to the start + * of the gap, because their location has been removed. + * + * @param newGapStart the new gap start + */ + protected void shiftGapStartDown(int newGapStart) + { + if (newGapStart == gapStart) + return; + + assert newGapStart < gapStart : "The new gap start must be less than the " + + "old gap start."; + setPositionsInRange(newGapStart, gapStart - newGapStart, gapStart); + gapStart = newGapStart; + } + + /** + * Shifts the gap end upwards. This does not affect the content of the + * buffer. This only updates the gap end and all the marks that are between + * the old gap end and the new end start. They all are squeezed to the end + * of the gap, because their location has been removed. + * + * @param newGapEnd the new gap start + */ + protected void shiftGapEndUp(int newGapEnd) + { + if (newGapEnd == gapEnd) + return; + + assert newGapEnd > gapEnd : "The new gap end must be greater than the " + + "old gap end."; + setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1); + gapEnd = newGapEnd; } /** @@ -476,13 +582,15 @@ public class GapContent protected void replace(int position, int rmSize, Object addItems, int addSize) { + if (gapStart != position) + shiftGap(position); // Remove content - shiftGap(position); - gapEnd += rmSize; + if (rmSize > 0) + shiftGapEndUp(gapEnd + rmSize); // If gap is too small, enlarge the gap. - if ((gapEnd - gapStart) < addSize) - shiftEnd(addSize); + if ((gapEnd - gapStart) <= addSize) + shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE); // Add new items to the buffer. if (addItems != null) @@ -491,4 +599,164 @@ public class GapContent gapStart += addSize; } } + + /** + * Returns the start index of the gap within the buffer array. + * + * @return the start index of the gap within the buffer array + */ + protected final int getGapStart() + { + return gapStart; + } + + /** + * Returns the end index of the gap within the buffer array. + * + * @return the end index of the gap within the buffer array + */ + protected final int getGapEnd() + { + return gapEnd; + } + + /** + * Returns all <code>Position</code>s that are in the range specified by + * <code>offset</code> and </code>length</code> within the buffer array. + * + * @param v the vector to use; if <code>null</code>, a new Vector is allocated + * @param offset the start offset of the range to search + * @param length the length of the range to search + * + * @return the positions within the specified range + */ + protected Vector getPositionsInRange(Vector v, int offset, int length) + { + Vector res = v; + if (res == null) + res = new Vector(); + else + res.clear(); + + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + if (p.mark >= offset && p.mark <= endOffset) + res.add(p); + } + return res; + } + + /** + * Sets the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array to <code>value</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param value the new value for each mark + */ + void setPositionsInRange(int offset, int length, int value) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark = value; + } + } + + /** + * Adjusts the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array by <code>increment</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param incr the increment + */ + void adjustPositionsInRange(int offset, int length, int incr) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark += incr; + } + } + + /** + * Resets all <code>Position</code> that have an offset of <code>0</code>, + * to also have an array index of <code>0</code>. This might be necessary + * after a call to <code>shiftGap(0)</code>, since then the marks at offset + * <code>0</code> get shifted to <code>gapEnd</code>. + */ + protected void resetMarksAtZero() + { + if (gapStart != 0) + return; + + setPositionsInRange(gapEnd, 0, 0); + } + + /** + * Outputs debugging info to System.err. It prints out the buffer array, + * the gapStart is marked by a < sign, the gapEnd is marked by a > + * sign and each position is marked by a # sign. + */ + private void dump() + { + System.err.println("GapContent debug information"); + System.err.println("buffer length: " + buffer.length); + System.err.println("gap start: " + gapStart); + System.err.println("gap end: " + gapEnd); + for (int i = 0; i < buffer.length; i++) + { + if (i == gapStart) + System.err.print('<'); + if (i == gapEnd) + System.err.print('>'); + + if (!Character.isISOControl(buffer[i])) + System.err.print(buffer[i]); + else + System.err.print('.'); + } + System.err.println(); + } + + private void dumpPositions() + { + for (Iterator i = positions.iterator(); i.hasNext();) + { + GapContentPosition pos = (GapContentPosition) i.next(); + System.err.println("position at: " + pos.mark); + } + } } |