feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
954
jdkSrc/jdk8/javax/swing/text/GapContent.java
Normal file
954
jdkSrc/jdk8/javax/swing/text/GapContent.java
Normal file
@@ -0,0 +1,954 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package javax.swing.text;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import javax.swing.undo.AbstractUndoableEdit;
|
||||
import javax.swing.undo.CannotRedoException;
|
||||
import javax.swing.undo.CannotUndoException;
|
||||
import javax.swing.undo.UndoableEdit;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
|
||||
/**
|
||||
* An implementation of the AbstractDocument.Content interface
|
||||
* implemented using a gapped buffer similar to that used by emacs.
|
||||
* The underlying storage is a array of unicode characters with
|
||||
* a gap somewhere. The gap is moved to the location of changes
|
||||
* to take advantage of common behavior where most changes are
|
||||
* in the same location. Changes that occur at a gap boundary are
|
||||
* generally cheap and moving the gap is generally cheaper than
|
||||
* moving the array contents directly to accommodate the change.
|
||||
* <p>
|
||||
* The positions tracking change are also generally cheap to
|
||||
* maintain. The Position implementations (marks) store the array
|
||||
* index and can easily calculate the sequential position from
|
||||
* the current gap location. Changes only require update to the
|
||||
* the marks between the old and new gap boundaries when the gap
|
||||
* is moved, so generally updating the marks is pretty cheap.
|
||||
* The marks are stored sorted so they can be located quickly
|
||||
* with a binary search. This increases the cost of adding a
|
||||
* mark, and decreases the cost of keeping the mark updated.
|
||||
*
|
||||
* @author Timothy Prinzing
|
||||
*/
|
||||
public class GapContent extends GapVector implements AbstractDocument.Content, Serializable {
|
||||
|
||||
/**
|
||||
* Creates a new GapContent object. Initial size defaults to 10.
|
||||
*/
|
||||
public GapContent() {
|
||||
this(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GapContent object, with the initial
|
||||
* size specified. The initial size will not be allowed
|
||||
* to go below 2, to give room for the implied break and
|
||||
* the gap.
|
||||
*
|
||||
* @param initialLength the initial size
|
||||
*/
|
||||
public GapContent(int initialLength) {
|
||||
super(Math.max(initialLength,2));
|
||||
char[] implied = new char[1];
|
||||
implied[0] = '\n';
|
||||
replace(0, 0, implied, implied.length);
|
||||
|
||||
marks = new MarkVector();
|
||||
search = new MarkData(0);
|
||||
queue = new ReferenceQueue<StickyPosition>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an array to store items of the type
|
||||
* appropriate (which is determined by the subclass).
|
||||
*/
|
||||
protected Object allocateArray(int len) {
|
||||
return new char[len];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the allocated array.
|
||||
*/
|
||||
protected int getArrayLength() {
|
||||
char[] carray = (char[]) getArray();
|
||||
return carray.length;
|
||||
}
|
||||
|
||||
// --- AbstractDocument.Content methods -------------------------
|
||||
|
||||
/**
|
||||
* Returns the length of the content.
|
||||
*
|
||||
* @return the length >= 1
|
||||
* @see AbstractDocument.Content#length
|
||||
*/
|
||||
public int length() {
|
||||
int len = getArrayLength() - (getGapEnd() - getGapStart());
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a string into the content.
|
||||
*
|
||||
* @param where the starting position >= 0, < length()
|
||||
* @param str the non-null string to insert
|
||||
* @return an UndoableEdit object for undoing
|
||||
* @exception BadLocationException if the specified position is invalid
|
||||
* @see AbstractDocument.Content#insertString
|
||||
*/
|
||||
public UndoableEdit insertString(int where, String str) throws BadLocationException {
|
||||
if (where > length() || where < 0) {
|
||||
throw new BadLocationException("Invalid insert", length());
|
||||
}
|
||||
char[] chars = str.toCharArray();
|
||||
replace(where, 0, chars, chars.length);
|
||||
return new InsertUndo(where, str.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes part of the content.
|
||||
*
|
||||
* @param where the starting position >= 0, where + nitems < length()
|
||||
* @param nitems the number of characters to remove >= 0
|
||||
* @return an UndoableEdit object for undoing
|
||||
* @exception BadLocationException if the specified position is invalid
|
||||
* @see AbstractDocument.Content#remove
|
||||
*/
|
||||
public UndoableEdit remove(int where, int nitems) throws BadLocationException {
|
||||
if (where + nitems >= length()) {
|
||||
throw new BadLocationException("Invalid remove", length() + 1);
|
||||
}
|
||||
String removedString = getString(where, nitems);
|
||||
UndoableEdit edit = new RemoveUndo(where, removedString);
|
||||
replace(where, nitems, empty, 0);
|
||||
return edit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a portion of the content.
|
||||
*
|
||||
* @param where the starting position >= 0
|
||||
* @param len the length to retrieve >= 0
|
||||
* @return a string representing the content
|
||||
* @exception BadLocationException if the specified position is invalid
|
||||
* @see AbstractDocument.Content#getString
|
||||
*/
|
||||
public String getString(int where, int len) throws BadLocationException {
|
||||
Segment s = new Segment();
|
||||
getChars(where, len, s);
|
||||
return new String(s.array, s.offset, s.count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a portion of the content. If the desired content spans
|
||||
* the gap, we copy the content. If the desired content does not
|
||||
* span the gap, the actual store is returned to avoid the copy since
|
||||
* it is contiguous.
|
||||
*
|
||||
* @param where the starting position >= 0, where + len <= length()
|
||||
* @param len the number of characters to retrieve >= 0
|
||||
* @param chars the Segment object to return the characters in
|
||||
* @exception BadLocationException if the specified position is invalid
|
||||
* @see AbstractDocument.Content#getChars
|
||||
*/
|
||||
public void getChars(int where, int len, Segment chars) throws BadLocationException {
|
||||
int end = where + len;
|
||||
if (where < 0 || end < 0) {
|
||||
throw new BadLocationException("Invalid location", -1);
|
||||
}
|
||||
if (end > length() || where > length()) {
|
||||
throw new BadLocationException("Invalid location", length() + 1);
|
||||
}
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
char[] array = (char[]) getArray();
|
||||
if ((where + len) <= g0) {
|
||||
// below gap
|
||||
chars.array = array;
|
||||
chars.offset = where;
|
||||
} else if (where >= g0) {
|
||||
// above gap
|
||||
chars.array = array;
|
||||
chars.offset = g1 + where - g0;
|
||||
} else {
|
||||
// spans the gap
|
||||
int before = g0 - where;
|
||||
if (chars.isPartialReturn()) {
|
||||
// partial return allowed, return amount before the gap
|
||||
chars.array = array;
|
||||
chars.offset = where;
|
||||
chars.count = before;
|
||||
return;
|
||||
}
|
||||
// partial return not allowed, must copy
|
||||
chars.array = new char[len];
|
||||
chars.offset = 0;
|
||||
System.arraycopy(array, where, chars.array, 0, before);
|
||||
System.arraycopy(array, g1, chars.array, before, len - before);
|
||||
}
|
||||
chars.count = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a position within the content that will
|
||||
* track change as the content is mutated.
|
||||
*
|
||||
* @param offset the offset to track >= 0
|
||||
* @return the position
|
||||
* @exception BadLocationException if the specified position is invalid
|
||||
*/
|
||||
public Position createPosition(int offset) throws BadLocationException {
|
||||
while ( queue.poll() != null ) {
|
||||
unusedMarks++;
|
||||
}
|
||||
if (unusedMarks > Math.max(5, (marks.size() / 10))) {
|
||||
removeUnusedMarks();
|
||||
}
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
int index = (offset < g0) ? offset : offset + (g1 - g0);
|
||||
search.index = index;
|
||||
int sortIndex = findSortIndex(search);
|
||||
MarkData m;
|
||||
StickyPosition position;
|
||||
if (sortIndex < marks.size()
|
||||
&& (m = marks.elementAt(sortIndex)).index == index
|
||||
&& (position = m.getPosition()) != null) {
|
||||
//position references the correct StickyPostition
|
||||
} else {
|
||||
position = new StickyPosition();
|
||||
m = new MarkData(index,position,queue);
|
||||
position.setMark(m);
|
||||
marks.insertElementAt(m, sortIndex);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the data for a mark... separately from
|
||||
* the real mark so that the real mark (Position
|
||||
* that the caller of createPosition holds) can be
|
||||
* collected if there are no more references to
|
||||
* it. The update table holds only a reference
|
||||
* to this data.
|
||||
*/
|
||||
final class MarkData extends WeakReference<StickyPosition> {
|
||||
|
||||
MarkData(int index) {
|
||||
super(null);
|
||||
this.index = index;
|
||||
}
|
||||
MarkData(int index, StickyPosition position, ReferenceQueue<? super StickyPosition> queue) {
|
||||
super(position, queue);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the location in the contiguous sequence
|
||||
* being modeled. The index in the gap array
|
||||
* is held by the mark, so it is adjusted according
|
||||
* to it's relationship to the gap.
|
||||
*/
|
||||
public final int getOffset() {
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
int offs = (index < g0) ? index : index - (g1 - g0);
|
||||
return Math.max(offs, 0);
|
||||
}
|
||||
|
||||
StickyPosition getPosition() {
|
||||
return get();
|
||||
}
|
||||
int index;
|
||||
}
|
||||
|
||||
final class StickyPosition implements Position {
|
||||
|
||||
StickyPosition() {
|
||||
}
|
||||
|
||||
void setMark(MarkData mark) {
|
||||
this.mark = mark;
|
||||
}
|
||||
|
||||
public final int getOffset() {
|
||||
return mark.getOffset();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Integer.toString(getOffset());
|
||||
}
|
||||
|
||||
MarkData mark;
|
||||
}
|
||||
|
||||
// --- variables --------------------------------------
|
||||
|
||||
private static final char[] empty = new char[0];
|
||||
private transient MarkVector marks;
|
||||
|
||||
/**
|
||||
* Record used for searching for the place to
|
||||
* start updating mark indexs when the gap
|
||||
* boundaries are moved.
|
||||
*/
|
||||
private transient MarkData search;
|
||||
|
||||
/**
|
||||
* The number of unused mark entries
|
||||
*/
|
||||
private transient int unusedMarks = 0;
|
||||
|
||||
private transient ReferenceQueue<StickyPosition> queue;
|
||||
|
||||
final static int GROWTH_SIZE = 1024 * 512;
|
||||
|
||||
// --- gap management -------------------------------
|
||||
|
||||
/**
|
||||
* Make the gap bigger, moving any necessary data and updating
|
||||
* the appropriate marks
|
||||
*/
|
||||
protected void shiftEnd(int newSize) {
|
||||
int oldGapEnd = getGapEnd();
|
||||
|
||||
super.shiftEnd(newSize);
|
||||
|
||||
// Adjust marks.
|
||||
int dg = getGapEnd() - oldGapEnd;
|
||||
int adjustIndex = findMarkAdjustIndex(oldGapEnd);
|
||||
int n = marks.size();
|
||||
for (int i = adjustIndex; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
mark.index += dg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to make growth policy less agressive for large
|
||||
* text amount.
|
||||
*/
|
||||
int getNewArraySize(int reqSize) {
|
||||
if (reqSize < GROWTH_SIZE) {
|
||||
return super.getNewArraySize(reqSize);
|
||||
} else {
|
||||
return reqSize + GROWTH_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the start of the gap to a new location,
|
||||
* without changing the size of the gap. This
|
||||
* moves the data in the array and updates the
|
||||
* marks accordingly.
|
||||
*/
|
||||
protected void shiftGap(int newGapStart) {
|
||||
int oldGapStart = getGapStart();
|
||||
int dg = newGapStart - oldGapStart;
|
||||
int oldGapEnd = getGapEnd();
|
||||
int newGapEnd = oldGapEnd + dg;
|
||||
int gapSize = oldGapEnd - oldGapStart;
|
||||
|
||||
// shift gap in the character array
|
||||
super.shiftGap(newGapStart);
|
||||
|
||||
// update the marks
|
||||
if (dg > 0) {
|
||||
// Move gap up, move data and marks down.
|
||||
int adjustIndex = findMarkAdjustIndex(oldGapStart);
|
||||
int n = marks.size();
|
||||
for (int i = adjustIndex; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
if (mark.index >= newGapEnd) {
|
||||
break;
|
||||
}
|
||||
mark.index -= gapSize;
|
||||
}
|
||||
} else if (dg < 0) {
|
||||
// Move gap down, move data and marks up.
|
||||
int adjustIndex = findMarkAdjustIndex(newGapStart);
|
||||
int n = marks.size();
|
||||
for (int i = adjustIndex; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
if (mark.index >= oldGapEnd) {
|
||||
break;
|
||||
}
|
||||
mark.index += gapSize;
|
||||
}
|
||||
}
|
||||
resetMarksAtZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all the marks that have an offset of 0 to have an index of
|
||||
* zero as well.
|
||||
*/
|
||||
protected void resetMarksAtZero() {
|
||||
if (marks != null && getGapStart() == 0) {
|
||||
int g1 = getGapEnd();
|
||||
for (int counter = 0, maxCounter = marks.size();
|
||||
counter < maxCounter; counter++) {
|
||||
MarkData mark = marks.elementAt(counter);
|
||||
if (mark.index <= g1) {
|
||||
mark.index = 0;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the gap end downward. This doesn't move
|
||||
* any data, but it does update any marks affected
|
||||
* by the boundary change. All marks from the old
|
||||
* gap start down to the new gap start are squeezed
|
||||
* to the end of the gap (their location has been
|
||||
* removed).
|
||||
*/
|
||||
protected void shiftGapStartDown(int newGapStart) {
|
||||
// Push aside all marks from oldGapStart down to newGapStart.
|
||||
int adjustIndex = findMarkAdjustIndex(newGapStart);
|
||||
int n = marks.size();
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
for (int i = adjustIndex; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
if (mark.index > g0) {
|
||||
// no more marks to adjust
|
||||
break;
|
||||
}
|
||||
mark.index = g1;
|
||||
}
|
||||
|
||||
// shift the gap in the character array
|
||||
super.shiftGapStartDown(newGapStart);
|
||||
|
||||
resetMarksAtZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the gap end upward. This doesn't move
|
||||
* any data, but it does update any marks affected
|
||||
* by the boundary change. All marks from the old
|
||||
* gap end up to the new gap end are squeezed
|
||||
* to the end of the gap (their location has been
|
||||
* removed).
|
||||
*/
|
||||
protected void shiftGapEndUp(int newGapEnd) {
|
||||
int adjustIndex = findMarkAdjustIndex(getGapEnd());
|
||||
int n = marks.size();
|
||||
for (int i = adjustIndex; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
if (mark.index >= newGapEnd) {
|
||||
break;
|
||||
}
|
||||
mark.index = newGapEnd;
|
||||
}
|
||||
|
||||
// shift the gap in the character array
|
||||
super.shiftGapEndUp(newGapEnd);
|
||||
|
||||
resetMarksAtZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two marks.
|
||||
*
|
||||
* @param o1 the first object
|
||||
* @param o2 the second object
|
||||
* @return < 0 if o1 < o2, 0 if the same, > 0 if o1 > o2
|
||||
*/
|
||||
final int compare(MarkData o1, MarkData o2) {
|
||||
if (o1.index < o2.index) {
|
||||
return -1;
|
||||
} else if (o1.index > o2.index) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index to start mark adjustments given
|
||||
* some search index.
|
||||
*/
|
||||
final int findMarkAdjustIndex(int searchIndex) {
|
||||
search.index = Math.max(searchIndex, 1);
|
||||
int index = findSortIndex(search);
|
||||
|
||||
// return the first in the series
|
||||
// (ie. there may be duplicates).
|
||||
for (int i = index - 1; i >= 0; i--) {
|
||||
MarkData d = marks.elementAt(i);
|
||||
if (d.index != search.index) {
|
||||
break;
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index of where to insert a new mark.
|
||||
*
|
||||
* @param o the mark to insert
|
||||
* @return the index
|
||||
*/
|
||||
final int findSortIndex(MarkData o) {
|
||||
int lower = 0;
|
||||
int upper = marks.size() - 1;
|
||||
int mid = 0;
|
||||
|
||||
if (upper == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmp;
|
||||
MarkData last = marks.elementAt(upper);
|
||||
cmp = compare(o, last);
|
||||
if (cmp > 0)
|
||||
return upper + 1;
|
||||
|
||||
while (lower <= upper) {
|
||||
mid = lower + ((upper - lower) / 2);
|
||||
MarkData entry = marks.elementAt(mid);
|
||||
cmp = compare(o, entry);
|
||||
|
||||
if (cmp == 0) {
|
||||
// found a match
|
||||
return mid;
|
||||
} else if (cmp < 0) {
|
||||
upper = mid - 1;
|
||||
} else {
|
||||
lower = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find it, but we indicate the index of where it would belong.
|
||||
return (cmp < 0) ? mid : mid + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all unused marks out of the sorted collection
|
||||
* of marks.
|
||||
*/
|
||||
final void removeUnusedMarks() {
|
||||
int n = marks.size();
|
||||
MarkVector cleaned = new MarkVector(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
MarkData mark = marks.elementAt(i);
|
||||
if (mark.get() != null) {
|
||||
cleaned.addElement(mark);
|
||||
}
|
||||
}
|
||||
marks = cleaned;
|
||||
unusedMarks = 0;
|
||||
}
|
||||
|
||||
|
||||
static class MarkVector extends GapVector {
|
||||
|
||||
MarkVector() {
|
||||
super();
|
||||
}
|
||||
|
||||
MarkVector(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an array to store items of the type
|
||||
* appropriate (which is determined by the subclass).
|
||||
*/
|
||||
protected Object allocateArray(int len) {
|
||||
return new MarkData[len];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the allocated array
|
||||
*/
|
||||
protected int getArrayLength() {
|
||||
MarkData[] marks = (MarkData[]) getArray();
|
||||
return marks.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of marks currently held
|
||||
*/
|
||||
public int size() {
|
||||
int len = getArrayLength() - (getGapEnd() - getGapStart());
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a mark into the vector
|
||||
*/
|
||||
public void insertElementAt(MarkData m, int index) {
|
||||
oneMark[0] = m;
|
||||
replace(index, 0, oneMark, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mark to the end
|
||||
*/
|
||||
public void addElement(MarkData m) {
|
||||
insertElementAt(m, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the mark at the given index
|
||||
*/
|
||||
public MarkData elementAt(int index) {
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
MarkData[] array = (MarkData[]) getArray();
|
||||
if (index < g0) {
|
||||
// below gap
|
||||
return array[index];
|
||||
} else {
|
||||
// above gap
|
||||
index += g1 - g0;
|
||||
return array[index];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the elements in the specified range with the passed
|
||||
* in objects. This will NOT adjust the gap. The passed in indices
|
||||
* do not account for the gap, they are the same as would be used
|
||||
* int <code>elementAt</code>.
|
||||
*/
|
||||
protected void replaceRange(int start, int end, Object[] marks) {
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
int index = start;
|
||||
int newIndex = 0;
|
||||
Object[] array = (Object[]) getArray();
|
||||
if (start >= g0) {
|
||||
// Completely passed gap
|
||||
index += (g1 - g0);
|
||||
end += (g1 - g0);
|
||||
}
|
||||
else if (end >= g0) {
|
||||
// straddles gap
|
||||
end += (g1 - g0);
|
||||
while (index < g0) {
|
||||
array[index++] = marks[newIndex++];
|
||||
}
|
||||
index = g1;
|
||||
}
|
||||
else {
|
||||
// below gap
|
||||
while (index < end) {
|
||||
array[index++] = marks[newIndex++];
|
||||
}
|
||||
}
|
||||
while (index < end) {
|
||||
array[index++] = marks[newIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
MarkData[] oneMark = new MarkData[1];
|
||||
|
||||
}
|
||||
|
||||
// --- serialization -------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws ClassNotFoundException, IOException {
|
||||
s.defaultReadObject();
|
||||
marks = new MarkVector();
|
||||
search = new MarkData(0);
|
||||
queue = new ReferenceQueue<StickyPosition>();
|
||||
}
|
||||
|
||||
|
||||
// --- undo support --------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a Vector containing instances of UndoPosRef for the
|
||||
* Positions in the range
|
||||
* <code>offset</code> to <code>offset</code> + <code>length</code>.
|
||||
* If <code>v</code> is not null the matching Positions are placed in
|
||||
* there. The vector with the resulting Positions are returned.
|
||||
*
|
||||
* @param v the Vector to use, with a new one created on null
|
||||
* @param offset the starting offset >= 0
|
||||
* @param length the length >= 0
|
||||
* @return the set of instances
|
||||
*/
|
||||
protected Vector getPositionsInRange(Vector v, int offset, int length) {
|
||||
int endOffset = offset + length;
|
||||
int startIndex;
|
||||
int endIndex;
|
||||
int g0 = getGapStart();
|
||||
int g1 = getGapEnd();
|
||||
|
||||
// Find the index of the marks.
|
||||
if (offset < g0) {
|
||||
if (offset == 0) {
|
||||
// findMarkAdjustIndex start at 1!
|
||||
startIndex = 0;
|
||||
}
|
||||
else {
|
||||
startIndex = findMarkAdjustIndex(offset);
|
||||
}
|
||||
if (endOffset >= g0) {
|
||||
endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
|
||||
}
|
||||
else {
|
||||
endIndex = findMarkAdjustIndex(endOffset + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
startIndex = findMarkAdjustIndex(offset + (g1 - g0));
|
||||
endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
|
||||
}
|
||||
|
||||
Vector placeIn = (v == null) ? new Vector(Math.max(1, endIndex -
|
||||
startIndex)) : v;
|
||||
|
||||
for (int counter = startIndex; counter < endIndex; counter++) {
|
||||
placeIn.addElement(new UndoPosRef(marks.elementAt(counter)));
|
||||
}
|
||||
return placeIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the location for all the UndoPosRef instances
|
||||
* in <code>positions</code>.
|
||||
* <p>
|
||||
* This is meant for internal usage, and is generally not of interest
|
||||
* to subclasses.
|
||||
*
|
||||
* @param positions the UndoPosRef instances to reset
|
||||
*/
|
||||
protected void updateUndoPositions(Vector positions, int offset,
|
||||
int length) {
|
||||
// Find the indexs of the end points.
|
||||
int endOffset = offset + length;
|
||||
int g1 = getGapEnd();
|
||||
int startIndex;
|
||||
int endIndex = findMarkAdjustIndex(g1 + 1);
|
||||
|
||||
if (offset != 0) {
|
||||
startIndex = findMarkAdjustIndex(g1);
|
||||
}
|
||||
else {
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
// Reset the location of the refenences.
|
||||
for(int counter = positions.size() - 1; counter >= 0; counter--) {
|
||||
UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
|
||||
ref.resetLocation(endOffset, g1);
|
||||
}
|
||||
// We have to resort the marks in the range startIndex to endIndex.
|
||||
// We can take advantage of the fact that it will be in
|
||||
// increasing order, accept there will be a bunch of MarkData's with
|
||||
// the index g1 (or 0 if offset == 0) interspersed throughout.
|
||||
if (startIndex < endIndex) {
|
||||
Object[] sorted = new Object[endIndex - startIndex];
|
||||
int addIndex = 0;
|
||||
int counter;
|
||||
if (offset == 0) {
|
||||
// If the offset is 0, the positions won't have incremented,
|
||||
// have to do the reverse thing.
|
||||
// Find the elements in startIndex whose index is 0
|
||||
for (counter = startIndex; counter < endIndex; counter++) {
|
||||
MarkData mark = marks.elementAt(counter);
|
||||
if (mark.index == 0) {
|
||||
sorted[addIndex++] = mark;
|
||||
}
|
||||
}
|
||||
for (counter = startIndex; counter < endIndex; counter++) {
|
||||
MarkData mark = marks.elementAt(counter);
|
||||
if (mark.index != 0) {
|
||||
sorted[addIndex++] = mark;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (counter = startIndex; counter < endIndex; counter++) {
|
||||
MarkData mark = marks.elementAt(counter);
|
||||
if (mark.index != g1) {
|
||||
sorted[addIndex++] = mark;
|
||||
}
|
||||
}
|
||||
for (counter = startIndex; counter < endIndex; counter++) {
|
||||
MarkData mark = marks.elementAt(counter);
|
||||
if (mark.index == g1) {
|
||||
sorted[addIndex++] = mark;
|
||||
}
|
||||
}
|
||||
}
|
||||
// And replace
|
||||
marks.replaceRange(startIndex, endIndex, sorted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to hold a reference to a Mark that is being reset as the
|
||||
* result of removing from the content.
|
||||
*/
|
||||
final class UndoPosRef {
|
||||
UndoPosRef(MarkData rec) {
|
||||
this.rec = rec;
|
||||
this.undoLocation = rec.getOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the location of the Position to the offset when the
|
||||
* receiver was instantiated.
|
||||
*
|
||||
* @param endOffset end location of inserted string.
|
||||
* @param g1 resulting end of gap.
|
||||
*/
|
||||
protected void resetLocation(int endOffset, int g1) {
|
||||
if (undoLocation != endOffset) {
|
||||
this.rec.index = undoLocation;
|
||||
}
|
||||
else {
|
||||
this.rec.index = g1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Previous Offset of rec. */
|
||||
protected int undoLocation;
|
||||
/** Mark to reset offset. */
|
||||
protected MarkData rec;
|
||||
} // End of GapContent.UndoPosRef
|
||||
|
||||
|
||||
/**
|
||||
* UnoableEdit created for inserts.
|
||||
*/
|
||||
class InsertUndo extends AbstractUndoableEdit {
|
||||
protected InsertUndo(int offset, int length) {
|
||||
super();
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void undo() throws CannotUndoException {
|
||||
super.undo();
|
||||
try {
|
||||
// Get the Positions in the range being removed.
|
||||
posRefs = getPositionsInRange(null, offset, length);
|
||||
string = getString(offset, length);
|
||||
remove(offset, length);
|
||||
} catch (BadLocationException bl) {
|
||||
throw new CannotUndoException();
|
||||
}
|
||||
}
|
||||
|
||||
public void redo() throws CannotRedoException {
|
||||
super.redo();
|
||||
try {
|
||||
insertString(offset, string);
|
||||
string = null;
|
||||
// Update the Positions that were in the range removed.
|
||||
if(posRefs != null) {
|
||||
updateUndoPositions(posRefs, offset, length);
|
||||
posRefs = null;
|
||||
}
|
||||
} catch (BadLocationException bl) {
|
||||
throw new CannotRedoException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Where string was inserted. */
|
||||
protected int offset;
|
||||
/** Length of string inserted. */
|
||||
protected int length;
|
||||
/** The string that was inserted. This will only be valid after an
|
||||
* undo. */
|
||||
protected String string;
|
||||
/** An array of instances of UndoPosRef for the Positions in the
|
||||
* range that was removed, valid after undo. */
|
||||
protected Vector posRefs;
|
||||
} // GapContent.InsertUndo
|
||||
|
||||
|
||||
/**
|
||||
* UndoableEdit created for removes.
|
||||
*/
|
||||
class RemoveUndo extends AbstractUndoableEdit {
|
||||
protected RemoveUndo(int offset, String string) {
|
||||
super();
|
||||
this.offset = offset;
|
||||
this.string = string;
|
||||
this.length = string.length();
|
||||
posRefs = getPositionsInRange(null, offset, length);
|
||||
}
|
||||
|
||||
public void undo() throws CannotUndoException {
|
||||
super.undo();
|
||||
try {
|
||||
insertString(offset, string);
|
||||
// Update the Positions that were in the range removed.
|
||||
if(posRefs != null) {
|
||||
updateUndoPositions(posRefs, offset, length);
|
||||
posRefs = null;
|
||||
}
|
||||
string = null;
|
||||
} catch (BadLocationException bl) {
|
||||
throw new CannotUndoException();
|
||||
}
|
||||
}
|
||||
|
||||
public void redo() throws CannotRedoException {
|
||||
super.redo();
|
||||
try {
|
||||
string = getString(offset, length);
|
||||
// Get the Positions in the range being removed.
|
||||
posRefs = getPositionsInRange(null, offset, length);
|
||||
remove(offset, length);
|
||||
} catch (BadLocationException bl) {
|
||||
throw new CannotRedoException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Where the string was removed from. */
|
||||
protected int offset;
|
||||
/** Length of string removed. */
|
||||
protected int length;
|
||||
/** The string that was removed. This is valid when redo is valid. */
|
||||
protected String string;
|
||||
/** An array of instances of UndoPosRef for the Positions in the
|
||||
* range that was removed, valid before undo. */
|
||||
protected Vector posRefs;
|
||||
} // GapContent.RemoveUndo
|
||||
}
|
||||
Reference in New Issue
Block a user