3ea69bc7c5f1fe1f1f5d70da76dcb17334ac19c0
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / corext / textmanipulation / TextBufferEditor.java
1 /*
2  * (c) Copyright IBM Corp. 2000, 2001.
3  * All Rights Reserved.
4  */
5 package net.sourceforge.phpdt.internal.corext.textmanipulation;
6
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.List;
10
11 import net.sourceforge.phpdt.internal.corext.textmanipulation.TextEditNode.RootNode;
12
13 import org.eclipse.core.runtime.CoreException;
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.core.runtime.NullProgressMonitor;
16
17 // import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
18 // import net.sourceforge.phpdt.core.JavaModelException;
19 //
20 // import net.sourceforge.phpdt.internal.corext.Assert;
21
22 /**
23  * A <code>TextBufferEditor</code> manages a set of <code>TextEdit</code>s
24  * and applies them as a whole to a <code>TextBuffer</code>. Added
25  * <code>TextEdit</code>s must not overlap. The only exception from this rule
26  * are insertion point. There can be more than one insert point at the same text
27  * position. Clients should use the method <code>
28  * canPerformEdits</code> to
29  * validate if all added text edits follow these rules.
30  * <p>
31  * Clients can attach more than one <code>TextBufferEditor</code> to a single
32  * <code>
33  * TextBuffer</code>. If so <code>canPerformEdits</code> validates
34  * all text edits from all text buffer editors working on the same text buffer.
35  */
36 public class TextBufferEditor {
37
38         private TextBuffer fBuffer;
39
40         private List fEdits;
41
42         private RootNode fRootNode;
43
44         private int fNumberOfNodes;
45
46         private int fConnectCount;
47
48         private int fMode;
49
50         /* package */static final int UNDEFINED = 0;
51
52         /* package */static final int REDO = 1;
53
54         /* package */static final int UNDO = 2;
55
56         /**
57          * Creates a new <code>TextBufferEditor</code> for the given
58          * <code>TextBuffer</code>.
59          * 
60          * @param the
61          *            text buffer this editor is working on.
62          */
63         public TextBufferEditor(TextBuffer buffer) {
64                 fBuffer = buffer;
65                 // Assert.isNotNull(fBuffer);
66                 fEdits = new ArrayList();
67         }
68
69         /**
70          * Returns the text buffer this editor is working on.
71          * 
72          * @return the text buffer this editor is working on
73          */
74         public TextBuffer getTextBuffer() {
75                 return fBuffer;
76         }
77
78         /**
79          * Adds a <code>TextEdit</code> to this text editor. Adding a
80          * <code>TextEdit</code> to a <code>TextBufferEditor</code> transfers
81          * ownership of the edit to the editor. So after a edit has been added to a
82          * editor the creator of that edit <b>must</b> not continue modifing it.
83          * 
84          * @param edit
85          *            the text edit to be added
86          * @exception CoreException
87          *                if the text edit can not be added to this text buffer
88          *                editor
89          */
90         public void add(TextEdit edit) throws CoreException {
91                 // Assert.isTrue(fMode == UNDEFINED || fMode == REDO);
92                 internalAdd(edit);
93                 fMode = REDO;
94         }
95
96         /**
97          * Adds a <code>MultiTextEdit</code> to this text editor. Adding a
98          * <code>MultiTextEdit</code> to a <code>TextBufferEditor</code>
99          * transfers ownership of the edit to the editor. So after a edit has been
100          * added to a editor the creator of that edit <b>must</b> not continue
101          * modifing it.
102          * 
103          * @param edit
104          *            the multi text edit to be added
105          * @exception CoreException
106          *                if the multi text edit can not be added to this text
107          *                buffer editor
108          */
109         public void add(MultiTextEdit edit) throws CoreException {
110                 // Assert.isTrue(fMode == UNDEFINED || fMode == REDO);
111                 edit.connect(this);
112                 fMode = REDO;
113         }
114
115         /**
116          * Adds a <code>UndoMemento</code> to this text editor. Adding a
117          * <code>UndoMemento</code> to a <code>TextBufferEditor</code> transfers
118          * ownership of the memento to the editor. So after a memento has been added
119          * to a editor the creator of that memento <b>must</b> not continue
120          * modifing it.
121          * 
122          * @param undo
123          *            the undo memento to be added
124          * @exception CoreException
125          *                if the undo memento can not be added to this text buffer
126          *                editor
127          */
128         public void add(UndoMemento undo) throws CoreException {
129                 // Assert.isTrue(fMode == UNDEFINED);
130                 List list = undo.fEdits;
131                 // Add them reverse since we are adding undos.
132                 for (int i = list.size() - 1; i >= 0; i--) {
133                         internalAdd((TextEdit) list.get(i));
134                 }
135                 fMode = undo.fMode;
136         }
137
138         /**
139          * Checks if the <code>TextEdit</code> added to this text editor can be
140          * executed.
141          * 
142          * @return <code>true</code> if the edits can be executed. Return
143          *         <code>false
144          *      </code>otherwise. One major reason why text edits
145          *         cannot be executed is a wrong offset or length value of a
146          *         <code>TextEdit</code>.
147          */
148         public boolean canPerformEdits() {
149                 if (fRootNode != null)
150                         return true;
151                 fRootNode = buildTree();
152                 if (fRootNode == null)
153                         return false;
154                 if (fRootNode.validate(fBuffer.getLength()))
155                         return true;
156
157                 fRootNode = null;
158                 return false;
159         }
160
161         /**
162          * Clears the text buffer editor.
163          */
164         public void clear() {
165                 fRootNode = null;
166                 fMode = UNDEFINED;
167                 fEdits.clear();
168         }
169
170         /**
171          * Executes the text edits added to this text buffer editor and clears all
172          * added text edits.
173          * 
174          * @param pm
175          *            a progress monitor to report progress or <code>null</code>
176          *            if no progress is desired.
177          * @return an object representing the undo of the executed
178          *         <code>TextEdit</code>s
179          * @exception CoreException
180          *                if the edits cannot be executed
181          */
182         public UndoMemento performEdits(IProgressMonitor pm) throws CoreException {
183                 if (pm == null)
184                         pm = new NullProgressMonitor();
185
186                 int size = fEdits.size();
187                 if (size == 0)
188                         return new UndoMemento(fMode == UNDO ? REDO : UNDO);
189
190                 if (fRootNode == null) {
191                         fRootNode = buildTree();
192                         if (fRootNode == null || !fRootNode.validate(fBuffer.getLength())) {
193                                 // throw new JavaModelException(null,
194                                 // IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
195                         }
196                 }
197                 try {
198                         pm.beginTask("", fNumberOfNodes + 10); //$NON-NLS-1$
199                         UndoMemento undo = null;
200                         if (fMode == REDO) {
201                                 undo = fRootNode.performDo(fBuffer, pm);
202                                 fRootNode.performedDo();
203                         } else {
204                                 undo = fRootNode.performUndo(fBuffer, pm);
205                                 fRootNode.performedUndo();
206                         }
207                         pm.worked(10);
208                         return undo;
209                 } finally {
210                         pm.done();
211                         clear();
212                 }
213         }
214
215         // ---- Helper methods
216         // ------------------------------------------------------------
217
218         private RootNode buildTree() {
219                 TextEditNode[] nodes = new TextEditNode[fEdits.size()];
220                 for (int i = fEdits.size() - 1; i >= 0; i--) {
221                         nodes[i] = TextEditNode.create((TextEdit) fEdits.get(i));
222                 }
223                 fNumberOfNodes = nodes.length;
224                 Arrays.sort(nodes, new TextEditNodeComparator());
225                 RootNode root = new RootNode(fBuffer.getLength());
226                 for (int i = 0; i < nodes.length; i++) {
227                         root.add(nodes[i]);
228                 }
229                 return root;
230         }
231
232         private void internalAdd(TextEdit edit) throws CoreException {
233                 edit.index = fEdits.size();
234                 edit.isSynthetic = fConnectCount > 0;
235                 try {
236                         fConnectCount++;
237                         edit.connect(this);
238                 } finally {
239                         fConnectCount--;
240                 }
241                 fEdits.add(edit);
242         }
243 }