/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
package net.sourceforge.phpdt.internal.corext.textmanipulation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.sourceforge.phpdt.internal.corext.textmanipulation.TextEditNode.RootNode;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
// import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
// import net.sourceforge.phpdt.core.JavaModelException;
//
// import net.sourceforge.phpdt.internal.corext.Assert;
/**
* A TextBufferEditor
manages a set of TextEdit
s
* and applies them as a whole to a TextBuffer
. Added
* TextEdit
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
* canPerformEdits
to
* validate if all added text edits follow these rules.
*
* Clients can attach more than one TextBufferEditor
to a single
*
* TextBuffer
. If so canPerformEdits
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 TextBufferEditor
for the given
* TextBuffer
.
*
* @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 TextEdit
to this text editor. Adding a
* TextEdit
to a TextBufferEditor
transfers
* ownership of the edit to the editor. So after a edit has been added to a
* editor the creator of that edit must 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 MultiTextEdit
to this text editor. Adding a
* MultiTextEdit
to a TextBufferEditor
* transfers ownership of the edit to the editor. So after a edit has been
* added to a editor the creator of that edit must 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 UndoMemento
to this text editor. Adding a
* UndoMemento
to a TextBufferEditor
transfers
* ownership of the memento to the editor. So after a memento has been added
* to a editor the creator of that memento must 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 TextEdit
added to this text editor can be
* executed.
*
* @return true
if the edits can be executed. Return
* false
*
otherwise. One major reason why text edits
* cannot be executed is a wrong offset or length value of a
* TextEdit
.
*/
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 or null
* if no progress is desired.
* @return an object representing the undo of the executed
* TextEdit
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())) {
// throw new JavaModelException(null,
// IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
}
}
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);
}
}