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