KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > StringContent


1 /*
2  * @(#)StringContent.java 1.44 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.util.Vector JavaDoc;
10 import java.io.Serializable JavaDoc;
11 import javax.swing.undo.*;
12 import javax.swing.SwingUtilities JavaDoc;
13
14 /**
15  * An implementation of the AbstractDocument.Content interface that is
16  * a brute force implementation that is useful for relatively small
17  * documents and/or debugging. It manages the character content
18  * as a simple character array. It is also quite inefficient.
19  * <p>
20  * It is generally recommended that the gap buffer or piece table
21  * implementations be used instead. This buffer does not scale up
22  * to large sizes.
23  * <p>
24  * <strong>Warning:</strong>
25  * Serialized objects of this class will not be compatible with
26  * future Swing releases. The current serialization support is
27  * appropriate for short term storage or RMI between applications running
28  * the same version of Swing. As of 1.4, support for long term storage
29  * of all JavaBeans<sup><font size="-2">TM</font></sup>
30  * has been added to the <code>java.beans</code> package.
31  * Please see {@link java.beans.XMLEncoder}.
32  *
33  * @author Timothy Prinzing
34  * @version 1.44 12/19/03
35  */

36 public final class StringContent implements AbstractDocument.Content JavaDoc, Serializable JavaDoc {
37
38     /**
39      * Creates a new StringContent object. Initial size defaults to 10.
40      */

41     public StringContent() {
42     this(10);
43     }
44
45     /**
46      * Creates a new StringContent object, with the initial
47      * size specified. If the length is < 1, a size of 1 is used.
48      *
49      * @param initialLength the initial size
50      */

51     public StringContent(int initialLength) {
52     if (initialLength < 1) {
53         initialLength = 1;
54     }
55     data = new char[initialLength];
56     data[0] = '\n';
57     count = 1;
58     }
59
60     /**
61      * Returns the length of the content.
62      *
63      * @return the length >= 1
64      * @see AbstractDocument.Content#length
65      */

66     public int length() {
67     return count;
68     }
69
70     /**
71      * Inserts a string into the content.
72      *
73      * @param where the starting position >= 0 && < length()
74      * @param str the non-null string to insert
75      * @return an UndoableEdit object for undoing
76      * @exception BadLocationException if the specified position is invalid
77      * @see AbstractDocument.Content#insertString
78      */

79     public UndoableEdit insertString(int where, String JavaDoc str) throws BadLocationException JavaDoc {
80     if (where >= count || where < 0) {
81         throw new BadLocationException JavaDoc("Invalid location", count);
82     }
83     char[] chars = str.toCharArray();
84     replace(where, 0, chars, 0, chars.length);
85     if (marks != null) {
86         updateMarksForInsert(where, str.length());
87     }
88     return new InsertUndo(where, str.length());
89     }
90
91     /**
92      * Removes part of the content. where + nitems must be < length().
93      *
94      * @param where the starting position >= 0
95      * @param nitems the number of characters to remove >= 0
96      * @return an UndoableEdit object for undoing
97      * @exception BadLocationException if the specified position is invalid
98      * @see AbstractDocument.Content#remove
99      */

100     public UndoableEdit remove(int where, int nitems) throws BadLocationException JavaDoc {
101     if (where + nitems >= count) {
102         throw new BadLocationException JavaDoc("Invalid range", count);
103     }
104     String JavaDoc removedString = getString(where, nitems);
105     UndoableEdit edit = new RemoveUndo(where, removedString);
106     replace(where, nitems, empty, 0, 0);
107     if (marks != null) {
108         updateMarksForRemove(where, nitems);
109     }
110     return edit;
111     
112     }
113
114     /**
115      * Retrieves a portion of the content. where + len must be <= length().
116      *
117      * @param where the starting position >= 0
118      * @param len the length to retrieve >= 0
119      * @return a string representing the content; may be empty
120      * @exception BadLocationException if the specified position is invalid
121      * @see AbstractDocument.Content#getString
122      */

123     public String JavaDoc getString(int where, int len) throws BadLocationException JavaDoc {
124     if (where + len > count) {
125         throw new BadLocationException JavaDoc("Invalid range", count);
126     }
127     return new String JavaDoc(data, where, len);
128     }
129
130     /**
131      * Retrieves a portion of the content. where + len must be <= length()
132      *
133      * @param where the starting position >= 0
134      * @param len the number of characters to retrieve >= 0
135      * @param chars the Segment object to return the characters in
136      * @exception BadLocationException if the specified position is invalid
137      * @see AbstractDocument.Content#getChars
138      */

139     public void getChars(int where, int len, Segment JavaDoc chars) throws BadLocationException JavaDoc {
140     if (where + len > count) {
141         throw new BadLocationException JavaDoc("Invalid location", count);
142     }
143     chars.array = data;
144     chars.offset = where;
145     chars.count = len;
146     }
147
148     /**
149      * Creates a position within the content that will
150      * track change as the content is mutated.
151      *
152      * @param offset the offset to create a position for >= 0
153      * @return the position
154      * @exception BadLocationException if the specified position is invalid
155      */

156     public Position JavaDoc createPosition(int offset) throws BadLocationException JavaDoc {
157     // some small documents won't have any sticky positions
158
// at all, so the buffer is created lazily.
159
if (marks == null) {
160         marks = new Vector JavaDoc();
161     }
162     return new StickyPosition(offset);
163     }
164
165     // --- local methods ---------------------------------------
166

167     /**
168      * Replaces some of the characters in the array
169      * @param offset offset into the array to start the replace
170      * @param length number of characters to remove
171      * @param replArray replacement array
172      * @param replOffset offset into the replacement array
173      * @param replLength number of character to use from the
174      * replacement array.
175      */

176     void replace(int offset, int length,
177          char[] replArray, int replOffset, int replLength) {
178     int delta = replLength - length;
179     int src = offset + length;
180     int nmove = count - src;
181     int dest = src + delta;
182     if ((count + delta) >= data.length) {
183         // need to grow the array
184
int newLength = Math.max(2*data.length, count + delta);
185         char[] newData = new char[newLength];
186         System.arraycopy(data, 0, newData, 0, offset);
187         System.arraycopy(replArray, replOffset, newData, offset, replLength);
188         System.arraycopy(data, src, newData, dest, nmove);
189         data = newData;
190     } else {
191         // patch the existing array
192
System.arraycopy(data, src, data, dest, nmove);
193         System.arraycopy(replArray, replOffset, data, offset, replLength);
194     }
195     count = count + delta;
196     }
197
198     void resize(int ncount) {
199     char[] ndata = new char[ncount];
200     System.arraycopy(data, 0, ndata, 0, Math.min(ncount, count));
201     data = ndata;
202     }
203
204     synchronized void updateMarksForInsert(int offset, int length) {
205     if (offset == 0) {
206         // zero is a special case where we update only
207
// marks after it.
208
offset = 1;
209     }
210     int n = marks.size();
211     for (int i = 0; i < n; i++) {
212         PosRec mark = (PosRec) marks.elementAt(i);
213         if (mark.unused) {
214         // this record is no longer used, get rid of it
215
marks.removeElementAt(i);
216         i -= 1;
217         n -= 1;
218         } else if (mark.offset >= offset) {
219         mark.offset += length;
220         }
221     }
222     }
223
224     synchronized void updateMarksForRemove(int offset, int length) {
225     int n = marks.size();
226     for (int i = 0; i < n; i++) {
227         PosRec mark = (PosRec) marks.elementAt(i);
228         if (mark.unused) {
229         // this record is no longer used, get rid of it
230
marks.removeElementAt(i);
231         i -= 1;
232         n -= 1;
233         } else if (mark.offset >= (offset + length)) {
234         mark.offset -= length;
235         } else if (mark.offset >= offset) {
236         mark.offset = offset;
237         }
238     }
239     }
240
241     /**
242      * Returns a Vector containing instances of UndoPosRef for the
243      * Positions in the range
244      * <code>offset</code> to <code>offset</code> + <code>length</code>.
245      * If <code>v</code> is not null the matching Positions are placed in
246      * there. The vector with the resulting Positions are returned.
247      * <p>
248      * This is meant for internal usage, and is generally not of interest
249      * to subclasses.
250      *
251      * @param v the Vector to use, with a new one created on null
252      * @param offset the starting offset >= 0
253      * @param length the length >= 0
254      * @return the set of instances
255      */

256     protected Vector JavaDoc getPositionsInRange(Vector JavaDoc v, int offset,
257                               int length) {
258     int n = marks.size();
259     int end = offset + length;
260     Vector JavaDoc placeIn = (v == null) ? new Vector JavaDoc() : v;
261     for (int i = 0; i < n; i++) {
262         PosRec mark = (PosRec) marks.elementAt(i);
263         if (mark.unused) {
264         // this record is no longer used, get rid of it
265
marks.removeElementAt(i);
266         i -= 1;
267         n -= 1;
268         } else if(mark.offset >= offset && mark.offset <= end)
269         placeIn.addElement(new UndoPosRef(mark));
270     }
271     return placeIn;
272     }
273
274     /**
275      * Resets the location for all the UndoPosRef instances
276      * in <code>positions</code>.
277      * <p>
278      * This is meant for internal usage, and is generally not of interest
279      * to subclasses.
280      *
281      * @param positions the positions of the instances
282      */

283     protected void updateUndoPositions(Vector JavaDoc positions) {
284     for(int counter = positions.size() - 1; counter >= 0; counter--) {
285         UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
286         // Check if the Position is still valid.
287
if(ref.rec.unused) {
288         positions.removeElementAt(counter);
289         }
290         else
291         ref.resetLocation();
292     }
293     }
294
295     private static final char[] empty = new char[0];
296     private char[] data;
297     private int count;
298     transient Vector JavaDoc marks;
299
300     /**
301      * holds the data for a mark... separately from
302      * the real mark so that the real mark can be
303      * collected if there are no more references to
304      * it.... the update table holds only a reference
305      * to this grungy thing.
306      */

307     final class PosRec {
308
309     PosRec(int offset) {
310         this.offset = offset;
311     }
312
313     int offset;
314     boolean unused;
315     }
316
317     /**
318      * This really wants to be a weak reference but
319      * in 1.1 we don't have a 100% pure solution for
320      * this... so this class trys to hack a solution
321      * to causing the marks to be collected.
322      */

323     final class StickyPosition implements Position JavaDoc {
324
325     StickyPosition(int offset) {
326         rec = new PosRec(offset);
327         marks.addElement(rec);
328     }
329
330         public int getOffset() {
331         return rec.offset;
332     }
333
334     protected void finalize() throws Throwable JavaDoc {
335         // schedule the record to be removed later
336
// on another thread.
337
rec.unused = true;
338     }
339
340         public String JavaDoc toString() {
341         return Integer.toString(getOffset());
342     }
343
344     PosRec rec;
345     }
346
347     /**
348      * Used to hold a reference to a Position that is being reset as the
349      * result of removing from the content.
350      */

351     final class UndoPosRef {
352     UndoPosRef(PosRec rec) {
353         this.rec = rec;
354         this.undoLocation = rec.offset;
355     }
356
357     /**
358      * Resets the location of the Position to the offset when the
359      * receiver was instantiated.
360      */

361     protected void resetLocation() {
362         rec.offset = undoLocation;
363     }
364
365     /** Location to reset to when resetLocatino is invoked. */
366     protected int undoLocation;
367     /** Position to reset offset. */
368     protected PosRec rec;
369     }
370
371     /**
372      * UnoableEdit created for inserts.
373      */

374     class InsertUndo extends AbstractUndoableEdit {
375     protected InsertUndo(int offset, int length) {
376         super();
377         this.offset = offset;
378         this.length = length;
379     }
380
381     public void undo() throws CannotUndoException {
382         super.undo();
383         try {
384         synchronized(StringContent.this) {
385             // Get the Positions in the range being removed.
386
if(marks != null)
387             posRefs = getPositionsInRange(null, offset, length);
388             string = getString(offset, length);
389             remove(offset, length);
390         }
391         } catch (BadLocationException JavaDoc bl) {
392           throw new CannotUndoException();
393         }
394     }
395
396     public void redo() throws CannotRedoException {
397         super.redo();
398         try {
399         synchronized(StringContent.this) {
400             insertString(offset, string);
401             string = null;
402             // Update the Positions that were in the range removed.
403
if(posRefs != null) {
404             updateUndoPositions(posRefs);
405             posRefs = null;
406             }
407           }
408         } catch (BadLocationException JavaDoc bl) {
409           throw new CannotRedoException();
410         }
411     }
412
413     // Where the string goes.
414
protected int offset;
415     // Length of the string.
416
protected int length;
417     // The string that was inserted. To cut down on space needed this
418
// will only be valid after an undo.
419
protected String JavaDoc string;
420     // An array of instances of UndoPosRef for the Positions in the
421
// range that was removed, valid after undo.
422
protected Vector JavaDoc posRefs;
423     }
424
425
426     /**
427      * UndoableEdit created for removes.
428      */

429     class RemoveUndo extends AbstractUndoableEdit {
430     protected RemoveUndo(int offset, String JavaDoc string) {
431         super();
432         this.offset = offset;
433         this.string = string;
434         this.length = string.length();
435         if(marks != null)
436         posRefs = getPositionsInRange(null, offset, length);
437     }
438
439     public void undo() throws CannotUndoException {
440         super.undo();
441         try {
442         synchronized(StringContent.this) {
443             insertString(offset, string);
444             // Update the Positions that were in the range removed.
445
if(posRefs != null) {
446             updateUndoPositions(posRefs);
447             posRefs = null;
448             }
449             string = null;
450         }
451         } catch (BadLocationException JavaDoc bl) {
452           throw new CannotUndoException();
453         }
454     }
455
456     public void redo() throws CannotRedoException {
457         super.redo();
458         try {
459         synchronized(StringContent.this) {
460             string = getString(offset, length);
461             // Get the Positions in the range being removed.
462
if(marks != null)
463             posRefs = getPositionsInRange(null, offset, length);
464             remove(offset, length);
465         }
466         } catch (BadLocationException JavaDoc bl) {
467           throw new CannotRedoException();
468         }
469     }
470
471     // Where the string goes.
472
protected int offset;
473     // Length of the string.
474
protected int length;
475     // The string that was inserted. This will be null after an undo.
476
protected String JavaDoc string;
477     // An array of instances of UndoPosRef for the Positions in the
478
// range that was removed, valid before undo.
479
protected Vector JavaDoc posRefs;
480     }
481 }
482
Popular Tags