KickJava   Java API By Example, From Geeks To Geeks.

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


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.beans.PropertyVetoException JavaDoc;
23 import java.beans.VetoableChangeListener JavaDoc;
24 import java.util.Hashtable JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Dictionary JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.io.Reader JavaDoc;
30 import java.io.Writer JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.beans.PropertyChangeListener JavaDoc;
33 import java.beans.PropertyChangeEvent JavaDoc;
34 import java.util.EventListener JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.logging.Logger JavaDoc;
37 import javax.swing.event.DocumentListener JavaDoc;
38 import javax.swing.event.UndoableEditListener JavaDoc;
39 import javax.swing.text.BadLocationException JavaDoc;
40 import javax.swing.text.DefaultEditorKit JavaDoc;
41 import javax.swing.text.Position JavaDoc;
42 import javax.swing.text.Element JavaDoc;
43 import javax.swing.text.AttributeSet JavaDoc;
44 import javax.swing.text.AbstractDocument JavaDoc;
45 import javax.swing.text.StyleConstants JavaDoc;
46 import javax.swing.event.DocumentEvent JavaDoc;
47 import javax.swing.event.UndoableEditEvent JavaDoc;
48 import javax.swing.text.Segment JavaDoc;
49 import javax.swing.undo.AbstractUndoableEdit JavaDoc;
50 import javax.swing.undo.UndoableEdit JavaDoc;
51 import javax.swing.undo.CompoundEdit JavaDoc;
52 import javax.swing.undo.CannotUndoException JavaDoc;
53 import javax.swing.undo.CannotRedoException JavaDoc;
54 import org.netbeans.lib.editor.util.swing.DocumentListenerPriority;
55
56 /**
57 * Document implementation
58 *
59 * @author Miloslav Metelka
60 * @version 1.00
61 */

62
63 public class BaseDocument extends AbstractDocument JavaDoc implements SettingsChangeListener, AtomicLockDocument {
64
65     private static final Logger JavaDoc LOG = Logger.getLogger(BaseDocument.class.getName());
66     
67     /** Registry identification property */
68     public static final String JavaDoc ID_PROP = "id"; // NOI18N
69

70     /** Line separator property for reading files in */
71     public static final String JavaDoc READ_LINE_SEPARATOR_PROP
72     = DefaultEditorKit.EndOfLineStringProperty;
73
74     /** Line separator property for writing content into files. If not set
75      * the writing defaults to the READ_LINE_SEPARATOR_PROP.
76      */

77     public static final String JavaDoc WRITE_LINE_SEPARATOR_PROP
78     = "write-line-separator"; // NOI18N
79

80     /** File name property */
81     public static final String JavaDoc FILE_NAME_PROP = "file-name"; // NOI18N
82

83     /** Wrap search mark property */
84     public static final String JavaDoc WRAP_SEARCH_MARK_PROP = "wrap-search-mark"; // NOI18N
85

86     /** Undo manager property. This can be used to implement undo
87     * in a simple way. Default undo and redo actions try to get this
88     * property and perform undo and redo through it.
89     */

90     public static final String JavaDoc UNDO_MANAGER_PROP = "undo-manager"; // NOI18N
91

92     /** Kit class property. This can become useful for getting
93     * the settings that logicaly belonging to the document.
94     */

95     public static final String JavaDoc KIT_CLASS_PROP = "kit-class"; // NOI18N
96

97     /** String forward finder property */
98     public static final String JavaDoc STRING_FINDER_PROP = "string-finder"; // NOI18N
99

100     /** String backward finder property */
101     public static final String JavaDoc STRING_BWD_FINDER_PROP = "string-bwd-finder"; // NOI18N
102

103     /** Highlight search finder property. */
104     public static final String JavaDoc BLOCKS_FINDER_PROP = "blocks-finder"; // NOI18N
105

106     /** Maximum line width encountered during the initial read operation.
107     * This is filled by Analyzer and used by UI to set the correct initial width
108     * of the component.
109     * Values: java.lang.Integer
110     */

111     public static final String JavaDoc LINE_LIMIT_PROP = "line-limit"; // NOI18N
112

113
114     /** Size of the line batch. Line batch can be used at various places
115     * especially when processing lines by syntax scanner.
116     */

117     public static final String JavaDoc LINE_BATCH_SIZE = "line-batch-size"; // NOI18N
118

119     /** Line separator is marked by CR (Macintosh) */
120     public static final String JavaDoc LS_CR = "\r"; // NOI18N
121

122     /** Line separator is marked by LF (Unix) */
123     public static final String JavaDoc LS_LF = "\n"; // NOI18N
124

125     /** Line separator is marked by CR and LF (Windows) */
126     public static final String JavaDoc LS_CRLF = "\r\n"; // NOI18N
127

128     /**
129      * Document property holding either null or VetoableChangeListener
130      * that should be notified prior insert/remove or atomicLock()
131      * starts outside of a document lock.
132      */

133     private static final String JavaDoc BEFORE_MODIFICATION_LISTENER = "modificationListener"; // NOI18N
134

135     /** Maximum of concurrent read threads (other will wait until
136     * one of these will leave).
137     */

138     private static final int MAX_READ_THREADS = 10;
139
140     /** Write lock without write lock */
141     private static final String JavaDoc WRITE_LOCK_MISSING
142     = "extWriteUnlock() without extWriteLock()"; // NOI18N
143

144     private static final Object JavaDoc annotationsLock = new Object JavaDoc();
145     private static final Object JavaDoc getVisColFromPosLock = new Object JavaDoc();
146     private static final Object JavaDoc getOffsetFromVisColLock = new Object JavaDoc();
147
148     /** Debug modifications performed on the document */
149     private static final boolean debug
150         = Boolean.getBoolean("netbeans.debug.editor.document"); // NOI18N
151
/** Debug the stack of calling of the insert/remove */
152     private static final boolean debugStack
153         = Boolean.getBoolean("netbeans.debug.editor.document.stack"); // NOI18N
154
/** Debug the document insert/remove but do not output text inserted/removed */
155     private static final boolean debugNoText
156         = Boolean.getBoolean("netbeans.debug.editor.document.notext"); // NOI18N
157

158     /** Debug the StreamDescriptionProperty during read() */
159     private static final boolean debugRead
160         = Boolean.getBoolean("netbeans.debug.editor.document.read"); // NOI18N
161

162     public static final ThreadLocal JavaDoc THREAD_LOCAL_LOCK_DEPTH = new ThreadLocal JavaDoc();
163     private static final Integer JavaDoc[] lockIntegers
164         = new Integer JavaDoc[] {
165             null,
166             new Integer JavaDoc(1),
167             new Integer JavaDoc(2),
168             new Integer JavaDoc(3)
169         };
170
171     /** How many spaces should be displayed instead of '\t' character */
172     private int tabSize = SettingsDefaults.defaultTabSize.intValue();
173
174     /** Size of one indentation level. If this variable is null (value
175      * is not set in Settings, then the default algorithm will be used.
176      */

177     private Integer JavaDoc shiftWidth;
178
179     /** How many times current writer requested writing */
180     private int writeDepth;
181
182     /** How many times atomic writer requested writing */
183     private int atomicDepth;
184
185     /* Was the document initialized by reading? */
186     protected boolean inited;
187
188     /* Was the document modified by doing inert/remove */
189     protected boolean modified;
190
191     /** Listener to changes in find support */
192     PropertyChangeListener JavaDoc findSupportListener;
193
194     /** Default element - lazily inited */
195     protected Element JavaDoc defaultRootElem;
196
197     private SyntaxSupport syntaxSupport;
198
199     /** Layer list for document level layers */
200     private DrawLayerList drawLayerList = new DrawLayerList();
201
202     /** Reset merging next created undoable edit to the last one. */
203     boolean undoMergeReset;
204
205     /** Kit class stored here */
206     Class JavaDoc kitClass;
207
208     /** Undo event for atomic events is fired after the successful
209     * atomic operation is finished. The changes are stored in this variable
210     * during the atomic operation. If the operation is broken, these edits
211     * are used to restore previous state.
212     */

213     private AtomicCompoundEdit atomicEdits;
214
215     private Acceptor identifierAcceptor;
216
217     private Acceptor whitespaceAcceptor;
218
219     private ArrayList JavaDoc syntaxList = new ArrayList JavaDoc();
220
221     /** List of the positions used by storePosition() */
222     private ArrayList JavaDoc posList = new ArrayList JavaDoc();
223
224     /** List of the integers marking the free positions in the posList. */
225     private ArrayList JavaDoc posFreeList = new ArrayList JavaDoc();
226
227     /** Root element of line elements representation */
228     protected LineRootElement lineRootElement;
229
230     /** Last document event to be undone. The field is filled
231      * by the lastly done modification undoable edit.
232      * BaseDocumentEvent.canUndo() checks this flag.
233      */

234     UndoableEdit JavaDoc lastModifyUndoEdit; // #8692 check last modify undo edit
235

236     /** List of annotations for this document. */
237     private Annotations annotations;
238
239     /* Bug #6258 during composing I18N text using input method the Undoable Edit
240      * actions must be disabled so only the final push (and not all intermediate
241      * ones) of the I18N word will be stored as undoable edit.
242      */

243      private boolean composedText = false;
244     
245     /**
246      * Map of [multi-mark, Mark-instance] pairs.
247      * These multi-marks need to be stored separately and not be undone/redone.
248      */

249     final Map JavaDoc marks = new HashMap JavaDoc();
250     final MarkVector marksStorage = new MarkVector();
251
252     /** Finder for visual x-coord to position conversion */
253     private FinderFactory.VisColPosFwdFinder visColPosFwdFinder;
254
255     /** Finder for position to x-coord conversion */
256     private FinderFactory.PosVisColFwdFinder posVisColFwdFinder;
257     
258     /** Atomic lock event instance shared by all the atomic lock firings done for this document */
259     private AtomicLockEvent atomicLockEventInstance = new AtomicLockEvent(this);
260     
261     private FixLineSyntaxState fixLineSyntaxState;
262     private UndoableEdit JavaDoc removeUpdateLineUndo;
263
264     private Object JavaDoc[] atomicLockListenerList;
265
266     /**
267      * ThreadLocal variable holding status of notification about subsequent
268      * modification. It must be notified prior acquiring of the document's lock.
269      */

270     private final ThreadLocal JavaDoc STATUS = new ThreadLocal JavaDoc ();
271     
272     private boolean modsUndoneOrRedone;
273     
274     private DocumentListener JavaDoc postModificationDocumentListener;
275     
276     private Position JavaDoc lastPositionEditedByTyping = null;
277     
278     /** Create base document with a specified syntax.
279     * @param kitClass class used to initialize this document with proper settings
280     * category based on the editor kit for which this document is created
281     * @param syntax syntax scanner to use with this document
282     */

283     public BaseDocument(Class JavaDoc kitClass, boolean addToRegistry) {
284         super(new DocumentContent());
285         this.kitClass = kitClass;
286
287         setDocumentProperties(createDocumentProperties(getDocumentProperties()));
288         super.addDocumentListener(
289                 org.netbeans.lib.editor.util.swing.DocumentUtilities.initPriorityListening(this));
290
291         putProperty(GapStart.class, getDocumentContent());
292         putProperty("supportsModificationListener", Boolean.TRUE); // NOI18N
293

294         lineRootElement = new LineRootElement(this);
295
296         settingsChange(null); // initialize variables from settings
297
Settings.addSettingsChangeListener(this);
298
299         // Line separators default to platform ones
300
putProperty(READ_LINE_SEPARATOR_PROP, Analyzer.getPlatformLS());
301
302         // Additional initialization of the document through the kit
303
BaseKit kit = BaseKit.getKit(kitClass);
304         if (kit != null) {
305             kit.initDocument(this);
306         }
307
308         // Possibly add the document to registry
309
if (addToRegistry) {
310             Registry.addDocument(this); // add if created thru the kit
311
}
312
313         // Start listen on find-support
314
findSupportListener = new PropertyChangeListener JavaDoc() {
315                                   public void propertyChange(PropertyChangeEvent JavaDoc evt) {
316                                       findSupportChange(evt);
317                                   }
318                               };
319         FindSupport.getFindSupport().addPropertyChangeListener(findSupportListener);
320         findSupportChange(null); // update doc by find settings
321
}
322     
323     private DocumentContent getDocumentContent() {
324         return (DocumentContent)getContent();
325     }
326     
327     public CharSeq getText() {
328         return getDocumentContent();
329     }
330
331     private void findSupportChange(PropertyChangeEvent JavaDoc evt) {
332         // set all finders to null
333
putProperty(STRING_FINDER_PROP, null);
334         putProperty(STRING_BWD_FINDER_PROP, null);
335         putProperty(BLOCKS_FINDER_PROP, null);
336     }
337
338     /** Called when settings were changed. The method is called
339     * also in constructor, so the code must count with the evt being null.
340     */

341     public void settingsChange(SettingsChangeEvent evt) {
342         String JavaDoc settingName = (evt != null) ? evt.getSettingName() : null;
343
344         if (settingName == null || SettingsNames.TAB_SIZE.equals(settingName)) {
345             tabSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.TAB_SIZE,
346                           SettingsDefaults.defaultTabSize);
347         }
348
349         if (settingName == null || SettingsNames.INDENT_SHIFT_WIDTH.equals(settingName)) {
350             Object JavaDoc shw = Settings.getValue(kitClass, SettingsNames.INDENT_SHIFT_WIDTH);
351             if (shw instanceof Integer JavaDoc) { // currently only Integer values are supported
352
shiftWidth = (Integer JavaDoc)shw;
353             }
354         }
355         
356         if (settingName == null || SettingsNames.READ_BUFFER_SIZE.equals(settingName)) {
357             int readBufferSize = SettingsUtil.getPositiveInteger(kitClass,
358                                  SettingsNames.READ_BUFFER_SIZE, SettingsDefaults.defaultReadBufferSize);
359             putProperty(SettingsNames.READ_BUFFER_SIZE, new Integer JavaDoc(readBufferSize));
360         }
361
362         if (settingName == null || SettingsNames.WRITE_BUFFER_SIZE.equals(settingName)) {
363             int writeBufferSize = SettingsUtil.getPositiveInteger(kitClass,
364                                   SettingsNames.WRITE_BUFFER_SIZE, SettingsDefaults.defaultWriteBufferSize);
365             putProperty(SettingsNames.WRITE_BUFFER_SIZE, new Integer JavaDoc(writeBufferSize));
366         }
367
368         if (settingName == null || SettingsNames.MARK_DISTANCE.equals(settingName)) {
369             int markDistance = SettingsUtil.getPositiveInteger(kitClass,
370                                SettingsNames.MARK_DISTANCE, SettingsDefaults.defaultMarkDistance);
371             putProperty(SettingsNames.MARK_DISTANCE, new Integer JavaDoc(markDistance));
372         }
373
374         if (settingName == null || SettingsNames.MAX_MARK_DISTANCE.equals(settingName)) {
375             int maxMarkDistance = SettingsUtil.getPositiveInteger(kitClass,
376                                   SettingsNames.MAX_MARK_DISTANCE, SettingsDefaults.defaultMaxMarkDistance);
377             putProperty(SettingsNames.MAX_MARK_DISTANCE, new Integer JavaDoc(maxMarkDistance));
378         }
379
380         if (settingName == null || SettingsNames.MIN_MARK_DISTANCE.equals(settingName)) {
381             int minMarkDistance = SettingsUtil.getPositiveInteger(kitClass,
382                                   SettingsNames.MIN_MARK_DISTANCE, SettingsDefaults.defaultMinMarkDistance);
383             putProperty(SettingsNames.MIN_MARK_DISTANCE, new Integer JavaDoc(minMarkDistance));
384         }
385
386         if (settingName == null || SettingsNames.READ_MARK_DISTANCE.equals(settingName)) {
387             int readMarkDistance = SettingsUtil.getPositiveInteger(kitClass,
388                                    SettingsNames.READ_MARK_DISTANCE, SettingsDefaults.defaultReadMarkDistance);
389             putProperty(SettingsNames.READ_MARK_DISTANCE, new Integer JavaDoc(readMarkDistance));
390         }
391
392         if (settingName == null || SettingsNames.SYNTAX_UPDATE_BATCH_SIZE.equals(settingName)) {
393             int syntaxUpdateBatchSize = SettingsUtil.getPositiveInteger(kitClass,
394                                         SettingsNames.SYNTAX_UPDATE_BATCH_SIZE, SettingsDefaults.defaultSyntaxUpdateBatchSize);
395             putProperty(SettingsNames.SYNTAX_UPDATE_BATCH_SIZE, new Integer JavaDoc(syntaxUpdateBatchSize));
396         }
397
398         if (settingName == null || SettingsNames.LINE_BATCH_SIZE.equals(settingName)) {
399             int lineBatchSize = SettingsUtil.getPositiveInteger(kitClass,
400                                 SettingsNames.LINE_BATCH_SIZE, SettingsDefaults.defaultLineBatchSize);
401             putProperty(SettingsNames.LINE_BATCH_SIZE, new Integer JavaDoc(lineBatchSize));
402         }
403
404         if (settingName == null || SettingsNames.IDENTIFIER_ACCEPTOR.equals(settingName)) {
405             identifierAcceptor = SettingsUtil.getAcceptor(kitClass,
406                                  SettingsNames.IDENTIFIER_ACCEPTOR, AcceptorFactory.LETTER_DIGIT);
407         }
408
409         if (settingName == null || SettingsNames.WHITESPACE_ACCEPTOR.equals(settingName)) {
410             whitespaceAcceptor = SettingsUtil.getAcceptor(kitClass,
411                                  SettingsNames.WHITESPACE_ACCEPTOR, AcceptorFactory.WHITESPACE);
412         }
413
414         boolean stopOnEOL = SettingsUtil.getBoolean(kitClass,
415                             SettingsNames.WORD_MOVE_NEWLINE_STOP, true);
416         if (settingName == null || SettingsNames.NEXT_WORD_FINDER.equals(settingName)) {
417             putProperty(SettingsNames.NEXT_WORD_FINDER,
418                         SettingsUtil.getValue(kitClass, SettingsNames.NEXT_WORD_FINDER,
419                                               new FinderFactory.NextWordFwdFinder(this, stopOnEOL, false)));
420         }
421
422         if (settingName == null || SettingsNames.PREVIOUS_WORD_FINDER.equals(settingName)) {
423             putProperty(SettingsNames.PREVIOUS_WORD_FINDER,
424                         SettingsUtil.getValue(kitClass, SettingsNames.PREVIOUS_WORD_FINDER,
425                                               new FinderFactory.PreviousWordBwdFinder(this, stopOnEOL, false)));
426         }
427
428     }
429
430     Syntax getFreeSyntax() {
431         BaseKit kit = BaseKit.getKit(kitClass);
432         synchronized (Settings.class) {
433             int cnt = syntaxList.size();
434             return (cnt > 0) ? (Syntax)syntaxList.remove(cnt - 1)
435                    : kit.createSyntax(this);
436         }
437     }
438
439     void releaseSyntax(Syntax syntax) {
440         synchronized (Settings.class) {
441             syntaxList.add(syntax);
442         }
443     }
444
445     /** Get the formatter for this document. */
446     public Formatter getFormatter() {
447         return Formatter.getFormatter(kitClass);
448     }
449
450     public SyntaxSupport getSyntaxSupport() {
451         if (syntaxSupport == null) {
452             syntaxSupport = BaseKit.getKit(kitClass).createSyntaxSupport(this);
453         }
454         return syntaxSupport;
455     }
456     
457     /** Perform any generic text processing. The advantage of this method
458     * is that it allows the text to processed in line batches. The initial
459     * size of the batch is given by the SettingsNames.LINE_BATCH_SIZE.
460     * The TextBatchProcessor.processTextBatch() method is called for every
461     * text batch. If the method returns true, it means the processing should
462     * continue with the next batch of text which will have double line count
463     * compared to the previous one. This guarantees there will be not too many
464     * batches so the processing should be more efficient.
465     * @param tbp text batch processor to be used to process the text batches
466     * @param startPos starting position of the processing.
467     * @param endPos ending position of the processing. This can be -1 to signal
468     * the end of document. If the endPos is lower than startPos then the batches
469     * are created in the backward direction.
470     * @return the returned value from the last tpb.processTextBatch() call.
471     * The -1 will be returned for (startPos == endPos).
472     */

473     public int processText(TextBatchProcessor tbp, int startPos, int endPos)
474     throws BadLocationException JavaDoc {
475         if (endPos == -1) {
476             endPos = getLength();
477         }
478         int batchLineCnt = ((Integer JavaDoc)getProperty(SettingsNames.LINE_BATCH_SIZE)).intValue();
479         int batchStart = startPos;
480         int ret = -1;
481         if (startPos < endPos) { // batching in forward direction
482
while (ret < 0 && batchStart < endPos) {
483                 int batchEnd = Math.min(Utilities.getRowStart(this, batchStart, batchLineCnt), endPos);
484                 if (batchEnd == -1) { // getRowStart() returned -1
485
batchEnd = endPos;
486                 }
487                 ret = tbp.processTextBatch(this, batchStart, batchEnd, (batchEnd == endPos));
488                 batchLineCnt *= 2; // double the scanned area
489
batchStart = batchEnd;
490             }
491         } else {
492             while (ret < 0 && batchStart > endPos) {
493                 int batchEnd = Math.max(Utilities.getRowStart(this, batchStart, -batchLineCnt), endPos);
494                 ret = tbp.processTextBatch(this, batchStart, batchEnd, (batchEnd == endPos));
495                 batchLineCnt *= 2; // double the scanned area
496
batchStart = batchEnd;
497             }
498         }
499         return ret;
500     }
501
502
503     public boolean isIdentifierPart(char ch) {
504         return identifierAcceptor.accept(ch);
505     }
506
507     public boolean isWhitespace(char ch) {
508         return whitespaceAcceptor.accept(ch);
509     }
510
511     /** Create the mark for the given position
512     * and store it in the list. The position can
513     * be later retrieved through its ID.
514     */

515     int storePosition(int pos) throws BadLocationException JavaDoc {
516         MultiMark mark = getDocumentContent().createBiasMark(pos, Position.Bias.Forward);
517         int ind;
518         if (posFreeList.size() > 0) {
519             ind = ((Integer JavaDoc)posFreeList.remove(posFreeList.size() - 1)).intValue();
520             posList.set(ind, mark);
521         } else { // no free indexes
522
ind = posList.size();
523             posList.add(mark);
524         }
525         return ind;
526     }
527
528     int getStoredPosition(int posID) {
529         if (posID < 0 || posID >= posList.size()) {
530             return -1;
531         }
532         MultiMark mark = (MultiMark)posList.get(posID);
533         return (mark != null) ? mark.getOffset() : -1;
534     }
535
536     void removeStoredPosition(int posID) {
537         if (posID >= 0 || posID < posList.size()) {
538             Mark mark = (Mark)posList.get(posID);
539             posList.set(posID, null); // clear the index
540
posFreeList.add(new Integer JavaDoc(posID));
541
542             // Remove the mark #19429
543
try {
544                 mark.remove();
545             } catch (InvalidMarkException e) {
546             }
547         }
548     }
549
550     /** Inserts string into document */
551     public void insertString(int offset, String JavaDoc text, AttributeSet JavaDoc a)
552     throws BadLocationException JavaDoc {
553         if (text == null || text.length() == 0) {
554             return;
555         }
556
557         // Check offset correctness
558
if (offset < 0 || offset > getLength()) {
559             throw new BadLocationException JavaDoc("Wrong insert position " + offset, offset); // NOI18N
560
}
561
562         // possible CR-LF conversion
563
text = Analyzer.convertLSToLF(text);
564         
565         // Check whether there is an active postModificationDocumentListener
566
// and if so then start an atomic transaction.
567
boolean activePostModification;
568         DocumentEvent JavaDoc postModificationEvent = null;
569         synchronized (this) {
570             activePostModification = (postModificationDocumentListener != null);
571             if (activePostModification) {
572                 atomicLock();
573             }
574         }
575         try {
576
577         // Perform the insert
578
boolean notifyMod = notifyModifyCheckStart(offset, "insertString() vetoed"); // NOI18N
579
boolean modFinished = false; // Whether modification succeeded
580
extWriteLock();
581         try {
582
583             /*
584             boolean checkSpaces = inited && org.netbeans.lib.editor.util.swing.DocumentUtilities.isTypingModification(this);
585             if (checkSpaces) {
586                 Position offsPosition = createPosition(offset);
587                 checkTrailingSpaces(offset);
588                 offset = offsPosition.getOffset();
589             }
590              */

591             
592             preInsertCheck(offset, text, a);
593
594             // Do the real insert into the content
595
UndoableEdit JavaDoc edit = getContent().insertString(offset, text);
596
597             /*
598             if (checkSpaces) {
599                 lastPositionEditedByTyping = createPosition(offset);
600             }
601              */

602             
603             if (debug) {
604                 System.err.println("BaseDocument.insertString(): doc=" + this // NOI18N
605
+ (modified ? "" : " - first modification") // NOI18N
606
+ ", offset=" + Utilities.offsetToLineColumnString(this, offset) // NOI18N
607
+ (debugNoText ? "" : (", text='" + text + "'")) // NOI18N
608
);
609             }
610             if (debugStack) {
611                 Thread.dumpStack();
612             }
613
614             BaseDocumentEvent evt = createDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT);
615
616             preInsertUpdate(evt, a);
617
618             if (edit != null) {
619                 evt.addEdit(edit);
620                 
621                 lastModifyUndoEdit = edit; // #8692 check last modify undo edit
622
}
623
624             modified = true;
625
626             if (atomicDepth > 0) {
627                 if (atomicEdits == null) {
628                     atomicEdits = new AtomicCompoundEdit();
629                 }
630                 atomicEdits.addEdit(evt); // will be added
631
}
632
633             insertUpdate(evt, a);
634
635             evt.end();
636
637             fireInsertUpdate(evt);
638
639             boolean isComposedText = ((a != null)
640                                       && (a.isDefined(StyleConstants.ComposedTextAttribute)));
641
642             if (composedText && !isComposedText)
643                 composedText = false;
644             if (!composedText && isComposedText)
645                 composedText = true;
646             
647             if (atomicDepth == 0 && !isComposedText) { // !!! check
648
fireUndoableEditUpdate(new UndoableEditEvent JavaDoc(this, evt));
649             }
650             modFinished = true;
651             postModificationEvent = evt;
652         } finally {
653             extWriteUnlock();
654             // Notify no mod done if notified mod but mod did not succeeded
655
if (notifyMod) {
656                 notifyModifyCheckEnd(modFinished);
657             }
658         }
659         
660         } finally { // for post modification
661
if (activePostModification) {
662                 if (postModificationEvent != null) { // Modification finished successfully
663
if (postModificationDocumentListener != null) {
664                         postModificationDocumentListener.insertUpdate(postModificationEvent);
665                     }
666                 }
667                 atomicUnlock();
668             }
669         }
670     }
671     
672     public void checkTrailingSpaces(int offset) {
673         try {
674             int lineNum = Utilities.getLineOffset(this, offset);
675             int lastEditedLine = lastPositionEditedByTyping != null ? Utilities.getLineOffset(this, lastPositionEditedByTyping.getOffset()) : -1;
676             if (lastEditedLine != -1 && lastEditedLine != lineNum) {
677                 // clear trailing spaces in the last edited line
678
Element JavaDoc root = getDefaultRootElement();
679                 Element JavaDoc elem = root.getElement(lastEditedLine);
680                 int start = elem.getStartOffset();
681                 int end = elem.getEndOffset();
682                 String JavaDoc line = getText(start, end - start);
683                 
684                 int endIndex = line.length() - 1;
685                 if (endIndex >= 0 && line.charAt(endIndex) == '\n') {
686                     endIndex--;
687                     if (endIndex >= 0 && line.charAt(endIndex) == '\r') {
688                         endIndex--;
689                     }
690                 }
691
692                 int startIndex = endIndex;
693                 while (startIndex >= 0 && Character.isWhitespace(line.charAt(startIndex)) && line.charAt(startIndex) != '\n' &&
694                         line.charAt(startIndex) != '\r') {
695                     startIndex--;
696                 }
697                 startIndex++;
698                 if (startIndex >= 0 && startIndex <= endIndex) {
699                     remove(start + startIndex, endIndex - startIndex + 1);
700                 }
701             }
702         } catch (BadLocationException JavaDoc e) {
703             e.printStackTrace();
704         }
705     }
706     
707     /** Removes portion of a document */
708     public void remove(int offset, int len) throws BadLocationException JavaDoc {
709         if (len > 0) {
710             if (offset < 0) {
711                 throw new BadLocationException JavaDoc("Wrong remove position " + offset, offset); // NOI18N
712
}
713
714             // Check whether there is an active postModificationDocumentListener
715
// and if so then start an atomic transaction.
716
boolean activePostModification;
717             DocumentEvent JavaDoc postModificationEvent = null;
718             synchronized (this) {
719                 activePostModification = (postModificationDocumentListener != null);
720                 if (activePostModification) {
721                     atomicLock();
722                 }
723             }
724             try {
725                 
726             boolean notifyMod = notifyModifyCheckStart(offset, "remove() vetoed"); // NOI18N
727
boolean modFinished = false; // Whether modification succeeded
728
extWriteLock();
729             try {
730                 int docLen = getLength();
731                 if (offset < 0 || offset > docLen) {
732                     throw new BadLocationException JavaDoc("Wrong remove position " + offset, offset); // NOI18N
733
}
734                 if (offset + len > docLen) {
735                     throw new BadLocationException JavaDoc("End offset of removed text " // NOI18N
736
+ (offset + len) + " > getLength()=" + docLen, // NOI18N
737
offset + len
738                     ); // NOI18N
739
}
740
741                 preRemoveCheck(offset, len);
742
743                 BaseDocumentEvent evt = createDocumentEvent(offset, len, DocumentEvent.EventType.REMOVE);
744
745                 removeUpdate(evt);
746
747                 UndoableEdit JavaDoc edit = getContent().remove(offset, len);
748                 if (edit != null) {
749                     evt.addEdit(edit);
750                 
751                     lastModifyUndoEdit = edit; // #8692 check last modify undo edit
752
}
753
754                 if (debug) {
755                     System.err.println("BaseDocument.remove(): doc=" + this // NOI18N
756
+ ", origDocLen=" + docLen // NOI18N
757
+ ", offset=" + Utilities.offsetToLineColumnString(this, offset) // NOI18N
758
+ ", len=" + len // NOI18N
759
+ (debugNoText ? "" : (", removedText='" + ((DocumentContent.Edit)edit).getUndoRedoText() + "'")) // NOI18N
760
);
761                 }
762                 if (debugStack) {
763                     Thread.dumpStack();
764                 }
765
766                 if (atomicDepth > 0) { // add edits as soon as possible
767
if (atomicEdits == null) {
768                         atomicEdits = new AtomicCompoundEdit();
769                     }
770                     atomicEdits.addEdit(evt); // will be added
771
}
772
773                 postRemoveUpdate(evt);
774
775                 evt.end();
776
777                 fireRemoveUpdate(evt);
778                 if (atomicDepth == 0 && !composedText) {
779                     fireUndoableEditUpdate(new UndoableEditEvent JavaDoc(this, evt));
780                 }
781                 
782                 modFinished = true;
783                 postModificationEvent = evt;
784             } finally {
785                 extWriteUnlock();
786                 // Notify no mod done if notified mod but mod did not succeeded
787
if (notifyMod) {
788                     notifyModifyCheckEnd(modFinished);
789                 }
790             }
791
792             } finally { // for post modification
793
if (activePostModification) {
794                     if (postModificationEvent != null) { // Modification finished successfully
795
if (postModificationDocumentListener != null) {
796                             postModificationDocumentListener.removeUpdate(postModificationEvent);
797                         }
798                     }
799                     atomicUnlock();
800                 }
801             }
802
803         }
804     }
805
806     /**
807      * Check notify modify status and initialize it if necessary
808      * before the actual modification is going to be done.
809      * <br>
810      * This method is invoked internally by this document implementation
811      * and from the document event implementation as well during undo/redo.
812      *
813      * @param offset &gt;=0 offset at which the modification has been done.
814      * @param vetoExceptionText text to be used for exception in case
815      * the modification has been vetoed.
816      * @return whether the modification had to be notified or not.
817      * In case it had to be notified the {@link #notifyModifyCheckEnd(boolean)}
818      * has to be called.
819      */

820     boolean notifyModifyCheckStart(int offset, String JavaDoc vetoExceptionText) throws BadLocationException JavaDoc {
821         NotifyModifyStatus notifyModifyStatus = (NotifyModifyStatus)STATUS.get();
822         boolean notifyMod;
823         if (notifyModifyStatus == null) { // not in atomic lock
824
notifyModifyStatus = new NotifyModifyStatus (this);
825             STATUS.set (notifyModifyStatus);
826             notifyModify(notifyModifyStatus);
827             notifyMod = true;
828         } else {
829             notifyMod = false;
830         }
831
832         if (notifyModifyStatus.isModificationVetoed()) {
833             if (notifyMod) {
834                 STATUS.set (null);
835             }
836             throw new BadLocationException JavaDoc(vetoExceptionText, offset);
837         }
838         return notifyMod;
839     }
840     
841     /**
842      * Check notify modify status after the modification has been done.
843      * <br>
844      * This method is invoked internally by this document implementation
845      * and from the document event implementation as well during undo/redo.
846      * <br>
847      * It should only be called if the {@link #notifyModifyCheckStart(int, String)}
848      * has returned <code>true</code>.
849      *
850      * @param modFinished whether the actual modification of the document succeeded.
851      */

852     void notifyModifyCheckEnd(boolean modFinished) {
853         NotifyModifyStatus notifyModifyStatus = (NotifyModifyStatus)STATUS.get();
854         STATUS.set (null);
855         if (!modFinished) {
856             notifyUnmodify(notifyModifyStatus);
857         }
858     }
859     
860     /** This method is called automatically before the document
861     * insertion occurs and can be used to revoke the insertion before it occurs
862     * by throwing the <tt>BadLocationException</tt>.
863     * @param offset position where the insertion will be done
864     * @param text string to be inserted
865     * @param a attributes of the inserted text
866     */

867     protected void preInsertCheck(int offset, String JavaDoc text, AttributeSet JavaDoc a)
868     throws BadLocationException JavaDoc {
869     }
870
871     /** This method is called automatically before the document
872     * removal occurs and can be used to revoke the removal before it occurs
873     * by throwing the <tt>BadLocationException</tt>.
874     * @param offset position where the insertion will be done
875     * @param len length of the removal
876     */

877     protected void preRemoveCheck(int offset, int len)
878     throws BadLocationException JavaDoc {
879     }
880
881     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet JavaDoc attr) {
882         super.insertUpdate(chng, attr);
883
884         // Store modification text as an event's property
885
org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng);
886         org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(chng, String JavaDoc.class,
887                 ((BaseDocumentEvent)chng).getText());
888
889         MarksStorageUndo marksStorageUndo = new MarksStorageUndo(chng);
890         marksStorageUndo.updateMarksStorage();
891         chng.addEdit(marksStorageUndo); // fix compatible marks
892

893         UndoableEdit JavaDoc lineUndo = lineRootElement.insertUpdate(chng.getOffset(), chng.getLength());
894         if (lineUndo != null) {
895             chng.addEdit(lineUndo);
896         }
897         
898         fixLineSyntaxState.update(false);
899         chng.addEdit(fixLineSyntaxState.createAfterLineUndo());
900         fixLineSyntaxState = null;
901
902         // Useful for lexer snapshots
903
org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng);
904         BaseDocumentEvent bEvt = (BaseDocumentEvent)chng;
905         org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(
906                 chng, String JavaDoc.class, bEvt.getText());
907         
908     }
909     
910     protected void preInsertUpdate(DefaultDocumentEvent chng, AttributeSet JavaDoc attr) {
911         fixLineSyntaxState = new FixLineSyntaxState(chng);
912         chng.addEdit(fixLineSyntaxState.createBeforeLineUndo());
913     }
914
915     protected void removeUpdate(DefaultDocumentEvent chng) {
916         super.removeUpdate(chng);
917
918         // Store modification text as an event's property
919
org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng);
920         String JavaDoc removedText;
921         try {
922             removedText = getText(chng.getOffset(), chng.getLength());
923         } catch (BadLocationException JavaDoc e) {
924             // Ignore
925
removedText = null;
926         }
927         org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(chng, String JavaDoc.class,
928                 removedText);
929
930         // Remember the line changes here but add them to chng during postRemoveUpdate()
931
removeUpdateLineUndo = lineRootElement.removeUpdate(chng.getOffset(), chng.getLength());
932         
933         fixLineSyntaxState = new FixLineSyntaxState(chng);
934         chng.addEdit(fixLineSyntaxState.createBeforeLineUndo());
935     }
936
937     protected void postRemoveUpdate(DefaultDocumentEvent chng) {
938         super.postRemoveUpdate(chng);
939
940         MarksStorageUndo marksStorageUndo = new MarksStorageUndo(chng);
941         marksStorageUndo.updateMarksStorage();
942         chng.addEdit(marksStorageUndo); // fix compatible marks
943

944         if (removeUpdateLineUndo != null) {
945             chng.addEdit(removeUpdateLineUndo);
946             removeUpdateLineUndo = null;
947         }
948         
949         fixLineSyntaxState.update(false);
950         chng.addEdit(fixLineSyntaxState.createAfterLineUndo());
951         fixLineSyntaxState = null;
952
953         // Useful for lexer snapshots
954
org.netbeans.lib.editor.util.swing.DocumentUtilities.addEventPropertyStorage(chng);
955         BaseDocumentEvent bEvt = (BaseDocumentEvent)chng;
956         org.netbeans.lib.editor.util.swing.DocumentUtilities.putEventProperty(
957                 chng, String JavaDoc.class, bEvt.getText());
958     }
959
960     public String JavaDoc getText(int[] block) throws BadLocationException JavaDoc {
961         return getText(block[0], block[1] - block[0]);
962     }
963
964     /**
965      * @param pos position of the first character to get.
966      * @param len number of characters to obtain.
967      * @return array with the requested characters.
968      */

969     public char[] getChars(int pos, int len) throws BadLocationException JavaDoc {
970         char[] chars = new char[len];
971         getChars(pos, chars, 0, len);
972         return chars;
973     }
974
975     /**
976      * @param block two-element array with starting and ending offset
977      * @return array with the requested characters.
978      */

979     public char[] getChars(int[] block) throws BadLocationException JavaDoc {
980         return getChars(block[0], block[1] - block[0]);
981     }
982
983     /**
984      * @param pos position of the first character to get.
985      * @param ret destination array
986      * @param offset offset in the destination array.
987      * @param len number of characters to obtain.
988      * @return array with the requested characters.
989      */

990     public void getChars(int pos, char ret[], int offset, int len)
991     throws BadLocationException JavaDoc {
992         DocumentUtilities.copyText(this, pos, pos + len, ret, offset);
993     }
994
995     /** Find something in document using a finder.
996     * @param finder finder to be used for the search
997     * @param startPos position in the document where the search will start
998     * @param limitPos position where the search will be end with reporting
999     * that nothing was found.
1000    */

1001    public int find(Finder finder, int startPos, int limitPos)
1002    throws BadLocationException JavaDoc {
1003        int docLen = getLength();
1004        if (limitPos == -1) {
1005            limitPos = docLen;
1006        }
1007        if (startPos == -1) {
1008            startPos = docLen;
1009        }
1010
1011        if (finder instanceof AdjustFinder) {
1012            if (startPos == limitPos) { // stop immediately
1013
finder.reset(); // reset() should be called in all the cases
1014
return -1; // must stop here because wouldn't know if fwd/bwd search?
1015
}
1016
1017            boolean forwardAdjustedSearch = (startPos < limitPos);
1018            startPos = ((AdjustFinder)finder).adjustStartPos(this, startPos);
1019            limitPos = ((AdjustFinder)finder).adjustLimitPos(this, limitPos);
1020            boolean voidSearch = (forwardAdjustedSearch ? (startPos >= limitPos) : (startPos <= limitPos));
1021            if (voidSearch) {
1022                finder.reset();
1023                return -1;
1024            }
1025        }
1026
1027        finder.reset();
1028        if (startPos == limitPos) {
1029            return -1;
1030        }
1031
1032        Segment JavaDoc text = DocumentUtilities.SEGMENT_CACHE.getSegment();
1033        try {
1034            int gapStart = DocumentUtilities.getGapStart(this);
1035            if (gapStart == -1) {
1036                throw new IllegalStateException JavaDoc("Cannot get gapStart"); // NOI18N
1037
}
1038
1039            int pos = startPos; // pos at which the search starts (continues)
1040
boolean fwdSearch = (startPos <= limitPos); // forward search
1041
if (fwdSearch) {
1042                while (pos >= startPos && pos < limitPos) {
1043                    int p0; // low bound
1044
int p1; // upper bound
1045
if (pos < gapStart) { // part below gap
1046
p0 = startPos;
1047                        p1 = Math.min(gapStart, limitPos);
1048                    } else { // part above gap
1049
p0 = Math.max(gapStart, startPos);
1050                        p1 = limitPos;
1051                    }
1052                    
1053                    getText(p0, p1 - p0, text);
1054                    pos = finder.find(p0 - text.offset, text.array,
1055                            text.offset, text.offset + text.count, pos, limitPos);
1056                    
1057                    if (finder.isFound()) {
1058                        return pos;
1059                    }
1060                }
1061
1062            } else { // backward search limitPos < startPos
1063
pos--; // start one char below the upper bound
1064
while (limitPos <= pos && pos <= startPos) {
1065                    int p0; // low bound
1066
int p1; // upper bound
1067
if (pos < gapStart) { // part below gap
1068
p0 = limitPos;
1069                        p1 = Math.min(gapStart, startPos);
1070                    } else { // part above gap
1071
p0 = Math.max(gapStart, limitPos);
1072                        p1 = startPos;
1073                    }
1074                    
1075                    getText(p0, p1 - p0, text);
1076                    pos = finder.find(p0 - text.offset, text.array,
1077                            text.offset, text.offset + text.count, pos, limitPos);
1078                    
1079                    if (finder.isFound()) {
1080                        return pos;
1081                    }
1082                }
1083            }
1084            
1085            return -1; // position outside bounds => not found
1086

1087        } finally {
1088            DocumentUtilities.SEGMENT_CACHE.releaseSegment(text);
1089        }
1090    }
1091
1092    /** Fire the change event to repaint the given block of text. */
1093    public void repaintBlock(int startOffset, int endOffset) {
1094        BaseDocumentEvent evt = createDocumentEvent(startOffset,
1095                endOffset - startOffset, DocumentEvent.EventType.CHANGE);
1096        fireChangedUpdate(evt);
1097    }
1098
1099    public void print(PrintContainer container) {
1100        print(container, true, true,0,getLength());
1101    }
1102
1103    /**
1104     * Print into given container.
1105     *
1106     * @param container printing container into which the printing will be done.
1107     * @param usePrintColoringMap use printing coloring settings instead
1108     * of the regular ones.
1109     * @param lineNumberEnabled if set to false the line numbers will not be printed.
1110     * If set to true the visibility of line numbers depends on the settings
1111     * for the line number visibility.
1112     * @param startOffset start offset of text to print
1113     * @param endOffset end offset of text to print
1114     */

1115    public void print(PrintContainer container, boolean usePrintColoringMap, boolean lineNumberEnabled, int startOffset,
1116                      int endOffset) {
1117        readLock();
1118        try {
1119            EditorUI editorUI = BaseKit.getKit(kitClass).createPrintEditorUI(this,
1120                usePrintColoringMap, lineNumberEnabled);
1121
1122            DrawGraphics.PrintDG printDG = new DrawGraphics.PrintDG(container);
1123            DrawEngine.getDrawEngine().draw(printDG, editorUI, startOffset, endOffset, 0, 0, Integer.MAX_VALUE);
1124        } catch (BadLocationException JavaDoc e) {
1125            e.printStackTrace();
1126        } finally {
1127            readUnlock();
1128        }
1129    }
1130
1131    /**
1132     * Print into given container.
1133     *
1134     * @param container printing container into which the printing will be done.
1135     * @param usePrintColoringMap use printing coloring settings instead
1136     * of the regular ones.
1137     * @param lineNumberEnabled if null, the visibility of line numbers is the same as it is given by settings
1138     * for the line number visibility, otherwise the visibility equals the boolean value of the parameter
1139     * @param startOffset start offset of text to print
1140     * @param endOffset end offset of text to print
1141     */

1142    public void print(PrintContainer container, boolean usePrintColoringMap, Boolean JavaDoc lineNumberEnabled, int startOffset,
1143                      int endOffset) {
1144        readLock();
1145        try {
1146            boolean lineNumberEnabledPar = true;
1147            boolean forceLineNumbers = false;
1148            if (lineNumberEnabled != null) {
1149                lineNumberEnabledPar = lineNumberEnabled.booleanValue();
1150                forceLineNumbers = lineNumberEnabled.booleanValue();
1151            }
1152            EditorUI editorUI = BaseKit.getKit(kitClass).createPrintEditorUI(this, usePrintColoringMap, lineNumberEnabledPar);
1153            if (forceLineNumbers) {
1154                editorUI.setLineNumberVisibleSetting(true);
1155                editorUI.setLineNumberEnabled(true);
1156                editorUI.updateLineNumberWidth(0);
1157            }
1158
1159            DrawGraphics.PrintDG printDG = new DrawGraphics.PrintDG(container);
1160            DrawEngine.getDrawEngine().draw(printDG, editorUI, startOffset, endOffset, 0, 0, Integer.MAX_VALUE);
1161        } catch (BadLocationException JavaDoc e) {
1162            e.printStackTrace();
1163        } finally {
1164            readUnlock();
1165        }
1166    }
1167    
1168    /** Create biased position in document */
1169    public Position JavaDoc createPosition(int offset, Position.Bias JavaDoc bias)
1170    throws BadLocationException JavaDoc {
1171        return getDocumentContent().createBiasPosition(offset, bias);
1172    }
1173    
1174    MultiMark createMark(int offset) throws BadLocationException JavaDoc {
1175        return getDocumentContent().createMark(offset);
1176    }
1177    
1178    MultiMark createBiasMark(int offset, Position.Bias JavaDoc bias) throws BadLocationException JavaDoc {
1179        return getDocumentContent().createBiasMark(offset, bias);
1180    }
1181    
1182    /** Return array of root elements - usually only one */
1183    public Element JavaDoc[] getRootElements() {
1184        Element JavaDoc[] elems = new Element JavaDoc[1];
1185        elems[0] = getDefaultRootElement();
1186        return elems;
1187    }
1188
1189    /** Return default root element */
1190    public Element JavaDoc getDefaultRootElement() {
1191        if (defaultRootElem == null) {
1192            defaultRootElem = getLineRootElement();
1193        }
1194        return defaultRootElem;
1195    }
1196
1197    /** Runs the runnable under read lock. */
1198    public void render(Runnable JavaDoc r) {
1199        readLock();
1200        assert incrementThreadLocalLockDepth();
1201        try {
1202            r.run();
1203        } finally {
1204            assert decrementThreadLocalLockDepth();
1205            readUnlock();
1206        }
1207    }
1208    
1209    private boolean incrementThreadLocalLockDepth() {
1210        Integer JavaDoc depthInteger = (Integer JavaDoc)THREAD_LOCAL_LOCK_DEPTH.get();
1211        if (depthInteger == null) {
1212            depthInteger = lockIntegers[1];
1213        } else {
1214            int newDepth = depthInteger.intValue() + 1;
1215            depthInteger = (newDepth < lockIntegers.length)
1216                    ? lockIntegers[newDepth]
1217                    : new Integer JavaDoc(newDepth);
1218        }
1219        THREAD_LOCAL_LOCK_DEPTH.set(depthInteger);
1220        return true;
1221    }
1222    
1223    private boolean decrementThreadLocalLockDepth() {
1224        Integer JavaDoc depthInteger = (Integer JavaDoc)THREAD_LOCAL_LOCK_DEPTH.get();
1225        assert (depthInteger != null);
1226        int newDepth = depthInteger.intValue() - 1;
1227        assert (newDepth >= 0);
1228        THREAD_LOCAL_LOCK_DEPTH.set(
1229            (newDepth < lockIntegers.length)
1230                ? lockIntegers[newDepth]
1231                : new Integer JavaDoc(newDepth)
1232        );
1233        return true;
1234    }
1235
1236    /** Runs the runnable under write lock. This is a stronger version
1237    * of the runAtomicAsUser() method, because if there any locked sections
1238    * in the documents this methods breaks the modification locks and modifies
1239    * the document.
1240    * If there are any excpeptions thrown during the processing of the runnable,
1241    * all the document modifications are rolled back automatically.
1242    */

1243    public void runAtomic(Runnable JavaDoc r) {
1244        runAtomicAsUser(r);
1245    }
1246
1247    /** Runs the runnable under write lock.
1248    * If there are any excpeptions thrown during the processing of the runnable,
1249    * all the document modifications are rolled back automatically.
1250    */

1251    public void runAtomicAsUser(Runnable JavaDoc r) {
1252        boolean completed = false;
1253        atomicLock();
1254        try {
1255            r.run();
1256            completed = true;
1257        } finally {
1258            try {
1259                if (!completed) {
1260                    breakAtomicLock();
1261                }
1262            } finally {
1263                atomicUnlock();
1264            }
1265        }
1266    }
1267
1268    /** Insert contents of reader at specified position into document.
1269    * @param reader reader from which data will be read
1270    * @param pos on which position that data will be inserted
1271    */

1272    public void read(Reader JavaDoc reader, int pos)
1273    throws IOException JavaDoc, BadLocationException JavaDoc {
1274        extWriteLock();
1275        try {
1276
1277            if (pos < 0 || pos > getLength()) {
1278                throw new BadLocationException JavaDoc("BaseDocument.read()", pos); // NOI18N
1279
}
1280
1281            if (inited || modified) { // was the document already initialized?
1282
Analyzer.read(this, reader, pos);
1283            } else { // not initialized yet, we can use initialRead()
1284
Analyzer.initialRead(this, reader, true);
1285                inited = true; // initialized but not modified
1286
}
1287            if (debugRead) {
1288                System.err.println("BaseDocument.read(): StreamDescriptionProperty: "+getProperty(StreamDescriptionProperty));
1289            }
1290            
1291            // Compact storage
1292
Content content = getContent();
1293            if (content instanceof DocumentContent) {
1294                ((DocumentContent)content).compact();
1295            }
1296        } finally {
1297            extWriteUnlock();
1298        }
1299    }
1300
1301    /** Write part of the document into specified writer.
1302    * @param writer writer into which data will be written.
1303    * @param pos from which position get the data
1304    * @param len how many characters write
1305    */

1306    public void write(Writer JavaDoc writer, int pos, int len)
1307    throws IOException JavaDoc, BadLocationException JavaDoc {
1308        readLock();
1309        try {
1310
1311            if ((pos < 0) || ((pos + len) > getLength())) {
1312                throw new BadLocationException JavaDoc("BaseDocument.write()", pos); // NOI18N
1313
}
1314            Analyzer.write(this, writer, pos, len);
1315            writer.flush();
1316        } finally {
1317            readUnlock();
1318        }
1319    }
1320
1321    /** Invalidate the state-infos in all the syntax-marks
1322     * in the whole document. The Syntax can call this method
1323     * if it changes its internal state in the way that affects
1324     * the future returned tokens. The syntax-state-info in all
1325     * the marks is reset and it will be lazily restored when necessary.
1326     */

1327    public void invalidateSyntaxMarks() {
1328        extWriteLock();
1329        try {
1330            FixLineSyntaxState.invalidateAllSyntaxStateInfos(this);
1331            repaintBlock(0, getLength());
1332        } finally {
1333          extWriteUnlock();
1334        }
1335    }
1336
1337    /** Get the number of spaces the TAB character ('\t') visually represents.
1338     * This is related to <code>SettingsNames.TAB_SIZE</code> setting.
1339     */

1340    public int getTabSize() {
1341        return tabSize;
1342    }
1343    
1344    /** Get the width of one indentation level.
1345     * The algorithm first checks whether there's a value for the INDENT_SHIFT_WIDTH
1346     * setting. If so it uses it, otherwise it uses <code>formatter.getSpacesPerTab()</code>.
1347     *
1348     * @see getTabSize()
1349     * @see Formatter.getSpacesPerTab()
1350     */

1351    public int getShiftWidth() {
1352        if (shiftWidth != null) {
1353            return shiftWidth.intValue();
1354
1355        } else {
1356            return getFormatter().getSpacesPerTab();
1357        }
1358    }
1359
1360    public final Class JavaDoc getKitClass() {
1361        return kitClass;
1362    }
1363
1364    /** This method prohibits merging of the next document modification
1365    * with the previous one even if it would be normally possible.
1366    */

1367    public void resetUndoMerge() {
1368        undoMergeReset = true;
1369    }
1370
1371    /* Defined because of the hack for undo()
1372     * in the BaseDocumentEvent.
1373     */

1374    protected void fireChangedUpdate(DocumentEvent JavaDoc e) {
1375        super.fireChangedUpdate(e);
1376    }
1377    protected void fireInsertUpdate(DocumentEvent JavaDoc e) {
1378        super.fireInsertUpdate(e);
1379    }
1380    protected void fireRemoveUpdate(DocumentEvent JavaDoc e) {
1381        super.fireRemoveUpdate(e);
1382    }
1383
1384    protected void fireUndoableEditUpdate(UndoableEditEvent JavaDoc e) {
1385    // Fire to the list of listeners that was used before the atomic lock started
1386
// This fixes issue #47881 and appears to be somewhat more logical
1387
// than the default approach to fire all the current listeners
1388
Object JavaDoc[] listeners = (atomicLockListenerList != null)
1389            ? atomicLockListenerList
1390            : listenerList.getListenerList();
1391
1392    for (int i = listeners.length - 2; i >= 0; i -= 2) {
1393        if (listeners[i] == UndoableEditListener JavaDoc.class) {
1394        ((UndoableEditListener JavaDoc)listeners[i + 1]).undoableEditHappened(e);
1395        }
1396    }
1397    }
1398
1399    /** Extended write locking of the document allowing
1400    * reentrant write lock acquiring.
1401    */

1402    public synchronized final void extWriteLock() {
1403        if (Thread.currentThread() != getCurrentWriter()) {
1404            super.writeLock();
1405            assert incrementThreadLocalLockDepth();
1406        } else { // inner locking block
1407
writeDepth++; // only increase write deepness
1408
}
1409    }
1410
1411    /** Extended write unlocking.
1412    * @see extWriteLock()
1413    */

1414    public synchronized final void extWriteUnlock() {
1415        if (Thread.currentThread() != getCurrentWriter()) {
1416            throw new RuntimeException JavaDoc(WRITE_LOCK_MISSING);
1417        }
1418
1419        if (writeDepth == 0) { // most outer locking block
1420
assert decrementThreadLocalLockDepth();
1421            super.writeUnlock();
1422        } else { // just inner locking block
1423
writeDepth--;
1424        }
1425    }
1426    
1427    public final void atomicLock() {
1428        synchronized (this) {
1429            NotifyModifyStatus notifyModifyStatus = (NotifyModifyStatus)STATUS.get();
1430            if (notifyModifyStatus == null) {
1431                // Notify that the modification will be done prior locking
1432
notifyModifyStatus = new NotifyModifyStatus (this);
1433                STATUS.set (notifyModifyStatus);
1434                //assert atomicDepth == 0 : "New status only on depth 0, but: " + atomicDepth; // NOI18N
1435
} else {
1436                //assert atomicDepth > 0 : "When there is a status: " + notifyModifyStatus+ " there needs to be a lot as well: " + atomicDepth; // NOI18N
1437
}
1438            
1439            extWriteLock();
1440            atomicDepth++;
1441            if (atomicDepth == 1) { // lock really started
1442
fireAtomicLock(atomicLockEventInstance);
1443                // Copy the listener list - will be used for firing undo
1444
atomicLockListenerList = listenerList.getListenerList();
1445            }
1446        }
1447    }
1448
1449    public final void atomicUnlock() {
1450        boolean modsDone = false;
1451        boolean lastAtomic = false;
1452        synchronized (this) {
1453            extWriteUnlock();
1454            if (atomicDepth == 0) {
1455                throw new IllegalStateException JavaDoc("atomicUnlock() without atomicLock()"); // NOI18N
1456
}
1457
1458
1459            if (--atomicDepth == 0) { // lock really ended
1460
lastAtomic = true;
1461                fireAtomicUnlock(atomicLockEventInstance);
1462
1463                if (atomicEdits != null && atomicEdits.size() > 0) {
1464                    modsDone = true;
1465                    // Some edits performed
1466
atomicEdits.end();
1467                    fireUndoableEditUpdate(new UndoableEditEvent JavaDoc(this, atomicEdits));
1468                    atomicEdits = null;
1469                }
1470                
1471                if (modsUndoneOrRedone) { // Check whether any modifications were undone or redone
1472
modsUndoneOrRedone = false;
1473                    modsDone = true;
1474                }
1475                atomicLockListenerList = null;
1476            }
1477        }
1478        
1479        // Notify unmodification if there were document modifications
1480
// inside the atomic section
1481
// or in case when in undo/redo because in such case
1482
// no insertString() or remove() would be done (just undoing
1483
// physical changes in the buffer and firing document listeners)
1484
if (modsDone) {
1485            NotifyModifyStatus notifyModifyStatus = (NotifyModifyStatus)STATUS.get();
1486            notifyModify(notifyModifyStatus);
1487        }
1488        
1489        if (lastAtomic) {
1490            STATUS.set (null);
1491        }
1492    }
1493    
1494    void markModsUndoneOrRedone() {
1495        modsUndoneOrRedone = true;
1496    }
1497
1498    /** Is the document currently atomically locked?
1499    * It's not synced as this method must be called only from writer thread.
1500    */

1501    public final boolean isAtomicLock() {
1502        return (atomicDepth > 0);
1503    }
1504
1505    /** Break the atomic lock so that doc is no longer in atomic mode.
1506    * All the performed changes are rolled back automatically.
1507    * Even after calling this method, the atomicUnlock() must still be called.
1508    * This method is not synced as it must be called only from writer thread.
1509    */

1510    public final void breakAtomicLock() {
1511        if (atomicEdits != null && atomicEdits.size() > 0) {
1512            atomicEdits.end();
1513            atomicEdits.undo();
1514            atomicEdits = null;
1515        }
1516    }
1517    
1518    /**
1519     * Notify the beforeModificationListener that there is going to be
1520     * a document modification or atomic lock started.
1521     */

1522    private void notifyModify(NotifyModifyStatus notifyModifyStatus) {
1523        notifyModifyStatus.setModificationVetoed(false);
1524        VetoableChangeListener JavaDoc bml = notifyModifyStatus.getBeforeModificationListener();
1525        if (bml != null) {
1526            try {
1527                bml.vetoableChange(new PropertyChangeEvent JavaDoc(this, "modified", null, Boolean.TRUE)); // NOI18N
1528
} catch (PropertyVetoException JavaDoc ex) {
1529                // Modification is prohibited
1530
notifyModifyStatus.setModificationVetoed(true);
1531            }
1532        }
1533    }
1534    
1535    /**
1536     * Notify the beforeModificationListener that the modification
1537     * was not performed during the atomic lock and that the previously
1538     * supposed modification notification should be reverted.
1539     */

1540    private void notifyUnmodify(NotifyModifyStatus notifyModifyStatus) {
1541        VetoableChangeListener JavaDoc bml = notifyModifyStatus.getBeforeModificationListener();
1542        if (bml != null) {
1543            try {
1544                bml.vetoableChange(new PropertyChangeEvent JavaDoc(this, "modified", null, Boolean.FALSE)); // NOI18N
1545
} catch (PropertyVetoException JavaDoc e) {
1546                // ignore
1547
}
1548            notifyModifyStatus.setModificationVetoed(false);
1549        }
1550    }
1551
1552    public void atomicUndo() {
1553        breakAtomicLock();
1554    }
1555    
1556    public void addAtomicLockListener(AtomicLockListener l) {
1557        listenerList.add(AtomicLockListener.class, l);
1558    }
1559    
1560    public void removeAtomicLockListener(AtomicLockListener l) {
1561        listenerList.remove(AtomicLockListener.class, l);
1562    }
1563    
1564    private void fireAtomicLock(AtomicLockEvent evt) {
1565        EventListener JavaDoc[] listeners = listenerList.getListeners(AtomicLockListener.class);
1566        int cnt = listeners.length;
1567        for (int i = 0; i < cnt; i++) {
1568            ((AtomicLockListener)listeners[i]).atomicLock(evt);
1569        }
1570    }
1571    
1572    private void fireAtomicUnlock(AtomicLockEvent evt) {
1573        EventListener JavaDoc[] listeners = listenerList.getListeners(AtomicLockListener.class);
1574        int cnt = listeners.length;
1575        for (int i = 0; i < cnt; i++) {
1576            ((AtomicLockListener)listeners[i]).atomicUnlock(evt);
1577        }
1578    }
1579    
1580    protected final int getAtomicDepth() {
1581        return atomicDepth;
1582    }
1583
1584    public void addDocumentListener(DocumentListener JavaDoc listener) {
1585    if (!org.netbeans.lib.editor.util.swing.DocumentUtilities.addPriorityDocumentListener(
1586                this, listener, DocumentListenerPriority.DEFAULT))
1587            super.addDocumentListener(listener);
1588    }
1589
1590    public void removeDocumentListener(DocumentListener JavaDoc listener) {
1591    if (!org.netbeans.lib.editor.util.swing.DocumentUtilities.removePriorityDocumentListener(
1592                this, listener, DocumentListenerPriority.DEFAULT))
1593            super.removeDocumentListener(listener);
1594    }
1595
1596    protected BaseDocumentEvent createDocumentEvent(int pos, int length,
1597            DocumentEvent.EventType JavaDoc type) {
1598        return new BaseDocumentEvent(this, pos, length, type);
1599    }
1600    
1601    /**
1602     * Set or clear a special document listener that gets notified
1603     * after the modification and that is allowed to do further
1604     * mutations to the document.
1605     * <br>
1606     * Additional mutations will be made in a single atomic transaction
1607     * with an original mutation.
1608     * <br>
1609     * This functionality may be used for example by code templates
1610     * to synchronize other regions of the document with the one
1611     * currently being modified.
1612     * <br>
1613     * If there is an active post modification document listener
1614     * then each document modification is encapsulated in an atomic lock
1615     * transaction automatically to allow further changes inside a transaction.
1616     */

1617    public void setPostModificationDocumentListener(DocumentListener JavaDoc listener) {
1618        this.postModificationDocumentListener = listener;
1619    }
1620
1621    /** Was the document modified by either insert/remove
1622    * but not the initial read)?
1623    */

1624    public boolean isModified() {
1625        return modified;
1626    }
1627
1628    /**
1629     * Get the layer with the specified name. Using of <code>DrawLayer</code>s
1630     * has been deprecated.
1631     *
1632     * @deprecated Please use Highlighting SPI instead, for details see
1633     * <a HREF="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a>.
1634     */

1635    public DrawLayer findLayer(String JavaDoc layerName) {
1636        return drawLayerList.findLayer(layerName);
1637    }
1638
1639    /**
1640     * Using of <code>DrawLayer</code>s has been deprecated.
1641     *
1642     * @deprecated Please use Highlighting SPI instead, for details see
1643     * <a HREF="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a>.
1644     */

1645    public boolean addLayer(DrawLayer layer, int visibility) {
1646        if (drawLayerList.add(layer, visibility)) {
1647            BaseDocumentEvent evt = createDocumentEvent(0, 0, DocumentEvent.EventType.CHANGE);
1648            evt.addEdit(new BaseDocumentEvent.DrawLayerChange(layer.getName(), visibility));
1649            fireChangedUpdate(evt);
1650            return true;
1651        } else {
1652            return false;
1653        }
1654    }
1655
1656    final DrawLayerList getDrawLayerList() {
1657        return drawLayerList;
1658    }
1659
1660    private LineRootElement getLineRootElement() {
1661        return lineRootElement;
1662    }
1663
1664    public Element JavaDoc getParagraphElement(int pos) {
1665        return getLineRootElement().getElement(
1666                   getLineRootElement().getElementIndex(pos));
1667    }
1668
1669    /** Returns object which represent list of annotations which are
1670     * attached to this document.
1671     * @return object which represent attached annotations
1672     */

1673    public Annotations getAnnotations() {
1674        synchronized (annotationsLock) {
1675            if (annotations == null) {
1676                annotations = new Annotations(this);
1677            }
1678            return annotations;
1679        }
1680    }
1681    
1682    /**
1683     * @see LineRootElement#prepareSyntax()
1684     */

1685    void prepareSyntax(Segment JavaDoc text, Syntax syntax, int reqPos, int reqLen,
1686    boolean forceLastBuffer, boolean forceNotLastBuffer) throws BadLocationException JavaDoc {
1687        FixLineSyntaxState.prepareSyntax(this, text, syntax, reqPos, reqLen,
1688            forceLastBuffer, forceNotLastBuffer);
1689    }
1690
1691    int getTokenSafeOffset(int offset) {
1692        return FixLineSyntaxState.getTokenSafeOffset(this, offset);
1693    }
1694
1695    /** Get position on line from visual column. This method can be used
1696    * only for superfixed font i.e. all characters of all font styles
1697    * have the same width.
1698    * @param visCol visual column
1699    * @param startLinePos position of line start
1700    * @return position on line for particular x-coord
1701    */

1702    int getOffsetFromVisCol(int visCol, int startLinePos)
1703    throws BadLocationException JavaDoc {
1704        
1705        synchronized (getOffsetFromVisColLock) {
1706            if (startLinePos < 0 || startLinePos >= getLength()) {
1707                throw new BadLocationException JavaDoc("Invalid start line offset", startLinePos); // NOI18N
1708
}
1709            if (visCol <= 0) {
1710                return startLinePos;
1711            }
1712            if (visColPosFwdFinder == null) {
1713                visColPosFwdFinder = new FinderFactory.VisColPosFwdFinder();
1714            }
1715            visColPosFwdFinder.setVisCol(visCol);
1716            visColPosFwdFinder.setTabSize(getTabSize());
1717            int pos = find(visColPosFwdFinder, startLinePos, -1);
1718            return (pos != -1)
1719                ? pos
1720                : Utilities.getRowEnd(this, startLinePos);
1721        }
1722    }
1723
1724    /** Get visual column from position. This method can be used
1725    * only for superfixed font i.e. all characters of all font styles
1726    * have the same width.
1727    * @param pos position for which the visual column should be returned
1728    * the function itself computes the begining of the line first
1729    */

1730    int getVisColFromPos(int pos) throws BadLocationException JavaDoc {
1731        synchronized (getVisColFromPosLock) {
1732            if (pos < 0 || pos > getLength()) {
1733                throw new BadLocationException JavaDoc("Invalid offset", pos); // NOI18N
1734
}
1735
1736            if (posVisColFwdFinder == null) {
1737                posVisColFwdFinder = new FinderFactory.PosVisColFwdFinder();
1738            }
1739
1740            int startLinePos = Utilities.getRowStart(this, pos);
1741            posVisColFwdFinder.setTabSize(getTabSize());
1742            find(posVisColFwdFinder, startLinePos, pos);
1743            return posVisColFwdFinder.getVisCol();
1744        }
1745    }
1746    
1747    protected Dictionary JavaDoc createDocumentProperties(Dictionary JavaDoc origDocumentProperties) {
1748        return new LazyPropertyMap(origDocumentProperties);
1749    }
1750
1751    public String JavaDoc toString() {
1752        return super.toString() + ", kitClass=" + getKitClass() // NOI18N
1753
+ ", docLen=" + getLength(); // NOI18N
1754
}
1755    
1756    /** Detailed debug info about the document */
1757    public String JavaDoc toStringDetail() {
1758        return toString();
1759    }
1760
1761    /** Compound edit that write-locks the document for the whole processing
1762     * of its undo operation.
1763     */

1764    class AtomicCompoundEdit extends CompoundEdit JavaDoc {
1765        
1766        private UndoableEdit JavaDoc previousEdit;
1767        
1768        public void undo() throws CannotUndoException JavaDoc {
1769            atomicLock();
1770            try {
1771                super.undo();
1772            } finally {
1773                atomicUnlock();
1774            }
1775
1776            if (previousEdit != null) {
1777                previousEdit.undo();
1778            }
1779            
1780        }
1781        
1782        public void redo() throws CannotRedoException JavaDoc {
1783            if (previousEdit != null) {
1784                previousEdit.redo();
1785            }
1786            
1787            atomicLock();
1788            try {
1789                super.redo();
1790            } finally {
1791                atomicUnlock();
1792            }
1793        }
1794        
1795        public void die() {
1796            super.die();
1797            
1798            if (previousEdit != null) {
1799                previousEdit.die();
1800                previousEdit = null;
1801            }
1802        }
1803        
1804        public int size() {
1805            return edits.size();
1806        }
1807        
1808        public boolean replaceEdit(UndoableEdit JavaDoc anEdit) {
1809            UndoableEdit JavaDoc childEdit;
1810            if (size() == 1 && ((childEdit = (UndoableEdit JavaDoc)getEdits().get(0)) instanceof BaseDocumentEvent)) {
1811                BaseDocumentEvent childEvt = (BaseDocumentEvent)childEdit;
1812                if (anEdit instanceof BaseDocument.AtomicCompoundEdit) {
1813                    BaseDocument.AtomicCompoundEdit compEdit
1814                            = (BaseDocument.AtomicCompoundEdit)anEdit;
1815
1816                    if (!undoMergeReset && compEdit.getEdits().size() == 1) {
1817                        UndoableEdit JavaDoc edit = (UndoableEdit JavaDoc)compEdit.getEdits().get(0);
1818                        if (edit instanceof BaseDocumentEvent && childEvt.canMerge((BaseDocumentEvent)edit)) {
1819                            previousEdit = anEdit;
1820                            return true;
1821                        }
1822                    }
1823                } else if (anEdit instanceof BaseDocumentEvent) {
1824                    BaseDocumentEvent evt = (BaseDocumentEvent)anEdit;
1825
1826                    if (!undoMergeReset && childEvt.canMerge(evt)) {
1827                        previousEdit = anEdit;
1828                        return true;
1829                    }
1830                }
1831            }
1832            undoMergeReset = false;
1833            return false;
1834        }
1835        
1836        java.util.Vector JavaDoc getEdits() {
1837            return edits;
1838        }
1839
1840    }
1841    
1842    /** Property evaluator is useful for lazy evaluation
1843     * of properties of the document when
1844     * {@link javax.swing.text.Document#getProperty(java.lang.String)}
1845     * is called.
1846     */

1847    public interface PropertyEvaluator {
1848        
1849        /** Get the real value of the property */
1850        public Object JavaDoc getValue();
1851        
1852    }
1853    
1854    protected static class LazyPropertyMap extends Hashtable JavaDoc {
1855        
1856        protected LazyPropertyMap(Dictionary JavaDoc dict) {
1857            super(5);
1858            
1859            Enumeration JavaDoc en = dict.keys();
1860            while (en.hasMoreElements()) {
1861                Object JavaDoc key = en.nextElement();
1862                put(key, dict.get(key));
1863            }
1864        }
1865        
1866        public Object JavaDoc get(Object JavaDoc key) {
1867            Object JavaDoc val = super.get(key);
1868            if (val instanceof PropertyEvaluator) {
1869                val = ((PropertyEvaluator)val).getValue();
1870            }
1871            
1872            return val;
1873        }
1874        
1875    }
1876
1877    private static final class MarksStorageUndo extends AbstractUndoableEdit JavaDoc {
1878        
1879        private DocumentEvent JavaDoc evt;
1880        
1881        MarksStorageUndo(DocumentEvent JavaDoc evt) {
1882            this.evt = evt;
1883        }
1884        
1885        private int getLength() {
1886            int length = evt.getLength();
1887            if (evt.getType() == DocumentEvent.EventType.REMOVE) {
1888                length = -length;
1889            }
1890            return length;
1891        }
1892        
1893        void updateMarksStorage() {
1894            BaseDocument doc = (BaseDocument)evt.getDocument();
1895            // Update document's compatible marks storage - no undo
1896
doc.marksStorage.update(evt.getOffset(), getLength(), null);
1897        }
1898        
1899        public void undo() throws CannotUndoException JavaDoc {
1900            BaseDocument doc = (BaseDocument)evt.getDocument();
1901            // Update document's compatible marks storage - no undo
1902
doc.marksStorage.update(evt.getOffset(), -getLength(), null);
1903            super.undo();
1904        }
1905        
1906        public void redo() throws CannotRedoException JavaDoc {
1907            updateMarksStorage();
1908            super.redo();
1909        }
1910        
1911
1912    }
1913
1914    private static final class NotifyModifyStatus {
1915        /**
1916         * Listener instance that was modified prior obtaining
1917         * of the document lock.
1918         * If there will be no modification performed
1919         * the notifyUnmodify() needs to be called to the same listener
1920         * instance.
1921         */

1922        private final VetoableChangeListener JavaDoc beforeModificationListener;
1923        
1924        /**
1925         * When true then the modification ended by veto exception
1926         * so all the future modifications should be prohibited
1927         * by ending with a BadLocationException.
1928         * <br>
1929         * The atomicLock() itself cannot throw the BadLocationException
1930         * so the unallowed modification can't be notified earlier.
1931         */

1932        private boolean modificationVetoed;
1933
1934        NotifyModifyStatus(BaseDocument document) {
1935            beforeModificationListener = (VetoableChangeListener JavaDoc)document.getProperty(BEFORE_MODIFICATION_LISTENER);
1936        }
1937        
1938        public boolean isModificationVetoed() {
1939            return modificationVetoed;
1940        }
1941
1942        public void setModificationVetoed(boolean modificationVetoed) {
1943            this.modificationVetoed = modificationVetoed;
1944        }
1945        
1946        public VetoableChangeListener JavaDoc getBeforeModificationListener() {
1947            return beforeModificationListener;
1948        }
1949    }
1950    
1951}
1952
Popular Tags