46 |
import javax.swing.undo.UndoableEdit; |
import javax.swing.undo.UndoableEdit; |
47 |
|
|
48 |
/** |
/** |
49 |
* This implementation of {@link AbstractDocument.Content} uses a gapped |
* This implementation of {@link AbstractDocument.Content} uses a gapped buffer. |
50 |
* buffer. This takes advantage of the fact that text area content is |
* This takes advantage of the fact that text area content is mostly inserted |
51 |
* mostly inserted sequentially. The buffer is a char array that maintains |
* sequentially. The buffer is a char array that maintains a gap at the current |
52 |
* a gap at the current insertion point. If characters a inserted at |
* insertion point. If characters a inserted at gap boundaries, the cost is |
53 |
* gap boundaries, the cost is minimal (simple array access). The array only |
* minimal (simple array access). The array only has to be shifted around when |
54 |
* has to be shifted around when the insertion point moves (then the gap also |
* the insertion point moves (then the gap also moves and one array copy is |
55 |
* moves and one array copy is necessary) or when the gap is filled up and |
* necessary) or when the gap is filled up and the buffer has to be enlarged. |
56 |
* the buffer has to be enlarged. |
* |
|
* |
|
57 |
* TODO: Implement UndoableEdit support stuff |
* TODO: Implement UndoableEdit support stuff |
58 |
*/ |
*/ |
59 |
public class GapContent |
public class GapContent |
60 |
implements AbstractDocument.Content, Serializable |
implements AbstractDocument.Content, Serializable |
61 |
{ |
{ |
62 |
|
|
63 |
/** |
/** |
64 |
* A {@link Position} implementation for <code>GapContent</code>. |
* A {@link Position} implementation for <code>GapContent</code>. |
65 |
*/ |
*/ |
66 |
class GapContentPosition implements Position, Comparable |
class GapContentPosition |
67 |
{ |
implements Position, Comparable |
68 |
|
{ |
69 |
/** The index within the buffer array. */ |
|
70 |
int mark; |
/** The index within the buffer array. */ |
71 |
|
int mark; |
72 |
/** |
|
73 |
* Creates a new GapContentPosition object. |
/** |
74 |
* |
* Creates a new GapContentPosition object. |
75 |
* @param mark the mark of this Position |
* |
76 |
*/ |
* @param mark the mark of this Position |
77 |
GapContentPosition(int mark) |
*/ |
78 |
{ |
GapContentPosition(int mark) |
79 |
this.mark = mark; |
{ |
80 |
} |
this.mark = mark; |
81 |
|
} |
|
/** |
|
|
* 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. |
|
|
*/ |
|
|
static final int DEFAULT_BUFSIZE = 64; |
|
|
|
|
|
/** |
|
|
* The text buffer. |
|
|
*/ |
|
|
char[] buffer; |
|
|
|
|
|
/** |
|
|
* The index of the first character of the gap. |
|
|
*/ |
|
|
int gapStart; |
|
|
|
|
|
/** |
|
|
* The index of the character after the last character of the gap. |
|
|
*/ |
|
|
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() |
|
|
{ |
|
|
this(DEFAULT_BUFSIZE); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Creates a new GapContent object with a specified initial size. |
|
|
* |
|
|
* @param size the initial size of the buffer |
|
|
*/ |
|
|
public GapContent(int size) |
|
|
{ |
|
|
buffer = (char[]) allocateArray(size); |
|
|
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) |
|
|
{ |
|
|
return new char[size]; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Returns the length of the allocated buffer array. |
|
|
* |
|
|
* @return the length of the allocated buffer array |
|
|
*/ |
|
|
protected int getArrayLength() |
|
|
{ |
|
|
return buffer.length; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Returns the length of the content. |
|
|
* |
|
|
* @return the length of the content |
|
|
*/ |
|
|
public int length() |
|
|
{ |
|
|
return buffer.length - (gapEnd - gapStart); |
|
|
} |
|
|
|
|
|
/** |
|
|
* 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 |
|
|
*/ |
|
|
public UndoableEdit insertString(int where, String str) |
|
|
throws BadLocationException |
|
|
{ |
|
|
// check arguments |
|
|
int length = length(); |
|
|
int strLen = str.length(); |
|
|
|
|
|
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); |
|
|
|
|
|
// are we at the gap boundary? |
|
|
if (where != gapStart) |
|
|
shiftGap(where); |
|
|
|
|
|
// 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 |
|
|
*/ |
|
|
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); |
|
|
if ((where + nitems) > length) |
|
|
throw new BadLocationException("where + nitems cannot be greater" |
|
|
+ " than the content length", |
|
|
where + nitems); |
|
|
|
|
|
// check if we are at the gap boundary |
|
|
if (where != gapStart) |
|
|
shiftGap(where); |
|
|
|
|
|
// 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); |
|
|
} |
|
|
|
|
|
/** |
|
|
* 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. |
|
|
* |
|
|
* @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 |
|
|
{ |
|
|
// check arguments |
|
|
int length = length(); |
|
|
if (where >= length) |
|
|
throw new BadLocationException("the where argument cannot be greater" |
|
|
+ " than the content length", where); |
|
|
if ((where + len) > length) |
|
|
throw new BadLocationException("len plus where cannot be greater" |
|
|
+ " than the content length", |
|
|
len + where); |
|
82 |
|
|
83 |
// check if requested segment is contiguous |
/** |
84 |
if ((where < gapStart) && ((gapStart - where) < len)) |
* Comparable interface implementation. This is used to store all |
85 |
|
* positions in an ordered fashion. |
86 |
|
* |
87 |
|
* @param o the object to be compared to this |
88 |
|
* |
89 |
|
* @return a negative integer if this is less than <code>o</code>, zero |
90 |
|
* if both are equal or a positive integer if this is greater than |
91 |
|
* <code>o</code> |
92 |
|
* |
93 |
|
* @throws ClassCastException if <code>o</code> is not a |
94 |
|
* GapContentPosition or Integer object |
95 |
|
*/ |
96 |
|
public int compareTo(Object o) |
97 |
{ |
{ |
98 |
// requested segment is not contiguous -> copy the pieces together |
if (o instanceof Integer) |
99 |
char[] copy = new char[len]; |
{ |
100 |
int lenFirst = gapStart - where; // the length of the first segment |
int otherMark = ((Integer) o).intValue(); |
101 |
System.arraycopy(buffer, where, copy, 0, lenFirst); |
return mark - otherMark; |
102 |
System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst); |
} |
103 |
txt.array = copy; |
else |
104 |
txt.offset = 0; |
{ |
105 |
txt.count = len; |
GapContentPosition other = (GapContentPosition) o; |
106 |
|
return mark - other.mark; |
107 |
|
} |
108 |
} |
} |
109 |
else |
|
110 |
|
/** |
111 |
|
* Returns the current offset of this Position within the content. |
112 |
|
* |
113 |
|
* @return the current offset of this Position within the content. |
114 |
|
*/ |
115 |
|
public int getOffset() |
116 |
{ |
{ |
117 |
// requested segment is contiguous -> we can simply return the |
if (mark <= gapStart) |
118 |
// actual content |
return mark; |
119 |
txt.array = buffer; |
else |
120 |
if (where < gapStart) |
return mark - (gapEnd - gapStart); |
|
txt.offset = where; |
|
|
else |
|
|
txt.offset = where + (gapEnd - gapStart); |
|
|
txt.count = len; |
|
121 |
} |
} |
122 |
} |
} |
123 |
|
|
124 |
|
private static final long serialVersionUID = 8374645204155842629L; |
125 |
|
|
126 |
|
/** |
127 |
|
* This is the default buffer size and the amount of bytes that a buffer is |
128 |
|
* extended if it is full. |
129 |
|
*/ |
130 |
|
static final int DEFAULT_BUFSIZE = 64; |
131 |
|
|
132 |
|
/** |
133 |
|
* The text buffer. |
134 |
|
*/ |
135 |
|
char[] buffer; |
136 |
|
|
137 |
|
/** |
138 |
|
* The index of the first character of the gap. |
139 |
|
*/ |
140 |
|
int gapStart; |
141 |
|
|
142 |
|
/** |
143 |
|
* The index of the character after the last character of the gap. |
144 |
|
*/ |
145 |
|
int gapEnd; |
146 |
|
|
147 |
|
/** |
148 |
|
* The positions generated by this GapContent. They are kept in an ordered |
149 |
|
* fashion, so they can be looked up easily. |
150 |
|
*/ |
151 |
|
LinkedList positions; |
152 |
|
|
153 |
|
/** |
154 |
|
* Creates a new GapContent object. |
155 |
|
*/ |
156 |
|
public GapContent() |
157 |
|
{ |
158 |
|
this(DEFAULT_BUFSIZE); |
159 |
|
} |
160 |
|
|
161 |
|
/** |
162 |
|
* Creates a new GapContent object with a specified initial size. |
163 |
|
* |
164 |
|
* @param size the initial size of the buffer |
165 |
|
*/ |
166 |
|
public GapContent(int size) |
167 |
|
{ |
168 |
|
buffer = (char[]) allocateArray(size); |
169 |
|
gapStart = 0; |
170 |
|
gapEnd = size - 1; |
171 |
|
buffer[size - 1] = '\n'; |
172 |
|
positions = new LinkedList(); |
173 |
|
} |
174 |
|
|
175 |
|
/** |
176 |
|
* Allocates an array of the specified length that can then be used as |
177 |
|
* buffer. |
178 |
|
* |
179 |
|
* @param size the size of the array to be allocated |
180 |
|
* |
181 |
|
* @return the allocated array |
182 |
|
*/ |
183 |
|
protected Object allocateArray(int size) |
184 |
|
{ |
185 |
|
return new char[size]; |
186 |
|
} |
187 |
|
|
188 |
|
/** |
189 |
|
* Returns the length of the allocated buffer array. |
190 |
|
* |
191 |
|
* @return the length of the allocated buffer array |
192 |
|
*/ |
193 |
|
protected int getArrayLength() |
194 |
|
{ |
195 |
|
return buffer.length; |
196 |
|
} |
197 |
|
|
198 |
|
/** |
199 |
|
* Returns the length of the content. |
200 |
|
* |
201 |
|
* @return the length of the content |
202 |
|
*/ |
203 |
|
public int length() |
204 |
|
{ |
205 |
|
return buffer.length - (gapEnd - gapStart); |
206 |
|
} |
207 |
|
|
208 |
|
/** |
209 |
|
* Inserts a string at the specified position. |
210 |
|
* |
211 |
|
* @param where the position where the string is inserted |
212 |
|
* @param str the string that is to be inserted |
213 |
|
* |
214 |
|
* @return an UndoableEdit object (currently not supported, so |
215 |
|
* <code>null</code> is returned) |
216 |
|
* |
217 |
|
* @throws BadLocationException if <code>where</code> is not a valid |
218 |
|
* location in the buffer |
219 |
|
*/ |
220 |
|
public UndoableEdit insertString(int where, String str) |
221 |
|
throws BadLocationException |
222 |
|
{ |
223 |
|
// check arguments |
224 |
|
int length = length(); |
225 |
|
int strLen = str.length(); |
226 |
|
|
227 |
|
if (where >= length) |
228 |
|
throw new BadLocationException("the where argument cannot be greater" |
229 |
|
+ " than the content length", where); |
230 |
|
|
231 |
|
// check if the gap is big enough to hold the string |
232 |
|
if ((gapEnd - gapStart) < strLen) |
233 |
|
// make room for this string and some more |
234 |
|
shiftEnd(strLen + DEFAULT_BUFSIZE); |
235 |
|
|
236 |
|
// are we at the gap boundary? |
237 |
|
if (where != gapStart) |
238 |
|
shiftGap(where); |
239 |
|
|
240 |
|
// now we can simple copy the string into the gap and adjust the |
241 |
|
// gap boundaries |
242 |
|
System.arraycopy(str.toCharArray(), 0, buffer, gapStart, strLen); |
243 |
|
gapStart += strLen; |
244 |
|
return null; |
245 |
|
} |
246 |
|
|
247 |
|
/** |
248 |
|
* Removes a piece of content at th specified position. |
249 |
|
* |
250 |
|
* @param where the position where the content is to be removed |
251 |
|
* @param nitems number of characters to be removed |
252 |
|
* |
253 |
|
* @return an UndoableEdit object (currently not supported, so |
254 |
|
* <code>null</code> is returned) |
255 |
|
* |
256 |
|
* @throws BadLocationException if <code>where</code> is not a valid |
257 |
|
* location in the buffer |
258 |
|
*/ |
259 |
|
public UndoableEdit remove(int where, int nitems) |
260 |
|
throws BadLocationException |
261 |
|
{ |
262 |
|
// check arguments |
263 |
|
int length = length(); |
264 |
|
|
265 |
|
if (where >= length) |
266 |
|
throw new BadLocationException("the where argument cannot be greater" |
267 |
|
+ " than the content length", where); |
268 |
|
if ((where + nitems) > length) |
269 |
|
throw new BadLocationException("where + nitems cannot be greater" |
270 |
|
+ " than the content length", where + nitems); |
271 |
|
|
272 |
|
// check if we are at the gap boundary |
273 |
|
if (where != gapStart) |
274 |
|
shiftGap(where); |
275 |
|
|
276 |
|
// now we simply have to enlarge the gap |
277 |
|
gapEnd += nitems; |
278 |
|
return null; |
279 |
|
} |
280 |
|
|
281 |
|
/** |
282 |
|
* Returns a piece of content as String. |
283 |
|
* |
284 |
|
* @param where the start location of the fragment |
285 |
|
* @param len the length of the fragment |
286 |
|
* |
287 |
|
* @throws BadLocationException if <code>where</code> or |
288 |
|
* <code>where + len</code> are no valid locations in the buffer |
289 |
|
*/ |
290 |
|
public String getString(int where, int len) throws BadLocationException |
291 |
|
{ |
292 |
|
Segment seg = new Segment(); |
293 |
|
getChars(where, len, seg); |
294 |
|
return new String(seg.array, seg.offset, seg.count); |
295 |
|
} |
296 |
|
|
297 |
|
/** |
298 |
|
* Fetches a piece of content and stores it in a {@link Segment} object. |
299 |
|
* |
300 |
|
* If the requested piece of text spans the gap, the content is copied into a |
301 |
|
* new array. If it doesn't then it is contiguous and the actual content |
302 |
|
* store is returned. |
303 |
|
* |
304 |
|
* @param where the start location of the fragment |
305 |
|
* @param len the length of the fragment |
306 |
|
* @param txt the Segment object to store the fragment in |
307 |
|
* |
308 |
|
* @throws BadLocationException if <code>where</code> or |
309 |
|
* <code>where + len</code> are no valid locations in the buffer |
310 |
|
*/ |
311 |
|
public void getChars(int where, int len, Segment txt) |
312 |
|
throws BadLocationException |
313 |
|
{ |
314 |
|
// check arguments |
315 |
|
int length = length(); |
316 |
|
if (where >= length) |
317 |
|
throw new BadLocationException("the where argument cannot be greater" |
318 |
|
+ " than the content length", where); |
319 |
|
if ((where + len) > length) |
320 |
|
throw new BadLocationException("len plus where cannot be greater" |
321 |
|
+ " than the content length", len + where); |
322 |
|
|
323 |
/** |
// check if requested segment is contiguous |
324 |
* Creates and returns a mark at the specified position. |
if ((where < gapStart) && ((gapStart - where) < len)) |
|
* |
|
|
* @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 |
|
|
*/ |
|
|
public Position createPosition(final int offset) throws BadLocationException |
|
|
{ |
|
|
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. |
|
|
* |
|
|
* @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); |
|
|
gapEnd = gapStart + newSize; |
|
|
buffer = newBuf; |
|
|
|
|
|
// Update the marks after the gapEnd. |
|
|
int index = Collections.binarySearch(positions, |
|
|
new GapContentPosition(gapEnd)); |
|
|
if (index < 0) |
|
325 |
{ |
{ |
326 |
index = -(index + 1); |
// requested segment is not contiguous -> copy the pieces together |
327 |
|
char[] copy = new char[len]; |
328 |
|
int lenFirst = gapStart - where; // the length of the first segment |
329 |
|
System.arraycopy(buffer, where, copy, 0, lenFirst); |
330 |
|
System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst); |
331 |
|
txt.array = copy; |
332 |
|
txt.offset = 0; |
333 |
|
txt.count = len; |
334 |
} |
} |
335 |
for (ListIterator i = positions.listIterator(index); i.hasNext();) |
else |
336 |
{ |
{ |
337 |
GapContentPosition p = (GapContentPosition) i.next(); |
// requested segment is contiguous -> we can simply return the |
338 |
p.mark += delta; |
// actual content |
339 |
|
txt.array = buffer; |
340 |
|
if (where < gapStart) |
341 |
|
txt.offset = where; |
342 |
|
else |
343 |
|
txt.offset = where + (gapEnd - gapStart); |
344 |
|
txt.count = len; |
345 |
} |
} |
346 |
} |
} |
347 |
|
|
348 |
/** |
/** |
349 |
* Shifts the gap to the specified position. |
* Creates and returns a mark at the specified position. |
350 |
* |
* |
351 |
* @param newGapStart the new start position of the gap |
* @param offset the position at which to create the mark |
352 |
*/ |
* |
353 |
protected void shiftGap(int newGapStart) |
* @return the create Position object for the mark |
354 |
{ |
* |
355 |
int newGapEnd = newGapStart + (gapEnd - gapStart); |
* @throws BadLocationException if the offset is not a valid position in the |
356 |
|
* buffer |
357 |
|
*/ |
358 |
// Update the positions between newGapEnd and (old) gapEnd. The marks |
public Position createPosition(final int offset) throws BadLocationException |
359 |
// must be shifted by (gapEnd - newGapEnd). |
{ |
360 |
int index1 = Collections.binarySearch(positions, |
if (offset < 0 || offset > length()) |
361 |
new GapContentPosition(gapEnd)); |
throw new BadLocationException( |
362 |
int index2 = Collections.binarySearch(positions, |
"The offset was out of the bounds of this" + " buffer", offset); |
363 |
new GapContentPosition(newGapEnd)); |
|
364 |
if (index1 > 0 || index2 > 0) |
// We store the actual array index in the GapContentPosition. The real |
365 |
{ |
// offset is then calculated in the GapContentPosition. |
366 |
int i1 = Math.min(index1, index2); |
int mark = offset; |
367 |
int i2 = Math.max(index1, index2); |
if (offset > gapStart) |
368 |
for (ListIterator i = positions.listIterator(i1); i.hasNext();) |
mark += gapEnd - gapStart; |
369 |
|
GapContentPosition pos = new GapContentPosition(mark); |
370 |
|
|
371 |
|
// Add this into our list in a sorted fashion. |
372 |
|
int index = Collections.binarySearch(positions, pos); |
373 |
|
if (index < 0) |
374 |
|
index = -(index + 1); |
375 |
|
positions.add(index, pos); |
376 |
|
|
377 |
|
return pos; |
378 |
|
} |
379 |
|
|
380 |
|
/** |
381 |
|
* Enlarges the gap. This allocates a new bigger buffer array, copy the |
382 |
|
* segment before the gap as it is and the segment after the gap at the end |
383 |
|
* of the new buffer array. This does change the gapEnd mark but not the |
384 |
|
* gapStart mark. |
385 |
|
* |
386 |
|
* @param newSize the new size of the gap |
387 |
|
*/ |
388 |
|
protected void shiftEnd(int newSize) |
389 |
|
{ |
390 |
|
int delta = (gapEnd - gapStart) - newSize; |
391 |
|
char[] newBuf = (char[]) allocateArray(length() + newSize); |
392 |
|
System.arraycopy(buffer, 0, newBuf, 0, gapStart); |
393 |
|
System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, |
394 |
|
buffer.length - gapEnd); |
395 |
|
gapEnd = gapStart + newSize; |
396 |
|
buffer = newBuf; |
397 |
|
|
398 |
|
// Update the marks after the gapEnd. |
399 |
|
int index = Collections.binarySearch(positions, new GapContentPosition( |
400 |
|
gapEnd)); |
401 |
|
if (index < 0) |
402 |
{ |
{ |
403 |
if (i.nextIndex() > i2) |
index = -(index + 1); |
|
break; |
|
|
|
|
|
GapContentPosition p = (GapContentPosition) i.next(); |
|
|
p.mark += gapEnd - newGapEnd; |
|
404 |
} |
} |
405 |
|
for (ListIterator i = positions.listIterator(index); i.hasNext();) |
|
if (newGapStart == gapStart) |
|
|
return; |
|
|
else if (newGapStart < gapStart) |
|
406 |
{ |
{ |
407 |
System.arraycopy(buffer, newGapStart, buffer, newGapEnd, |
GapContentPosition p = (GapContentPosition) i.next(); |
408 |
gapStart - newGapStart); |
p.mark += delta; |
|
gapStart = newGapStart; |
|
|
gapEnd = newGapEnd; |
|
409 |
} |
} |
410 |
else |
} |
411 |
|
|
412 |
|
/** |
413 |
|
* Shifts the gap to the specified position. |
414 |
|
* |
415 |
|
* @param newGapStart the new start position of the gap |
416 |
|
*/ |
417 |
|
protected void shiftGap(int newGapStart) |
418 |
|
{ |
419 |
|
int newGapEnd = newGapStart + (gapEnd - gapStart); |
420 |
|
|
421 |
|
// Update the positions between newGapEnd and (old) gapEnd. The marks |
422 |
|
// must be shifted by (gapEnd - newGapEnd). |
423 |
|
int index1 = Collections.binarySearch(positions, new GapContentPosition( |
424 |
|
gapEnd)); |
425 |
|
int index2 = Collections.binarySearch(positions, new GapContentPosition( |
426 |
|
newGapEnd)); |
427 |
|
if (index1 > 0 || index2 > 0) |
428 |
{ |
{ |
429 |
System.arraycopy(buffer, gapEnd, buffer, gapStart, |
int i1 = Math.min(index1, index2); |
430 |
newGapStart - gapStart); |
int i2 = Math.max(index1, index2); |
431 |
gapStart = newGapStart; |
for (ListIterator i = positions.listIterator(i1); i.hasNext();) |
432 |
gapEnd = newGapEnd; |
{ |
433 |
|
if (i.nextIndex() > i2) |
434 |
|
break; |
435 |
|
|
436 |
|
GapContentPosition p = (GapContentPosition) i.next(); |
437 |
|
p.mark += gapEnd - newGapEnd; |
438 |
|
} |
439 |
|
|
440 |
|
if (newGapStart == gapStart) |
441 |
|
return; |
442 |
|
else if (newGapStart < gapStart) |
443 |
|
{ |
444 |
|
System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart |
445 |
|
- newGapStart); |
446 |
|
gapStart = newGapStart; |
447 |
|
gapEnd = newGapEnd; |
448 |
|
} |
449 |
|
else |
450 |
|
{ |
451 |
|
System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart |
452 |
|
- gapStart); |
453 |
|
gapStart = newGapStart; |
454 |
|
gapEnd = newGapEnd; |
455 |
|
} |
456 |
} |
} |
457 |
} |
} |
|
} |
|
458 |
|
|
459 |
/** |
/** |
460 |
* Returns the allocated buffer array. |
* Returns the allocated buffer array. |
461 |
* |
* |
462 |
* @return the allocated buffer array |
* @return the allocated buffer array |
463 |
*/ |
*/ |
464 |
protected Object getArray() |
protected Object getArray() |
465 |
{ |
{ |
466 |
return buffer; |
return buffer; |
467 |
} |
} |
468 |
|
|
469 |
/** |
/** |
470 |
* Replaces a portion of the storage with the specified items. |
* Replaces a portion of the storage with the specified items. |
471 |
* |
* |
472 |
* @param position the position at which to remove items |
* @param position the position at which to remove items |
473 |
* @param rmSize the number of items to remove |
* @param rmSize the number of items to remove |
474 |
* @param addItems the items to add at location |
* @param addItems the items to add at location |
475 |
* @param addSize the number of items to add |
* @param addSize the number of items to add |
476 |
*/ |
*/ |
477 |
protected void replace(int position, int rmSize, Object addItems, |
protected void replace(int position, int rmSize, Object addItems, int addSize) |
478 |
int addSize) |
{ |
479 |
{ |
// Remove content |
480 |
// Remove content |
shiftGap(position); |
481 |
shiftGap(position); |
gapEnd += rmSize; |
482 |
gapEnd += rmSize; |
|
483 |
|
// If gap is too small, enlarge the gap. |
484 |
// If gap is too small, enlarge the gap. |
if ((gapEnd - gapStart) < addSize) |
485 |
if ((gapEnd - gapStart) < addSize) |
shiftEnd(addSize); |
486 |
shiftEnd(addSize); |
|
487 |
|
// Add new items to the buffer. |
488 |
// Add new items to the buffer. |
System.arraycopy(addItems, 0, buffer, gapStart, addSize); |
489 |
System.arraycopy(addItems, 0, buffer, gapStart, addSize); |
gapStart += addSize; |
490 |
gapStart += addSize; |
} |
|
} |
|
491 |
} |
} |