|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor; import java.util.Hashtable; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Map; import java.io.Reader; import java.io.Writer; import java.io.IOException; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.util.EventListener; import java.util.HashMap; import javax.swing.event.UndoableEditListener; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultEditorKit; import javax.swing.text.Position; import javax.swing.text.Element; import javax.swing.text.AttributeSet; import javax.swing.text.AbstractDocument; import javax.swing.text.StyleConstants; import javax.swing.event.EventListenerList; import javax.swing.event.DocumentEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.text.Segment; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.UndoableEdit; import javax.swing.undo.CompoundEdit; import javax.swing.undo.CannotUndoException; import javax.swing.undo.CannotRedoException; /** * Document implementation * * @author Miloslav Metelka * @version 1.00 */ public class BaseDocument extends AbstractDocument implements SettingsChangeListener, AtomicLockDocument { /** Registry identification property */ public static final String ID_PROP = "id"; // NOI18N /** Line separator property for reading files in */ public static final String READ_LINE_SEPARATOR_PROP = DefaultEditorKit.EndOfLineStringProperty; /** Line separator property for writing content into files. If not set * the writing defaults to the READ_LINE_SEPARATOR_PROP. */ public static final String WRITE_LINE_SEPARATOR_PROP = "write-line-separator"; // NOI18N /** File name property */ public static final String FILE_NAME_PROP = "file-name"; // NOI18N /** Wrap search mark property */ public static final String WRAP_SEARCH_MARK_PROP = "wrap-search-mark"; // NOI18N /** Undo manager property. This can be used to implement undo * in a simple way. Default undo and redo actions try to get this * property and perform undo and redo through it. */ public static final String UNDO_MANAGER_PROP = "undo-manager"; // NOI18N /** Kit class property. This can become useful for getting * the settings that logicaly belonging to the document. */ public static final String KIT_CLASS_PROP = "kit-class"; // NOI18N /** String forward finder property */ public static final String STRING_FINDER_PROP = "string-finder"; // NOI18N /** String backward finder property */ public static final String STRING_BWD_FINDER_PROP = "string-bwd-finder"; // NOI18N /** Highlight search finder property. */ public static final String BLOCKS_FINDER_PROP = "blocks-finder"; // NOI18N /** Maximum line width encountered during the initial read operation. * This is filled by Analyzer and used by UI to set the correct initial width * of the component. * Values: java.lang.Integer */ public static final String LINE_LIMIT_PROP = "line-limit"; // NOI18N /** Size of the line batch. Line batch can be used at various places * especially when processing lines by syntax scanner. */ public static final String LINE_BATCH_SIZE = "line-batch-size"; // NOI18N /** Line separator is marked by CR (Macintosh) */ public static final String LS_CR = "\r"; // NOI18N /** Line separator is marked by LF (Unix) */ public static final String LS_LF = "\n"; // NOI18N /** Line separator is marked by CR and LF (Windows) */ public static final String LS_CRLF = "\r\n"; // NOI18N /** Maximum of concurrent read threads (other will wait until * one of these will leave). */ private static final int MAX_READ_THREADS = 10; /** Write lock without write lock */ private static final String WRITE_LOCK_MISSING = "extWriteUnlock() without extWriteLock()"; // NOI18N private static final Object annotationsLock = new Object(); private static final Object bookmarksLock = new Object(); private static final Object getVisColFromPosLock = new Object(); private static final Object getOffsetFromVisColLock = new Object(); /** Debug modifications performed on the document */ private static final boolean debug = Boolean.getBoolean("netbeans.debug.editor.document"); // NOI18N /** Debug the stack of calling of the insert/remove */ private static final boolean debugStack = Boolean.getBoolean("netbeans.debug.editor.document.stack"); // NOI18N /** Debug the StreamDescriptionProperty during read() */ private static final boolean debugRead = Boolean.getBoolean("netbeans.debug.editor.document.read"); // NOI18N /** How many spaces should be displayed instead of '\t' character */ private int tabSize = SettingsDefaults.defaultTabSize.intValue(); /** Size of one indentation level. If this variable is null (value * is not set in Settings, then the default algorithm will be used. */ private Integer shiftWidth; /** How many times current writer requested writing */ private int writeDepth; /** How many times atomic writer requested writing */ private int atomicDepth; /* Was the document initialized by reading? */ protected boolean inited; /* Was the document modified by doing inert/remove */ protected boolean modified; /** Listener to changes in find support */ PropertyChangeListener findSupportListener; /** Default element - lazily inited */ protected Element defaultRootElem; private SyntaxSupport syntaxSupport; /** Layer list for document level layers */ private DrawLayerList drawLayerList = new DrawLayerList(); /** Chain of document level bookmarks */ private MarkChain bookmarkChain; /** Reset merging next created undoable edit to the last one. */ boolean undoMergeReset; /** Kit class stored here */ Class kitClass; /** Undo event for atomic events is fired after the successful * atomic operation is finished. The changes are stored in this variable * during the atomic operation. If the operation is broken, these edits * are used to restore previous state. */ private AtomicCompoundEdit atomicEdits; private Acceptor identifierAcceptor; private Acceptor whitespaceAcceptor; private ArrayList syntaxList = new ArrayList(); /** List of the positions used by storePosition() */ private ArrayList posList = new ArrayList(); /** List of the integers marking the free positions in the posList. */ private ArrayList posFreeList = new ArrayList(); /** Root element of line elements representation */ protected LineRootElement lineRootElement; /** Last document event to be undone. The field is filled * by the lastly done modification undoable edit. * BaseDocumentEvent.canUndo() checks this flag. */ UndoableEdit lastModifyUndoEdit; // #8692 check last modify undo edit /** List of annotations for this document. */ private Annotations annotations; /** List of bookmarks attached to this document */ private Bookmarks bookmarks; /* Bug #6258 during composing I18N text using input method the Undoable Edit * actions must be disabled so only the final push (and not all intermediate * ones) of the I18N word will be stored as undoable edit. */ private boolean composedText = false; /** * Map of [multi-mark, Mark-instance] pairs. * These multi-marks need to be stored separately and not be undone/redone. */ final Map marks = new HashMap(); final MarkVector marksStorage = new MarkVector(); /** Finder for visual x-coord to position conversion */ private FinderFactory.VisColPosFwdFinder visColPosFwdFinder; /** Finder for position to x-coord conversion */ private FinderFactory.PosVisColFwdFinder posVisColFwdFinder; /** Atomic lock event instance shared by all the atomic lock firings done for this document */ private AtomicLockEvent atomicLockEventInstance = new AtomicLockEvent(this); private FixLineSyntaxState fixLineSyntaxState; private UndoableEdit removeUpdateLineUndo; private Object[] atomicLockListenerList; /** Create base document with a specified syntax. * @param kitClass class used to initialize this document with proper settings * category based on the editor kit for which this document is created * @param syntax syntax scanner to use with this document */ public BaseDocument(Class kitClass, boolean addToRegistry) { super(new DocumentContent()); this.kitClass = kitClass; setDocumentProperties(createDocumentProperties(getDocumentProperties())); putProperty(GapStart.class, getDocumentContent()); lineRootElement = new LineRootElement(this); settingsChange(null); // initialize variables from settings Settings.addSettingsChangeListener(this); // Line separators default to platform ones putProperty(READ_LINE_SEPARATOR_PROP, Analyzer.getPlatformLS()); bookmarkChain = new MarkChain(this, DrawLayerFactory.BOOKMARK_LAYER_NAME); // Add document draw-layers addLayer(new DrawLayerFactory.SyntaxLayer(), DrawLayerFactory.SYNTAX_LAYER_VISIBILITY); addLayer(new DrawLayerFactory.HighlightSearchLayer(), DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_VISIBILITY); addLayer(new DrawLayerFactory.BookmarkLayer(), DrawLayerFactory.BOOKMARK_LAYER_VISIBILITY); // Additional initialization of the document through the kit BaseKit kit = BaseKit.getKit(kitClass); if (kit != null) { kit.initDocument(this); } // Possibly add the document to registry if (addToRegistry) { Registry.addDocument(this); // add if created thru the kit } // Start listen on find-support findSupportListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { findSupportChange(evt); } }; FindSupport.getFindSupport().addPropertyChangeListener(findSupportListener); findSupportChange(null); // update doc by find settings } private DocumentContent getDocumentContent() { return (DocumentContent)getContent(); } public CharSeq getText() { return getDocumentContent(); } private void findSupportChange(PropertyChangeEvent evt) { // set all finders to null putProperty(STRING_FINDER_PROP, null); putProperty(STRING_BWD_FINDER_PROP, null); putProperty(BLOCKS_FINDER_PROP, null); DrawLayerFactory.HighlightSearchLayer hsl = (DrawLayerFactory.HighlightSearchLayer)findLayer( DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_NAME); Boolean b = (Boolean)FindSupport.getFindSupport().getPropertyNoInit( SettingsNames.FIND_HIGHLIGHT_SEARCH); hsl.setEnabled((b != null) ? b.booleanValue() : false); fireChangedUpdate(createDocumentEvent(0, getLength(), DocumentEvent.EventType.CHANGE)); // refresh whole document } /** Called when settings were changed. The method is called * also in constructor, so the code must count with the evt being null. */ public void settingsChange(SettingsChangeEvent evt) { String settingName = (evt != null) ? evt.getSettingName() : null; if (settingName == null || SettingsNames.TAB_SIZE.equals(settingName)) { tabSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.TAB_SIZE, SettingsDefaults.defaultTabSize); } if (settingName == null || SettingsNames.INDENT_SHIFT_WIDTH.equals(settingName)) { Object shw = Settings.getValue(kitClass, SettingsNames.INDENT_SHIFT_WIDTH); if (shw instanceof Integer) { // currently only Integer values are supported shiftWidth = (Integer)shw; } } if (settingName == null || SettingsNames.READ_BUFFER_SIZE.equals(settingName)) { int readBufferSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.READ_BUFFER_SIZE, SettingsDefaults.defaultReadBufferSize); putProperty(SettingsNames.READ_BUFFER_SIZE, new Integer(readBufferSize)); } if (settingName == null || SettingsNames.WRITE_BUFFER_SIZE.equals(settingName)) { int writeBufferSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.WRITE_BUFFER_SIZE, SettingsDefaults.defaultWriteBufferSize); putProperty(SettingsNames.WRITE_BUFFER_SIZE, new Integer(writeBufferSize)); } if (settingName == null || SettingsNames.MARK_DISTANCE.equals(settingName)) { int markDistance = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.MARK_DISTANCE, SettingsDefaults.defaultMarkDistance); putProperty(SettingsNames.MARK_DISTANCE, new Integer(markDistance)); } if (settingName == null || SettingsNames.MAX_MARK_DISTANCE.equals(settingName)) { int maxMarkDistance = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.MAX_MARK_DISTANCE, SettingsDefaults.defaultMaxMarkDistance); putProperty(SettingsNames.MAX_MARK_DISTANCE, new Integer(maxMarkDistance)); } if (settingName == null || SettingsNames.MIN_MARK_DISTANCE.equals(settingName)) { int minMarkDistance = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.MIN_MARK_DISTANCE, SettingsDefaults.defaultMinMarkDistance); putProperty(SettingsNames.MIN_MARK_DISTANCE, new Integer(minMarkDistance)); } if (settingName == null || SettingsNames.READ_MARK_DISTANCE.equals(settingName)) { int readMarkDistance = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.READ_MARK_DISTANCE, SettingsDefaults.defaultReadMarkDistance); putProperty(SettingsNames.READ_MARK_DISTANCE, new Integer(readMarkDistance)); } if (settingName == null || SettingsNames.SYNTAX_UPDATE_BATCH_SIZE.equals(settingName)) { int syntaxUpdateBatchSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.SYNTAX_UPDATE_BATCH_SIZE, SettingsDefaults.defaultSyntaxUpdateBatchSize); putProperty(SettingsNames.SYNTAX_UPDATE_BATCH_SIZE, new Integer(syntaxUpdateBatchSize)); } if (settingName == null || SettingsNames.LINE_BATCH_SIZE.equals(settingName)) { int lineBatchSize = SettingsUtil.getPositiveInteger(kitClass, SettingsNames.LINE_BATCH_SIZE, SettingsDefaults.defaultLineBatchSize); putProperty(SettingsNames.LINE_BATCH_SIZE, new Integer(lineBatchSize)); } if (settingName == null || SettingsNames.IDENTIFIER_ACCEPTOR.equals(settingName)) { identifierAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.IDENTIFIER_ACCEPTOR, AcceptorFactory.LETTER_DIGIT); } if (settingName == null || SettingsNames.WHITESPACE_ACCEPTOR.equals(settingName)) { whitespaceAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.WHITESPACE_ACCEPTOR, AcceptorFactory.WHITESPACE); } boolean stopOnEOL = SettingsUtil.getBoolean(kitClass, SettingsNames.WORD_MOVE_NEWLINE_STOP, true); if (settingName == null || SettingsNames.NEXT_WORD_FINDER.equals(settingName)) { putProperty(SettingsNames.NEXT_WORD_FINDER, SettingsUtil.getValue(kitClass, SettingsNames.NEXT_WORD_FINDER, new FinderFactory.NextWordFwdFinder(this, stopOnEOL, false))); } if (settingName == null || SettingsNames.PREVIOUS_WORD_FINDER.equals(settingName)) { putProperty(SettingsNames.PREVIOUS_WORD_FINDER, SettingsUtil.getValue(kitClass, SettingsNames.PREVIOUS_WORD_FINDER, new FinderFactory.PreviousWordBwdFinder(this, stopOnEOL, false))); } } Syntax getFreeSyntax() { synchronized (syntaxList) { int cnt = syntaxList.size(); return (cnt > 0) ? (Syntax)syntaxList.remove(cnt - 1) : BaseKit.getKit(kitClass).createSyntax(this); } } void releaseSyntax(Syntax syntax) { synchronized (syntaxList) { syntaxList.add(syntax); } } /** Get the formatter for this document. */ public Formatter getFormatter() { return Formatter.getFormatter(kitClass); } public SyntaxSupport getSyntaxSupport() { if (syntaxSupport == null) { syntaxSupport = BaseKit.getKit(kitClass).createSyntaxSupport(this); } return syntaxSupport; } /** Perform any generic text processing. The advantage of this method * is that it allows the text to processed in line batches. The initial * size of the batch is given by the SettingsNames.LINE_BATCH_SIZE. * The TextBatchProcessor.processTextBatch() method is called for every * text batch. If the method returns true, it means the processing should * continue with the next batch of text which will have double line count * compared to the previous one. This guarantees there will be not too many * batches so the processing should be more efficient. * @param tbp text batch processor to be used to process the text batches * @param startPos starting position of the processing. * @param endPos ending position of the processing. This can be -1 to signal * the end of document. If the endPos is lower than startPos then the batches * are created in the backward direction. * @return the returned value from the last tpb.processTextBatch() call. * The -1 will be returned for (startPos == endPos). */ public int processText(TextBatchProcessor tbp, int startPos, int endPos) throws BadLocationException { if (endPos == -1) { endPos = getLength(); } int batchLineCnt = ((Integer)getProperty(SettingsNames.LINE_BATCH_SIZE)).intValue(); int batchStart = startPos; int ret = -1; if (startPos < endPos) { // batching in forward direction while (ret < 0 && batchStart < endPos) { int batchEnd = Math.min(Utilities.getRowStart(this, batchStart, batchLineCnt), endPos); if (batchEnd == -1) { // getRowStart() returned -1 batchEnd = endPos; } ret = tbp.processTextBatch(this, batchStart, batchEnd, (batchEnd == endPos)); batchLineCnt *= 2; // double the scanned area batchStart = batchEnd; } } else { while (ret < 0 && batchStart > endPos) { int batchEnd = Math.max(Utilities.getRowStart(this, batchStart, -batchLineCnt), endPos); ret = tbp.processTextBatch(this, batchStart, batchEnd, (batchEnd == endPos)); batchLineCnt *= 2; // double the scanned area batchStart = batchEnd; } } return ret; } public boolean isIdentifierPart(char ch) { return identifierAcceptor.accept(ch); } public boolean isWhitespace(char ch) { return whitespaceAcceptor.accept(ch); } /** Create the mark for the given position * and store it in the list. The position can * be later retrieved through its ID. */ int storePosition(int pos) throws BadLocationException { MultiMark mark = getDocumentContent().createBiasMark(pos, Position.Bias.Forward); int ind; if (posFreeList.size() > 0) { ind = ((Integer)posFreeList.remove(posFreeList.size() - 1)).intValue(); posList.set(ind, mark); } else { // no free indexes ind = posList.size(); posList.add(mark); } return ind; } int getStoredPosition(int posID) { if (posID < 0 || posID >= posList.size()) { return -1; } MultiMark mark = (MultiMark)posList.get(posID); return (mark != null) ? mark.getOffset() : -1; } void removeStoredPosition(int posID) { if (posID >= 0 || posID < posList.size()) { Mark mark = (Mark)posList.get(posID); posList.set(posID, null); // clear the index posFreeList.add(new Integer(posID)); // Remove the mark #19429 try { mark.remove(); } catch (InvalidMarkException e) { } } } /** Inserts string into document */ public void insertString(int offset, String text, AttributeSet a) throws BadLocationException { if (text == null || text.length() == 0) { return; } // Check offset correctness if (offset < 0 || offset > getLength()) { throw new BadLocationException("Wrong insert position " + offset, offset); // NOI18N } // possible CR-LF conversion text = Analyzer.convertLSToLF(text); // Perform the insert extWriteLock(); try { preInsertCheck(offset, text, a); // Do the real insert into the content UndoableEdit edit = getContent().insertString(offset, text); if (debug) { System.err.println("BaseDocument.insertString(): doc=" + this // NOI18N + (modified ? "" : " - first modification") // NOI18N + ", offset=" + offset // NOI18N + ", text='" + text + "'" // NOI18N ); } if (debugStack) { Thread.dumpStack(); } BaseDocumentEvent evt = createDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT); preInsertUpdate(evt, a); if (edit != null) { evt.addEdit(edit); lastModifyUndoEdit = edit; // #8692 check last modify undo edit } modified = true; if (atomicDepth > 0) { if (atomicEdits == null) { atomicEdits = new AtomicCompoundEdit(); } atomicEdits.addEdit(evt); // will be added } insertUpdate(evt, a); evt.end(); fireInsertUpdate(evt); boolean isComposedText = ((a != null) && (a.isDefined(StyleConstants.ComposedTextAttribute))); if (composedText && !isComposedText) composedText = false; if (!composedText && isComposedText) composedText = true; if (atomicDepth == 0 && !isComposedText) { // !!! check fireUndoableEditUpdate(new UndoableEditEvent(this, evt)); } } finally { extWriteUnlock(); } } /** Removes portion of a document */ public void remove(int offset, int len) throws BadLocationException { if (len > 0) { extWriteLock(); try { int docLen = getLength(); if (offset < 0 || offset > docLen) { throw new BadLocationException("Wrong remove position " + offset, offset); // NOI18N } if (offset + len > docLen) { throw new BadLocationException("End offset of removed text " // NOI18N + (offset + len) + " > getLength()=" + docLen, // NOI18N offset + len ); // NOI18N } preRemoveCheck(offset, len); BaseDocumentEvent evt = createDocumentEvent(offset, len, DocumentEvent.EventType.REMOVE); removeUpdate(evt); UndoableEdit edit = getContent().remove(offset, len); if (edit != null) { evt.addEdit(edit); lastModifyUndoEdit = edit; // #8692 check last modify undo edit } if (debug) { System.err.println("BaseDocument.remove(): doc=" + this // NOI18N + ", offset=" + offset + ", len=" + len); // NOI18N } if (debugStack) { Thread.dumpStack(); } if (atomicDepth > 0) { // add edits as soon as possible if (atomicEdits == null) { atomicEdits = new AtomicCompoundEdit(); } atomicEdits.addEdit(evt); // will be added } postRemoveUpdate(evt); evt.end(); fireRemoveUpdate(evt); if (atomicDepth == 0 && !composedText) { fireUndoableEditUpdate(new UndoableEditEvent(this, evt)); } } finally { extWriteUnlock(); } } } /** This method is called automatically before the document * insertion occurs and can be used to revoke the insertion before it occurs * by throwing the BadLocationException. * @param offset position where the insertion will be done * @param text string to be inserted * @param a attributes of the inserted text */ protected void preInsertCheck(int offset, String text, AttributeSet a) throws BadLocationException { } /** This method is called automatically before the document * removal occurs and can be used to revoke the removal before it occurs * by throwing the BadLocationException. * @param offset position where the insertion will be done * @param len length of the removal */ protected void preRemoveCheck(int offset, int len) throws BadLocationException { } protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { super.insertUpdate(chng, attr); MarksStorageUndo marksStorageUndo = new MarksStorageUndo(chng); marksStorageUndo.updateMarksStorage(); chng.addEdit(marksStorageUndo); // fix compatible marks UndoableEdit lineUndo = lineRootElement.insertUpdate(chng.getOffset(), chng.getLength()); if (lineUndo != null) { chng.addEdit(lineUndo); } fixLineSyntaxState.update(false); chng.addEdit(fixLineSyntaxState.createAfterLineUndo()); fixLineSyntaxState = null; } protected void preInsertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { fixLineSyntaxState = new FixLineSyntaxState(chng); chng.addEdit(fixLineSyntaxState.createBeforeLineUndo()); } protected void removeUpdate(DefaultDocumentEvent chng) { super.removeUpdate(chng); // Remember the line changes here but add them to chng during postRemoveUpdate() removeUpdateLineUndo = lineRootElement.removeUpdate(chng.getOffset(), chng.getLength()); fixLineSyntaxState = new FixLineSyntaxState(chng); chng.addEdit(fixLineSyntaxState.createBeforeLineUndo()); } protected void postRemoveUpdate(DefaultDocumentEvent chng) { super.postRemoveUpdate(chng); MarksStorageUndo marksStorageUndo = new MarksStorageUndo(chng); marksStorageUndo.updateMarksStorage(); chng.addEdit(marksStorageUndo); // fix compatible marks if (removeUpdateLineUndo != null) { chng.addEdit(removeUpdateLineUndo); removeUpdateLineUndo = null; } fixLineSyntaxState.update(false); chng.addEdit(fixLineSyntaxState.createAfterLineUndo()); fixLineSyntaxState = null; } public String getText(int[] block) throws BadLocationException { return getText(block[0], block[1] - block[0]); } /** * @param pos position of the first character to get. * @param len number of characters to obtain. * @return array with the requested characters. */ public char[] getChars(int pos, int len) throws BadLocationException { char[] chars = new char[len]; getChars(pos, chars, 0, len); return chars; } /** * @param block two-element array with starting and ending offset * @return array with the requested characters. */ public char[] getChars(int[] block) throws BadLocationException { return getChars(block[0], block[1] - block[0]); } /** * @param pos position of the first character to get. * @param ret destination array * @param offset offset in the destination array. * @param len number of characters to obtain. * @return array with the requested characters. */ public void getChars(int pos, char ret[], int offset, int len) throws BadLocationException { DocumentUtilities.copyText(this, pos, pos + len, ret, offset); } /** Find something in document using a finder. * @param finder finder to be used for the search * @param startPos position in the document where the search will start * @param limitPos position where the search will be end with reporting * that nothing was found. */ public int find(Finder finder, int startPos, int limitPos) throws BadLocationException { int docLen = getLength(); if (limitPos == -1) { limitPos = docLen; } if (startPos == -1) { startPos = docLen; } if (finder instanceof AdjustFinder) { if (startPos == limitPos) { // stop immediately finder.reset(); // reset() should be called in all the cases return -1; // must stop here because wouldn't know if fwd/bwd search? } boolean forwardAdjustedSearch = (startPos < limitPos); startPos = ((AdjustFinder)finder).adjustStartPos(this, startPos); limitPos = ((AdjustFinder)finder).adjustLimitPos(this, limitPos); boolean voidSearch = (forwardAdjustedSearch ? (startPos >= limitPos) : (startPos <= limitPos)); if (voidSearch) { finder.reset(); return -1; } } finder.reset(); if (startPos == limitPos) { return -1; } Segment text = DocumentUtilities.SEGMENT_CACHE.getSegment(); try { int gapStart = DocumentUtilities.getGapStart(this); if (gapStart == -1) { throw new IllegalStateException("Cannot get gapStart"); // NOI18N } int pos = startPos; // pos at which the search starts (continues) boolean fwdSearch = (startPos <= limitPos); // forward search if (fwdSearch) { while (pos >= startPos && pos < limitPos) { int p0; // low bound int p1; // upper bound if (pos < gapStart) { // part below gap p0 = startPos; p1 = Math.min(gapStart, limitPos); } else { // part above gap p0 = Math.max(gapStart, startPos); p1 = limitPos; } getText(p0, p1 - p0, text); pos = finder.find(p0 - text.offset, text.array, text.offset, text.offset + text.count, pos, limitPos); if (finder.isFound()) { return pos; } } } else { // backward search limitPos < startPos pos--; // start one char below the upper bound while (limitPos <= pos && pos <= startPos) { int p0; // low bound int p1; // upper bound if (pos < gapStart) { // part below gap p0 = limitPos; p1 = Math.min(gapStart, startPos); } else { // part above gap p0 = Math.max(gapStart, limitPos); p1 = startPos; } getText(p0, p1 - p0, text); pos = finder.find(p0 - text.offset, text.array, text.offset, text.offset + text.count, pos, limitPos); if (finder.isFound()) { return pos; } } } return -1; // position outside bounds => not found } finally { DocumentUtilities.SEGMENT_CACHE.releaseSegment(text); } } /** Fire the change event to repaint the given block of text. */ public void repaintBlock(int startOffset, int endOffset) { BaseDocumentEvent evt = createDocumentEvent(startOffset, endOffset - startOffset, DocumentEvent.EventType.CHANGE); fireChangedUpdate(evt); } public void print(PrintContainer container) { print(container, true, true,0,getLength()); } /** * Print into given container. * * @param container printing container into which the printing will be done. * @param usePrintColoringMap use printing coloring settings instead * of the regular ones. * @param lineNumberEnabled if set to false the line numbers will not be printed. * If set to true the visibility of line numbers depends on the settings * for the line number visibility. * @param startOffset start offset of text to print * @param endOffset end offset of text to print */ public void print(PrintContainer container, boolean usePrintColoringMap, boolean lineNumberEnabled, int startOffset, int endOffset) { readLock(); try { EditorUI editorUI = BaseKit.getKit(kitClass).createPrintEditorUI(this, usePrintColoringMap, lineNumberEnabled); DrawGraphics.PrintDG printDG = new DrawGraphics.PrintDG(container); DrawEngine.getDrawEngine().draw(printDG, editorUI, startOffset, endOffset, 0, 0, Integer.MAX_VALUE); } catch (BadLocationException e) { e.printStackTrace(); } finally { readUnlock(); } } /** Create biased position in document */ public Position createPosition(int offset, Position.Bias bias) throws BadLocationException { return getDocumentContent().createBiasPosition(offset, bias); } MultiMark createMark(int offset) throws BadLocationException { return getDocumentContent().createMark(offset); } MultiMark createBiasMark(int offset, Position.Bias bias) throws BadLocationException { return getDocumentContent().createBiasMark(offset, bias); } /** Return array of root elements - usually only one */ public Element[] getRootElements() { Element[] elems = new Element[1]; elems[0] = getDefaultRootElement(); return elems; } /** Return default root element */ public Element getDefaultRootElement() { if (defaultRootElem == null) { defaultRootElem = getLineRootElement(); } return defaultRootElem; } /** Runs the runnable under read lock. */ public void render(Runnable r) { readLock(); try { r.run(); } finally { readUnlock(); } } /** Runs the runnable under write lock. This is a stronger version * of the runAtomicAsUser() method, because if there any locked sections * in the documents this methods breaks the modification locks and modifies * the document. * If there are any excpeptions thrown during the processing of the runnable, * all the document modifications are rolled back automatically. */ public void runAtomic(Runnable r) { runAtomicAsUser(r); } /** Runs the runnable under write lock. * If there are any excpeptions thrown during the processing of the runnable, * all the document modifications are rolled back automatically. */ public void runAtomicAsUser(Runnable r) { boolean completed = false; atomicLock(); try { r.run(); completed = true; } finally { try { if (!completed) { breakAtomicLock(); } } finally { atomicUnlock(); } } } /** Insert contents of reader at specified position into document. * @param reader reader from which data will be read * @param pos on which position that data will be inserted */ public void read(Reader reader, int pos) throws IOException, BadLocationException { extWriteLock(); try { if (pos < 0 || pos > getLength()) { throw new BadLocationException("BaseDocument.read()", pos); // NOI18N } if (inited || modified) { // was the document already initialized? Analyzer.read(this, reader, pos); } else { // not initialized yet, we can use initialRead() Analyzer.initialRead(this, reader, true); inited = true; // initialized but not modified } if (debugRead) { System.err.println("BaseDocument.read(): StreamDescriptionProperty: "+getProperty(StreamDescriptionProperty)); } } finally { extWriteUnlock(); } } /** Write part of the document into specified writer. * @param writer writer into which data will be written. * @param pos from which position get the data * @param len how many characters write */ public void write(Writer writer, int pos, int len) throws IOException, BadLocationException { readLock(); try { if ((pos < 0) || ((pos + len) > getLength())) { throw new BadLocationException("BaseDocument.write()", pos); // NOI18N } Analyzer.write(this, writer, pos, len); writer.flush(); } finally { readUnlock(); } } /** Invalidate the state-infos in all the syntax-marks * in the whole document. The Syntax can call this method * if it changes its internal state in the way that affects * the future returned tokens. The syntax-state-info in all * the marks is reset and it will be lazily restored when necessary. */ public void invalidateSyntaxMarks() { extWriteLock(); try { FixLineSyntaxState.invalidateAllSyntaxStateInfos(this); repaintBlock(0, getLength()); } finally { extWriteUnlock(); } } /** Get the number of spaces the TAB character ('\t') visually represents. * This is related to |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.