diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/GapContent.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/GapContent.java | 360 |
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; + } + } } |