/*
* (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 org.eclipse.jdt.core.IJavaModelStatusConstants;
//import org.eclipse.jdt.core.JavaModelException;
//
//import org.eclipse.jdt.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);
}
}