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