blob: 2ee24f66208dea8603f0374b5d4c3f7543b51a84 [file] [log] [blame]
package org.eclipse.cdt.internal.corext.textmanipulation;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.util.Assert;
import org.eclipse.cdt.internal.corext.textmanipulation.TextEditNode.RootNode;
import org.eclipse.cdt.internal.ui.CStatusConstants;
import org.eclipse.cdt.ui.CUIPlugin;
/**
* A <code>TextBufferEditor</code> manages a set of <code>TextEdit</code>s and applies
* them as a whole to a <code>TextBuffer</code>. Added <code>TextEdit</code>s must
* not overlap. The only exception from this rule are insertion point. There can be more than
* one insert point at the same text position. Clients should use the method <code>
* canPerformEdits</code> to validate if all added text edits follow these rules.
* <p>
* Clients can attach more than one <code>TextBufferEditor</code> to a single <code>
* TextBuffer</code>. If so <code>canPerformEdits</code> validates all text edits from
* all text buffer editors working on the same text buffer.
*/
public class TextBufferEditor {
private TextBuffer fBuffer;
private List fEdits;
private RootNode fRootNode;
private int fNumberOfNodes;
private int fConnectCount;
private int fMode;
/* package */ static final int UNDEFINED= 0;
/* package */ static final int REDO= 1;
/* package */ static final int UNDO= 2;
/**
* Creates a new <code>TextBufferEditor</code> for the given
* <code>TextBuffer</code>.
*
* @param the text buffer this editor is working on.
*/
public TextBufferEditor(TextBuffer buffer) {
fBuffer= buffer;
Assert.isNotNull(fBuffer);
fEdits= new ArrayList();
}
/**
* Returns the text buffer this editor is working on.
*
* @return the text buffer this editor is working on
*/
public TextBuffer getTextBuffer() {
return fBuffer;
}
/**
* Adds a <code>TextEdit</code> to this text editor. Adding a <code>TextEdit</code>
* to a <code>TextBufferEditor</code> transfers ownership of the edit to the editor. So
* after a edit has been added to a editor the creator of that edit <b>must</b> not continue
* modifing it.
*
* @param edit the text edit to be added
* @exception CoreException if the text edit can not be added
* to this text buffer editor
*/
public void add(TextEdit edit) throws CoreException {
Assert.isTrue(fMode == UNDEFINED || fMode == REDO);
internalAdd(edit);
fMode= REDO;
}
/**
* Adds a <code>MultiTextEdit</code> to this text editor. Adding a <code>MultiTextEdit</code>
* to a <code>TextBufferEditor</code> transfers ownership of the edit to the editor. So
* after a edit has been added to a editor the creator of that edit <b>must</b> not continue
* modifing it.
*
* @param edit the multi text edit to be added
* @exception CoreException if the multi text edit can not be added
* to this text buffer editor
*/
public void add(MultiTextEdit edit) throws CoreException {
Assert.isTrue(fMode == UNDEFINED || fMode == REDO);
edit.connect(this);
fMode= REDO;
}
/**
* Adds a <code>UndoMemento</code> to this text editor. Adding a <code>UndoMemento</code>
* to a <code>TextBufferEditor</code> transfers ownership of the memento to the editor. So
* after a memento has been added to a editor the creator of that memento <b>must</b> not continue
* modifing it.
*
* @param undo the undo memento to be added
* @exception CoreException if the undo memento can not be added
* to this text buffer editor
*/
public void add(UndoMemento undo) throws CoreException {
Assert.isTrue(fMode == UNDEFINED);
List list= undo.fEdits;
// Add them reverse since we are adding undos.
for (int i= list.size() - 1; i >= 0; i--) {
internalAdd((TextEdit)list.get(i));
}
fMode= undo.fMode;
}
/**
* Checks if the <code>TextEdit</code> added to this text editor can be executed.
*
* @return <code>true</code> if the edits can be executed. Return <code>false
* </code>otherwise. One major reason why text edits cannot be executed
* is a wrong offset or length value of a <code>TextEdit</code>.
*/
public boolean canPerformEdits() {
if (fRootNode != null)
return true;
fRootNode= buildTree();
if (fRootNode == null)
return false;
if (fRootNode.validate(fBuffer.getLength()))
return true;
fRootNode= null;
return false;
}
/**
* Clears the text buffer editor.
*/
public void clear() {
fRootNode= null;
fMode= UNDEFINED;
fEdits.clear();
}
/**
* Executes the text edits added to this text buffer editor and clears all added
* text edits.
*
* @param pm a progress monitor to report progress
* @return an object representing the undo of the executed <code>TextEdit</code>s
* @exception CoreException if the edits cannot be executed
*/
public UndoMemento performEdits(IProgressMonitor pm) throws CoreException {
if (pm == null)
pm= new NullProgressMonitor();
int size= fEdits.size();
if (size == 0)
return new UndoMemento(fMode == UNDO ? REDO : UNDO);
if (fRootNode == null) {
fRootNode= buildTree();
if (fRootNode == null || !fRootNode.validate(fBuffer.getLength())) {
IStatus s= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, CStatusConstants.INTERNAL_ERROR,
"RootNode empty", null);
throw new CoreException(s);
}
}
try {
pm.beginTask("", fNumberOfNodes + 10); //$NON-NLS-1$
UndoMemento undo= null;
if (fMode == REDO) {
undo= fRootNode.performDo(fBuffer, pm);
fRootNode.performedDo();
} else {
undo= fRootNode.performUndo(fBuffer, pm);
fRootNode.performedUndo();
}
pm.worked(10);
return undo;
} finally {
pm.done();
clear();
}
}
//---- Helper methods ------------------------------------------------------------
private RootNode buildTree() {
TextEditNode[] nodes= new TextEditNode[fEdits.size()];
for (int i= fEdits.size() - 1; i >= 0; i--) {
nodes[i]= TextEditNode.create((TextEdit)fEdits.get(i));
}
fNumberOfNodes= nodes.length;
Arrays.sort(nodes, new TextEditNodeComparator());
RootNode root= new RootNode(fBuffer.getLength());
for (int i= 0; i < nodes.length; i++) {
root.add(nodes[i]);
}
return root;
}
private void internalAdd(TextEdit edit) throws CoreException {
edit.index= fEdits.size();
edit.isSynthetic= fConnectCount > 0;
try {
fConnectCount++;
edit.connect(this);
} finally {
fConnectCount--;
}
fEdits.add(edit);
}
}