/* * (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 TextEdits * and applies them as a whole to a TextBuffer. Added * TextEdits 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 * TextEdits * @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); } }