blob: eb0a05db023a2810a2beadaa07a470672935f584 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ltk.internal.core.refactoring;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.commands.operations.TriggeredOperations;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.IUndoManager;
import org.eclipse.ltk.core.refactoring.IUndoManagerListener;
import org.eclipse.ltk.core.refactoring.IValidationCheckResultQuery;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
public class UndoManager2 implements IUndoManager {
private class OperationHistroyListener implements IOperationHistoryListener {
public void historyNotification(OperationHistoryEvent event) {
IUndoableOperation op= event.getOperation();
if (op instanceof TriggeredOperations) {
op= ((TriggeredOperations)op).getTriggeringOperation();
}
UndoableOperation2ChangeAdapter changeOperation= null;
if (op instanceof UndoableOperation2ChangeAdapter) {
changeOperation= (UndoableOperation2ChangeAdapter)op;
}
if (changeOperation == null)
return;
Change change= changeOperation.getChange();
switch(event.getEventType()) {
case OperationHistoryEvent.ABOUT_TO_EXECUTE:
case OperationHistoryEvent.ABOUT_TO_UNDO:
case OperationHistoryEvent.ABOUT_TO_REDO:
fireAboutToPerformChange(change);
break;
case OperationHistoryEvent.DONE:
case OperationHistoryEvent.UNDONE:
case OperationHistoryEvent.REDONE:
fireChangePerformed(change);
fireUndoStackChanged();
fireRedoStackChanged();
break;
case OperationHistoryEvent.OPERATION_NOT_OK:
fireChangePerformed(change);
break;
case OperationHistoryEvent.OPERATION_ADDED:
// would be better to have different events for this
fireUndoStackChanged();
fireRedoStackChanged();
break;
case OperationHistoryEvent.OPERATION_REMOVED:
// would be better to have different events for this
fireUndoStackChanged();
fireRedoStackChanged();
break;
}
}
}
private static class NullQuery implements IValidationCheckResultQuery {
public boolean proceed(RefactoringStatus status) {
return true;
}
public void stopped(RefactoringStatus status) {
// do nothing
}
}
private static class QueryAdapter implements IAdaptable {
private IValidationCheckResultQuery fQuery;
public QueryAdapter(IValidationCheckResultQuery query) {
fQuery= query;
}
public Object getAdapter(Class adapter) {
if (IValidationCheckResultQuery.class.equals(adapter))
return fQuery;
return null;
}
}
private IOperationHistory fOperationHistroy;
private IOperationHistoryListener fOperationHistoryListener;
private boolean fIsOpen;
private TriggeredOperations fActiveOperation;
private ListenerList fListeners;
public UndoManager2() {
fOperationHistroy= OperationHistoryFactory.getOperationHistory();
}
public void addListener(IUndoManagerListener listener) {
if (fListeners == null) {
fListeners= new ListenerList(ListenerList.IDENTITY);
fOperationHistoryListener= new OperationHistroyListener();
fOperationHistroy.addOperationHistoryListener(fOperationHistoryListener);
}
fListeners.add(listener);
}
public void removeListener(IUndoManagerListener listener) {
if (fListeners == null)
return;
fListeners.remove(listener);
if (fListeners.size() == 0) {
fOperationHistroy.removeOperationHistoryListener(fOperationHistoryListener);
fListeners= null;
fOperationHistoryListener= null;
}
}
public void aboutToPerformChange(Change change) {
IUndoableOperation operation= new UndoableOperation2ChangeAdapter(change);
operation.addContext(RefactoringCorePlugin.getUndoContext());
fActiveOperation= new TriggeredOperations(operation, fOperationHistroy);
fActiveOperation.addContext(RefactoringCorePlugin.getUndoContext());
fOperationHistroy.openOperation(fActiveOperation, IOperationHistory.EXECUTE);
fIsOpen= true;
}
public void changePerformed(Change change) {
changePerformed(change, true);
}
public void changePerformed(Change change, boolean successful) {
if (fIsOpen && fActiveOperation != null) {
fOperationHistroy.closeOperation(successful, false, IOperationHistory.EXECUTE);
fIsOpen= false;
}
}
public void addUndo(String name, Change change) {
if (fActiveOperation != null) {
UndoableOperation2ChangeAdapter operation= (UndoableOperation2ChangeAdapter)fActiveOperation.getTriggeringOperation();
operation.setUndoChange(change);
operation.setLabel(name);
fOperationHistroy.add(fActiveOperation);
fActiveOperation= null;
}
}
public boolean anythingToUndo() {
return fOperationHistroy.canUndo(RefactoringCorePlugin.getUndoContext());
}
public String peekUndoName() {
IUndoableOperation op= fOperationHistroy.getUndoOperation(RefactoringCorePlugin.getUndoContext());
if (op == null)
return null;
return op.getLabel();
}
public void performUndo(IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException {
IUndoableOperation undo= fOperationHistroy.getUndoOperation(RefactoringCorePlugin.getUndoContext());
UndoableOperation2ChangeAdapter changeOperation= getUnwrappedOperation(undo);
if (changeOperation == null)
throw new CoreException(new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(),
IStatus.ERROR, RefactoringCoreMessages.UndoManager2_no_change, null));
if (query == null)
query= new NullQuery();
try {
fOperationHistroy.undoOperation(undo, pm, new QueryAdapter(query));
} catch (ExecutionException e) {
handleException(e);
}
}
public boolean anythingToRedo() {
return fOperationHistroy.canRedo(RefactoringCorePlugin.getUndoContext());
}
public String peekRedoName() {
IUndoableOperation op= fOperationHistroy.getRedoOperation(RefactoringCorePlugin.getUndoContext());
if (op == null)
return null;
return op.getLabel();
}
public void performRedo(IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException {
IUndoableOperation redo= fOperationHistroy.getRedoOperation(RefactoringCorePlugin.getUndoContext());
UndoableOperation2ChangeAdapter changeOperation= getUnwrappedOperation(redo);
if (changeOperation == null)
throw new CoreException(new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(),
IStatus.ERROR, RefactoringCoreMessages.UndoManager2_no_change, null));
if (query == null)
query= new NullQuery();
try {
fOperationHistroy.redoOperation(redo, pm, new QueryAdapter(query));
} catch (ExecutionException e) {
handleException(e);
}
}
private UndoableOperation2ChangeAdapter getUnwrappedOperation(IUndoableOperation operation) {
IUndoableOperation result= operation;
if (result instanceof TriggeredOperations) {
result= ((TriggeredOperations)result).getTriggeringOperation();
}
if (result instanceof UndoableOperation2ChangeAdapter) {
return (UndoableOperation2ChangeAdapter)result;
}
return null;
}
public void flush() {
if (fActiveOperation != null) {
if (fIsOpen) {
fOperationHistroy.closeOperation(false, false, IOperationHistory.EXECUTE);
}
/* the triggering operation is invalid, but we must ensure that any
* other operations executed while it was open remain in the undo
* history. We accomplish this by adding the invalid operation,
* since disposing the context later will cause it to be broken up into
* its atomic parts.
*/
fOperationHistroy.add(fActiveOperation);
}
fActiveOperation= null;
fIsOpen= false;
fOperationHistroy.dispose(RefactoringCorePlugin.getUndoContext(), true, true, false);
}
public void shutdown() {
// nothing to do since we have a shared undo manager anyways.
}
private void handleException(ExecutionException e) throws CoreException {
Throwable cause= e.getCause();
if (cause instanceof CoreException) {
throw (CoreException)cause;
} else {
throw new CoreException(new Status(
IStatus.ERROR, RefactoringCorePlugin.getPluginId(),IStatus.ERROR,
RefactoringCoreMessages.RefactoringCorePlugin_internal_error,
e));
}
}
//---- event firing methods -------------------------------------------------
private void fireAboutToPerformChange(final Change change) {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
final IUndoManagerListener listener= (IUndoManagerListener)listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
listener.aboutToPerformChange(UndoManager2.this, change);
}
public void handleException(Throwable exception) {
RefactoringCorePlugin.log(exception);
}
});
}
}
private void fireChangePerformed(final Change change) {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
final IUndoManagerListener listener= (IUndoManagerListener)listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
listener.changePerformed(UndoManager2.this, change);
}
public void handleException(Throwable exception) {
RefactoringCorePlugin.log(exception);
}
});
}
}
private void fireUndoStackChanged() {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
final IUndoManagerListener listener= (IUndoManagerListener)listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
listener.undoStackChanged(UndoManager2.this);
}
public void handleException(Throwable exception) {
RefactoringCorePlugin.log(exception);
}
});
}
}
private void fireRedoStackChanged() {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
final IUndoManagerListener listener= (IUndoManagerListener)listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
listener.redoStackChanged(UndoManager2.this);
}
public void handleException(Throwable exception) {
RefactoringCorePlugin.log(exception);
}
});
}
}
//---- testing methods ---------------------------------------------
public boolean testHasNumberOfUndos(int number) {
return fOperationHistroy.getUndoHistory(RefactoringCorePlugin.getUndoContext()).length == number;
}
public boolean testHasNumberOfRedos(int number) {
return fOperationHistroy.getRedoHistory(RefactoringCorePlugin.getUndoContext()).length == number;
}
}