summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/javax/swing/text/GapContent.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/GapContent.java')
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java360
1 files changed, 249 insertions, 111 deletions
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java
index 1bbef8f93d6..1dd46c4b0f4 100644
--- a/libjava/classpath/javax/swing/text/GapContent.java
+++ b/libjava/classpath/javax/swing/text/GapContent.java
@@ -39,29 +39,93 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.ListIterator;
import javax.swing.undo.UndoableEdit;
/**
- * This implementation of {@link AbstractDocument.Content} uses a gapped
- * buffer. This takes advantage of the fact that text area content is
- * mostly inserted sequentially. The buffer is a char array that maintains
- * a gap at the current insertion point. If characters a inserted at
- * gap boundaries, the cost is minimal (simple array access). The array only
- * has to be shifted around when the insertion point moves (then the gap also
- * moves and one array copy is necessary) or when the gap is filled up and
- * the buffer has to be enlarged.
- *
+ * This implementation of {@link AbstractDocument.Content} uses a gapped buffer.
+ * This takes advantage of the fact that text area content is mostly inserted
+ * sequentially. The buffer is a char array that maintains a gap at the current
+ * insertion point. If characters a inserted at gap boundaries, the cost is
+ * minimal (simple array access). The array only has to be shifted around when
+ * the insertion point moves (then the gap also moves and one array copy is
+ * necessary) or when the gap is filled up and the buffer has to be enlarged.
+ *
* TODO: Implement UndoableEdit support stuff
*/
public class GapContent
- implements AbstractDocument.Content, Serializable
+ implements AbstractDocument.Content, Serializable
{
+
+ /**
+ * A {@link Position} implementation for <code>GapContent</code>.
+ */
+ class GapContentPosition
+ implements Position, Comparable
+ {
+
+ /** The index within the buffer array. */
+ int mark;
+
+ /**
+ * Creates a new GapContentPosition object.
+ *
+ * @param mark the mark of this Position
+ */
+ GapContentPosition(int mark)
+ {
+ this.mark = mark;
+ }
+
+ /**
+ * Comparable interface implementation. This is used to store all
+ * positions in an ordered fashion.
+ *
+ * @param o the object to be compared to this
+ *
+ * @return a negative integer if this is less than <code>o</code>, zero
+ * if both are equal or a positive integer if this is greater than
+ * <code>o</code>
+ *
+ * @throws ClassCastException if <code>o</code> is not a
+ * GapContentPosition or Integer object
+ */
+ public int compareTo(Object o)
+ {
+ if (o instanceof Integer)
+ {
+ int otherMark = ((Integer) o).intValue();
+ return mark - otherMark;
+ }
+ else
+ {
+ GapContentPosition other = (GapContentPosition) o;
+ return mark - other.mark;
+ }
+ }
+
+ /**
+ * Returns the current offset of this Position within the content.
+ *
+ * @return the current offset of this Position within the content.
+ */
+ public int getOffset()
+ {
+ if (mark <= gapStart)
+ return mark;
+ else
+ return mark - (gapEnd - gapStart);
+ }
+ }
+
private static final long serialVersionUID = 8374645204155842629L;
/**
- * This is the default buffer size and the amount of bytes that
- * a buffer is extended if it is full.
+ * 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;
@@ -81,6 +145,12 @@ public class GapContent
int gapEnd;
/**
+ * The positions generated by this GapContent. They are kept in an ordered
+ * fashion, so they can be looked up easily.
+ */
+ LinkedList positions;
+
+ /**
* Creates a new GapContent object.
*/
public GapContent()
@@ -90,7 +160,7 @@ public class GapContent
/**
* Creates a new GapContent object with a specified initial size.
- *
+ *
* @param size the initial size of the buffer
*/
public GapContent(int size)
@@ -99,14 +169,15 @@ public class GapContent
gapStart = 0;
gapEnd = size - 1;
buffer[size - 1] = '\n';
+ positions = new LinkedList();
}
/**
* Allocates an array of the specified length that can then be used as
* buffer.
- *
+ *
* @param size the size of the array to be allocated
- *
+ *
* @return the allocated array
*/
protected Object allocateArray(int size)
@@ -116,7 +187,7 @@ public class GapContent
/**
* Returns the length of the allocated buffer array.
- *
+ *
* @return the length of the allocated buffer array
*/
protected int getArrayLength()
@@ -126,7 +197,7 @@ public class GapContent
/**
* Returns the length of the content.
- *
+ *
* @return the length of the content
*/
public int length()
@@ -136,18 +207,18 @@ public class GapContent
/**
* Inserts a string at the specified position.
- *
+ *
* @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)
- *
- * @throws BadLocationException if <code>where</code> is not a valid location
- * in the buffer
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in the buffer
*/
public UndoableEdit insertString(int where, String str)
- throws BadLocationException
+ throws BadLocationException
{
// check arguments
int length = length();
@@ -155,190 +226,230 @@ public class GapContent
if (where >= length)
throw new BadLocationException("the where argument cannot be greater"
- + " than the content length", where);
-
- // check if the gap is big enough to hold the string
- if ((gapEnd - gapStart) < strLen)
- // make room for this string and some more
- shiftEnd(strLen + DEFAULT_BUFSIZE);
+ + " than the content length", where);
- // are we at the gap boundary?
- if (where != gapStart)
- shiftGap(where);
+ replace(where, 0, str.toCharArray(), str.length());
- // now we can simple copy the string into the gap and adjust the
- // gap boundaries
- System.arraycopy(str.toCharArray(), 0, buffer, gapStart, strLen);
- gapStart += strLen;
return null;
}
/**
* Removes a piece of content at th specified position.
- *
+ *
* @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)
- *
- * @throws BadLocationException if <code>where</code> is not a valid location
- * in the buffer
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in the buffer
*/
- public UndoableEdit remove(int where, int nitems)
- throws BadLocationException
+ public UndoableEdit remove(int where, int nitems) throws BadLocationException
{
// check arguments
int length = length();
if (where >= length)
throw new BadLocationException("the where argument cannot be greater"
- + " than the content length", where);
+ + " than the content length", where);
if ((where + nitems) > length)
throw new BadLocationException("where + nitems cannot be greater"
- + " than the content length",
- where + nitems);
+ + " than the content length", where + nitems);
- // check if we are at the gap boundary
- if (where != gapStart)
- shiftGap(where);
+ replace(where, nitems, null, 0);
- // now we simply have to enlarge the gap
- gapEnd += nitems;
return null;
}
/**
* Returns a piece of content as String.
- *
+ *
* @param where the start location of the fragment
* @param len the length of the fragment
- *
+ *
* @throws BadLocationException if <code>where</code> or
* <code>where + len</code> are no valid locations in the buffer
*/
public String getString(int where, int len) throws BadLocationException
{
Segment seg = new Segment();
- getChars(where, len, seg);
- return new String(seg.array, seg.offset, seg.count);
+ try
+ {
+ getChars(where, len, seg);
+ return new String(seg.array, seg.offset, seg.count);
+ }
+ catch (StringIndexOutOfBoundsException ex)
+ {
+ int invalid = 0;
+ if (seg.offset < 0 || seg.offset >= seg.array.length)
+ invalid = seg.offset;
+ else
+ invalid = seg.offset + seg.count;
+ throw new BadLocationException("Illegal location: array.length = "
+ + seg.array.length + ", offset = "
+ + seg.offset + ", count = "
+ + seg.count, invalid);
+ }
}
/**
* Fetches a piece of content and stores it in a {@link Segment} object.
- *
- * If the requested piece of text spans the gap, the content is copied
- * into a new array. If it doesn't then it is contiguous and the
- * actual content store is returned.
- *
+ *
+ * If the requested piece of text spans the gap, the content is copied into a
+ * new array. If it doesn't then it is contiguous and the actual content
+ * store is returned.
+ *
* @param where the start location of the fragment
* @param len the length of the fragment
* @param txt the Segment object to store the fragment in
- *
+ *
* @throws BadLocationException if <code>where</code> or
* <code>where + len</code> are no valid locations in the buffer
*/
public void getChars(int where, int len, Segment txt)
- throws BadLocationException
+ throws BadLocationException
{
// check arguments
int length = length();
if (where >= length)
throw new BadLocationException("the where argument cannot be greater"
- + " than the content length", where);
+ + " than the content length", where);
if ((where + len) > length)
throw new BadLocationException("len plus where cannot be greater"
- + " than the content length",
- len + where);
+ + " than the content length", len + where);
// check if requested segment is contiguous
if ((where < gapStart) && ((gapStart - where) < len))
- {
- // requested segment is not contiguous -> copy the pieces together
- char[] copy = new char[len];
- int lenFirst = gapStart - where; // the length of the first segment
- System.arraycopy(buffer, where, copy, 0, lenFirst);
- System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst);
- txt.array = copy;
- txt.offset = 0;
- txt.count = len;
- }
+ {
+ // requested segment is not contiguous -> copy the pieces together
+ char[] copy = new char[len];
+ int lenFirst = gapStart - where; // the length of the first segment
+ System.arraycopy(buffer, where, copy, 0, lenFirst);
+ System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst);
+ txt.array = copy;
+ txt.offset = 0;
+ txt.count = len;
+ }
else
- {
- // requested segment is contiguous -> we can simply return the
- // actual content
- txt.array = buffer;
- if (where < gapStart)
- txt.offset = where;
- else
- txt.offset = where + (gapEnd - gapStart);
- txt.count = len;
- }
+ {
+ // requested segment is contiguous -> we can simply return the
+ // actual content
+ txt.array = buffer;
+ if (where < gapStart)
+ txt.offset = where;
+ else
+ txt.offset = where + (gapEnd - gapStart);
+ txt.count = len;
+ }
}
/**
* Creates and returns a mark at the specified position.
- *
+ *
* @param offset the position at which to create the mark
- *
+ *
* @return the create Position object for the mark
- *
- * @throws BadLocationException if the offset is not a valid position in
- * the buffer
+ *
+ * @throws BadLocationException if the offset is not a valid position in the
+ * buffer
*/
public Position createPosition(final int offset) throws BadLocationException
{
- return new Position()
- {
- int off = offset;
-
- public int getOffset()
- {
- return off;
- }
- };
+ if (offset < 0 || offset > length())
+ throw new BadLocationException("The offset was out of the bounds of this"
+ + " buffer", offset);
+
+ // We store the actual array index in the GapContentPosition. The real
+ // offset is then calculated in the GapContentPosition.
+ int mark = offset;
+ if (offset > gapStart)
+ mark += gapEnd - gapStart;
+ GapContentPosition pos = new GapContentPosition(mark);
+
+ // Add this into our list in a sorted fashion.
+ int index = Collections.binarySearch(positions, pos);
+ if (index < 0)
+ index = -(index + 1);
+ positions.add(index, pos);
+
+ return pos;
}
/**
* Enlarges the gap. This allocates a new bigger buffer array, copy the
- * segment before the gap as it is and the segment after the gap at
- * the end of the new buffer array. This does change the gapEnd mark
- * but not the gapStart mark.
- *
+ * segment before the gap as it is and the segment after the gap at the end
+ * of the new buffer array. This does change the gapEnd mark but not the
+ * gapStart mark.
+ *
* @param newSize the new size of the gap
*/
protected void shiftEnd(int newSize)
{
+ int delta = (gapEnd - gapStart) - newSize;
char[] newBuf = (char[]) allocateArray(length() + newSize);
System.arraycopy(buffer, 0, newBuf, 0, gapStart);
- System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize,
- buffer.length - gapEnd);
+ System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
+ - gapEnd);
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;
+ }
}
/**
* Shifts the gap to the specified position.
- *
+ *
* @param newGapStart the new start position of the gap
*/
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)
{
- System.arraycopy(buffer, newGapStart, buffer, newGapEnd,
- gapStart - newGapStart);
+ System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
+ - newGapStart);
gapStart = newGapStart;
gapEnd = newGapEnd;
}
else
{
- System.arraycopy(buffer, gapEnd, buffer, gapStart,
- newGapStart - gapStart);
+ System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
+ - gapStart);
gapStart = newGapStart;
gapEnd = newGapEnd;
}
@@ -346,11 +457,38 @@ public class GapContent
/**
* Returns the allocated buffer array.
- *
+ *
* @return the allocated buffer array
*/
protected Object getArray()
{
return buffer;
}
+
+ /**
+ * Replaces a portion of the storage with the specified items.
+ *
+ * @param position the position at which to remove items
+ * @param rmSize the number of items to remove
+ * @param addItems the items to add at location
+ * @param addSize the number of items to add
+ */
+ protected void replace(int position, int rmSize, Object addItems,
+ int addSize)
+ {
+ // Remove content
+ shiftGap(position);
+ gapEnd += rmSize;
+
+ // If gap is too small, enlarge the gap.
+ if ((gapEnd - gapStart) < addSize)
+ shiftEnd(addSize);
+
+ // Add new items to the buffer.
+ if (addItems != null)
+ {
+ System.arraycopy(addItems, 0, buffer, gapStart, addSize);
+ gapStart += addSize;
+ }
+ }
}
OpenPOWER on IntegriCloud