001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.text.edits;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.List;
015:
016: import org.eclipse.core.runtime.Assert;
017:
018: import org.eclipse.jface.text.BadLocationException;
019: import org.eclipse.jface.text.IDocument;
020:
021: /**
022: * A <code>TextEditProcessor</code> manages a set of edits and applies
023: * them as a whole to an <code>IDocument</code>.
024: * <p>
025: * This class isn't intended to be subclassed.</p>
026: *
027: * @see org.eclipse.text.edits.TextEdit#apply(IDocument)
028: *
029: * @since 3.0
030: */
031: public class TextEditProcessor {
032:
033: private IDocument fDocument;
034: private TextEdit fRoot;
035: private int fStyle;
036:
037: private boolean fChecked;
038: private MalformedTreeException fException;
039:
040: private List fSourceEdits;
041:
042: /**
043: * Constructs a new edit processor for the given
044: * document.
045: *
046: * @param document the document to manipulate
047: * @param root the root of the text edit tree describing
048: * the modifications. By passing a text edit a a text edit
049: * processor the ownership of the edit is transfered to the
050: * text edit processors. Clients must not modify the edit
051: * (e.g adding new children) any longer.
052: *
053: * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS})
054: */
055: public TextEditProcessor(IDocument document, TextEdit root,
056: int style) {
057: this (document, root, style, false);
058: }
059:
060: private TextEditProcessor(IDocument document, TextEdit root,
061: int style, boolean secondary) {
062: Assert.isNotNull(document);
063: Assert.isNotNull(root);
064: fDocument = document;
065: fRoot = root;
066: if (fRoot instanceof MultiTextEdit)
067: ((MultiTextEdit) fRoot).defineRegion(0);
068: fStyle = style;
069: if (secondary) {
070: fChecked = true;
071: fSourceEdits = new ArrayList();
072: }
073: }
074:
075: /**
076: * Creates a special internal processor used to during source computation inside
077: * move source and copy source edits
078: *
079: * @param document the document to be manipulated
080: * @param root the edit tree
081: * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS})
082: * @return a secondary text edit processor
083: * @since 3.1
084: */
085: static TextEditProcessor createSourceComputationProcessor(
086: IDocument document, TextEdit root, int style) {
087: return new TextEditProcessor(document, root, style, true);
088: }
089:
090: /**
091: * Returns the document to be manipulated.
092: *
093: * @return the document
094: */
095: public IDocument getDocument() {
096: return fDocument;
097: }
098:
099: /**
100: * Returns the edit processor's root edit.
101: *
102: * @return the processor's root edit
103: */
104: public TextEdit getRoot() {
105: return fRoot;
106: }
107:
108: /**
109: * Returns the style bits of the text edit processor
110: *
111: * @return the style bits
112: * @see TextEdit#CREATE_UNDO
113: * @see TextEdit#UPDATE_REGIONS
114: */
115: public int getStyle() {
116: return fStyle;
117: }
118:
119: /**
120: * Checks if the processor can execute all its edits.
121: *
122: * @return <code>true</code> if the edits can be executed. Return <code>false
123: * </code>otherwise. One major reason why edits cannot be executed are wrong
124: * offset or length values of edits. Calling perform in this case will very
125: * likely end in a <code>BadLocationException</code>.
126: */
127: public boolean canPerformEdits() {
128: try {
129: fRoot.dispatchCheckIntegrity(this );
130: fChecked = true;
131: } catch (MalformedTreeException e) {
132: fException = e;
133: return false;
134: }
135: return true;
136: }
137:
138: /**
139: * Executes the text edits.
140: *
141: * @return an object representing the undo of the executed edits
142: * @exception MalformedTreeException is thrown if the edit tree isn't
143: * in a valid state. This exception is thrown before any edit is executed.
144: * So the document is still in its original state.
145: * @exception BadLocationException is thrown if one of the edits in the
146: * tree can't be executed. The state of the document is undefined if this
147: * exception is thrown.
148: */
149: public UndoEdit performEdits() throws MalformedTreeException,
150: BadLocationException {
151: if (!fChecked) {
152: fRoot.dispatchCheckIntegrity(this );
153: } else {
154: if (fException != null)
155: throw fException;
156: }
157: return fRoot.dispatchPerformEdits(this );
158: }
159:
160: /*
161: * Class isn't intended to be sub-lcassed
162: */
163: protected boolean considerEdit(TextEdit edit) {
164: return true;
165: }
166:
167: //---- checking --------------------------------------------------------------------
168:
169: void checkIntegrityDo() throws MalformedTreeException {
170: fSourceEdits = new ArrayList();
171: fRoot.traverseConsistencyCheck(this , fDocument, fSourceEdits);
172: if (fRoot.getExclusiveEnd() > fDocument.getLength())
173: throw new MalformedTreeException(
174: null,
175: fRoot,
176: TextEditMessages
177: .getString("TextEditProcessor.invalid_length")); //$NON-NLS-1$
178: }
179:
180: void checkIntegrityUndo() {
181: if (fRoot.getExclusiveEnd() > fDocument.getLength())
182: throw new MalformedTreeException(
183: null,
184: fRoot,
185: TextEditMessages
186: .getString("TextEditProcessor.invalid_length")); //$NON-NLS-1$
187: }
188:
189: //---- execution --------------------------------------------------------------------
190:
191: UndoEdit executeDo() throws BadLocationException {
192: UndoCollector collector = new UndoCollector(fRoot);
193: try {
194: if (createUndo())
195: collector.connect(fDocument);
196: computeSources();
197: fRoot.traverseDocumentUpdating(this , fDocument);
198: if (updateRegions()) {
199: fRoot.traverseRegionUpdating(this , fDocument, 0, false);
200: }
201: } finally {
202: collector.disconnect(fDocument);
203: }
204: return collector.undo;
205: }
206:
207: private void computeSources() {
208: for (Iterator iter = fSourceEdits.iterator(); iter.hasNext();) {
209: List list = (List) iter.next();
210: if (list != null) {
211: for (Iterator edits = list.iterator(); edits.hasNext();) {
212: TextEdit edit = (TextEdit) edits.next();
213: edit.traverseSourceComputation(this , fDocument);
214: }
215: }
216: }
217: }
218:
219: UndoEdit executeUndo() throws BadLocationException {
220: UndoCollector collector = new UndoCollector(fRoot);
221: try {
222: if (createUndo())
223: collector.connect(fDocument);
224: TextEdit[] edits = fRoot.getChildren();
225: for (int i = edits.length - 1; i >= 0; i--) {
226: edits[i].performDocumentUpdating(fDocument);
227: }
228: } finally {
229: collector.disconnect(fDocument);
230: }
231: return collector.undo;
232: }
233:
234: private boolean createUndo() {
235: return (fStyle & TextEdit.CREATE_UNDO) != 0;
236: }
237:
238: private boolean updateRegions() {
239: return (fStyle & TextEdit.UPDATE_REGIONS) != 0;
240: }
241: }
|