KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > DocumentContent


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.List JavaDoc;
25 import javax.swing.text.Segment JavaDoc;
26 import javax.swing.text.BadLocationException JavaDoc;
27 import javax.swing.text.Position JavaDoc;
28 import javax.swing.text.AbstractDocument JavaDoc;
29 import javax.swing.undo.AbstractUndoableEdit JavaDoc;
30 import javax.swing.undo.CannotUndoException JavaDoc;
31 import javax.swing.undo.CannotRedoException JavaDoc;
32 import javax.swing.undo.UndoableEdit JavaDoc;
33
34 /**
35  * Content of the document.
36  *
37  * @author Miloslav Metelka
38  * @version 1.00
39  */

40
41 final class DocumentContent implements AbstractDocument.Content JavaDoc, CharSeq, GapStart {
42     
43     private static final char[] EMPTY_CHAR_ARRAY = new char[0];
44
45     /**
46      * Invalid undoable edit being used to mark that the line undo was already
47      * processed. It must never be undone/redone as it's used in a flyweight
48      * way but the undomanager's operation changes states of undoable edits
49      * being undone/redone.
50      */

51     private static final UndoableEdit JavaDoc INVALID_EDIT = new AbstractUndoableEdit JavaDoc();
52     
53     private static final boolean debugUndo
54             = Boolean.getBoolean("netbeans.debug.editor.document.undo");
55         
56     /** Vector holding the marks for the document */
57     private final MarkVector markVector;
58     
59     /** Array with gap holding the text of the document */
60     private char[] charArray;
61
62     /** Start index of the gap */
63     private int gapStart;
64
65     /** Length of the gap */
66     private int gapLength;
67     
68     DocumentContent() {
69         charArray = EMPTY_CHAR_ARRAY;
70         markVector = new MarkVector();
71         
72         // Insert implied '\n'
73
insertText(0, "\n"); // NOI18N
74
}
75     
76     public final int getGapStart() { // to implement GapStart
77
return gapStart;
78     }
79
80     public UndoableEdit JavaDoc insertString(int offset, String JavaDoc text)
81     throws BadLocationException JavaDoc {
82
83         checkBounds(offset, 0, length() - 1);
84         return new Edit JavaDoc(offset, text);
85     }
86     
87     public UndoableEdit JavaDoc remove(int offset, int length)
88     throws BadLocationException JavaDoc {
89
90         checkBounds(offset, length, length() - 1);
91         return new Edit JavaDoc(offset, length);
92     }
93
94     public Position JavaDoc createPosition(int offset) throws BadLocationException JavaDoc {
95         return new BasePosition(createMark(offset));
96     }
97
98     public Position JavaDoc createBiasPosition(int offset, Position.Bias JavaDoc bias)
99     throws BadLocationException JavaDoc {
100         return new BasePosition(createBiasMark(offset, bias));
101     }
102
103     MultiMark createBiasMark(int offset, Position.Bias JavaDoc bias) throws BadLocationException JavaDoc {
104         checkOffset(offset);
105         return markVector.insert(markVector.createBiasMark(offset, bias));
106     }
107
108     MultiMark createMark(int offset) throws BadLocationException JavaDoc {
109         checkOffset(offset);
110         return markVector.insert(markVector.createMark(offset));
111     }
112
113     public int length() {
114         return charArray.length - gapLength;
115     }
116     
117     public void getChars(int offset, int length, Segment JavaDoc chars)
118     throws BadLocationException JavaDoc {
119
120         checkBounds(offset, length, length());
121         
122         if ((offset + length) <= gapStart) { // completely below gap
123
chars.array = charArray;
124             chars.offset = offset;
125             
126         } else if (offset >= gapStart) { // completely above gap
127
chars.array = charArray;
128             chars.offset = offset + gapLength;
129             
130         } else { // spans the gap, must copy
131
chars.array = copySpanChars(offset, length);
132             chars.offset = 0;
133         }
134         
135         chars.count = length;
136     }
137
138     public String JavaDoc getString(int offset, int length)
139     throws BadLocationException JavaDoc {
140
141         checkBounds(offset, length, length());
142         return getText(offset, length);
143     }
144     
145     String JavaDoc getText(int offset, int length) {
146         if (offset < 0 || length < 0) {
147             throw new IllegalStateException JavaDoc("offset=" + offset + ", length=" + length); // NOI18N
148
}
149
150         String JavaDoc ret;
151         if ((offset + length) <= gapStart) { // completely below gap
152
ret = new String JavaDoc(charArray, offset, length);
153             
154         } else if (offset >= gapStart) { // completely above gap
155
ret = new String JavaDoc(charArray, offset + gapLength, length);
156             
157         } else { // spans the gap, must copy
158
ret = new String JavaDoc(copySpanChars(offset, length));
159         }
160         
161         return ret;
162     }
163
164     public char charAt(int index) {
165         return charArray[getRawIndex(index)];
166     }
167     
168     void compact() {
169         if (gapLength > 0) {
170             int newLength = charArray.length - gapLength;
171             char[] newCharArray = new char[newLength];
172             int gapEnd = gapStart + gapLength;
173             System.arraycopy(charArray, 0, newCharArray, 0, gapStart);
174             System.arraycopy(charArray, gapEnd, newCharArray, gapStart,
175                 charArray.length - gapEnd);
176             charArray = newCharArray;
177             gapStart = charArray.length;
178             gapLength = 0;
179         }
180         
181         markVector.compact();
182     }
183     
184     private int getRawIndex(int index) {
185         return (index < gapStart) ? index : (index + gapLength);
186     }
187     
188     private void moveGap(int index) {
189         if (index <= gapStart) { // move gap down
190
int moveSize = gapStart - index;
191             System.arraycopy(charArray, index, charArray,
192                 gapStart + gapLength - moveSize, moveSize);
193             gapStart = index;
194
195         } else { // above gap
196
int gapEnd = gapStart + gapLength;
197             int moveSize = index - gapStart;
198             System.arraycopy(charArray, gapEnd, charArray, gapStart, moveSize);
199             gapStart += moveSize;
200         }
201     }
202     
203     private void enlargeGap(int extraLength) {
204         int newLength = Math.max(10, charArray.length * 3 / 2 + extraLength);
205         int gapEnd = gapStart + gapLength;
206         int afterGapLength = (charArray.length - gapEnd);
207         int newGapEnd = newLength - afterGapLength;
208         char[] newCharArray = new char[newLength];
209         System.arraycopy(charArray, 0, newCharArray, 0, gapStart);
210         System.arraycopy(charArray, gapEnd, newCharArray, newGapEnd, afterGapLength);
211         charArray = newCharArray;
212         gapLength = newGapEnd - gapStart;
213     }
214
215     private char[] copyChars(int offset, int length) {
216         char[] ret;
217         if ((offset + length) <= gapStart) { // completely below gap
218
ret = new char[length];
219             System.arraycopy(charArray, offset, ret, 0, length);
220             
221         } else if (offset >= gapStart) { // completely above gap
222
ret = new char[length];
223             System.arraycopy(charArray, offset + gapLength, ret, 0, length);
224             
225         } else { // spans the gap, must copy
226
ret = copySpanChars(offset, length);
227         }
228         
229         return ret;
230     }
231
232     private char[] copySpanChars(int offset, int length) {
233         char[] ret = new char[length];
234         int belowGap = gapStart - offset;
235         System.arraycopy(charArray, offset, ret, 0, belowGap);
236         System.arraycopy(charArray, gapStart + gapLength,
237             ret, belowGap, length - belowGap);
238         return ret;
239     }
240
241     void insertText(int offset, String JavaDoc text) {
242         ///*DEBUG*/System.err.println("DocumentContent.insertText(" + offset + ", \"" + text + "\")");
243
int textLength = text.length();
244         int extraLength = textLength - gapLength;
245         if (extraLength > 0) {
246             enlargeGap(extraLength);
247         }
248         if (offset != gapStart) {
249             moveGap(offset);
250         }
251         text.getChars(0, textLength, charArray, gapStart);
252         gapStart += textLength;
253         gapLength -= textLength;
254     }
255     
256     void removeText(int offset, int length) {
257         ///*DEBUG*/System.err.println("DocumentContent.removeText(" + offset + ", " + length + ")");
258
if (offset >= gapStart) { // completely over gap
259
if (offset > gapStart) {
260                 moveGap(offset);
261             }
262
263         } else { // completely below gap or spans the gap
264
int endOffset = offset + length;
265             if (endOffset <= gapStart) {
266                 if (endOffset < gapStart) {
267                     moveGap(endOffset);
268                 }
269                 gapStart -= length;
270                 
271             } else { // spans gap
272
gapStart = offset;
273             }
274         }
275
276         gapLength += length;
277     }
278     
279     private void checkOffset(int offset) throws BadLocationException JavaDoc {
280         if (offset > length()) { // can be doc.getLength() + 1 i.e. getEndPosition()
281
throw new BadLocationException JavaDoc("Invalid offset=" + offset // I18N // NOI18N
282
+ ", docLength=" + (length() - 1), offset); // I18N // NOI18N
283
}
284     }
285
286     private void checkBounds(int offset, int length, int limitOffset)
287     throws BadLocationException JavaDoc {
288
289     if (offset < 0) {
290         throw new BadLocationException JavaDoc("Invalid offset=" + offset, offset); // NOI18N
291
}
292         if (length < 0) {
293             throw new BadLocationException JavaDoc("Invalid length" + length, length); // NOI18N
294
}
295     if (offset + length > limitOffset) {
296         throw new BadLocationException JavaDoc(
297                 "docLength=" + (length() - 1) // NOI18N
298
+ ": Invalid offset" // NOI18N
299
+ ((length != 0) ? "+length" : "") // NOI18N
300
+ "=" + (offset + length), // NOI18N
301
(offset + length)
302             );
303     }
304     }
305     
306     class Edit extends AbstractUndoableEdit JavaDoc {
307         
308         /** Constructor used for insert.
309          * @param offset offset of insert.
310          * @param text inserted text.
311          */

312         Edit(int offset, String JavaDoc text) {
313             this.offset = offset;
314             this.length = text.length();
315             this.text = text;
316
317             undoOrRedo(length, false); // pretend redo
318

319         }
320         
321         /** Constructor used for remove.
322          * @param offset offset of remove.
323          * @param length length of the removed text.
324          */

325         Edit(int offset, int length) {
326             this.offset = offset;
327             this.length = -length;
328             
329             // Added to make sure the text is not inited later at unappropriate time
330
this.text = getText(offset, length);
331
332             undoOrRedo(-length, false); // pretend redo
333

334         }
335         
336         private int offset;
337         
338         private int length;
339         
340         private String JavaDoc text;
341         
342         private MarkVector.Undo markVectorUndo;
343         
344         public void undo() throws CannotUndoException JavaDoc {
345             super.undo();
346
347             if (debugUndo) {
348                 /*DEBUG*/System.err.println("UNDO-" + dump()); // NOI18N
349
}
350             undoOrRedo(-length, true);
351         }
352         
353         public void redo() throws CannotRedoException JavaDoc {
354             super.redo();
355
356             if (debugUndo) {
357                 /*DEBUG*/System.err.println("REDO-" + dump()); // NOI18N
358
}
359             undoOrRedo(length, false);
360         }
361         
362         private String JavaDoc dump() {
363             return ((length >= 0) ? "INSERT" : "REMOVE") // NOI18N
364
+ ":offset=" + offset + ", length=" + length // NOI18N
365
+ ", text='" + text + '\''; // NOI18N
366
}
367         
368         private void undoOrRedo(int len, boolean undo) {
369             // Fix text content
370
if (len < 0) { // do remove
371
removeText(offset, -len);
372             } else { // do insert
373
insertText(offset, text);
374             }
375             
376             // Update marks
377
markVectorUndo = markVector.update(offset, len, markVectorUndo);
378         }
379
380         /**
381          * @return text of the modification.
382          */

383         final String JavaDoc getUndoRedoText() {
384             return text;
385         }
386         
387     }
388 }
389
Popular Tags