blob: ba951b7cb831373113ff39d7b57b7e709a274316 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ltk.internal.core.refactoring;
import java.util.Collection;
import java.util.Iterator;
import java.util.Stack;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
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.RefactoringStatus;
/**
* Default implementation of IUndoManager.
*/
public class UndoManager implements IUndoManager {
private Stack fUndoChanges;
private Stack fRedoChanges;
private Stack fUndoNames;
private Stack fRedoNames;
private ListenerList fListeners;
/**
* Creates a new undo manager with an empty undo and redo stack.
*/
public UndoManager() {
flush();
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void addListener(IUndoManagerListener listener) {
if (fListeners == null)
fListeners= new ListenerList();
fListeners.add(listener);
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void removeListener(IUndoManagerListener listener) {
if (fListeners == null)
return;
fListeners.remove(listener);
if (fListeners.size() == 0)
fListeners= null;
}
/**
* {@inheritDoc}
*/
public void aboutToPerformChange(Change change) {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
((IUndoManagerListener)listeners[i]).aboutToPerformChange(this, change);
}
}
/**
* {@inheritDoc}
*/
public void changePerformed(Change change) {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
((IUndoManagerListener)listeners[i]).changePerformed(this, change);
}
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void aboutToPerformRefactoring() {
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void refactoringPerformed(boolean success) {
}
/*
* (non-Javadoc)
*
* @see IUndoManager#shutdown()
*/
public void shutdown() {
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void flush() {
flushUndo();
flushRedo();
}
private void flushUndo() {
if (fUndoChanges != null) {
sendDispose(fUndoChanges);
}
fUndoChanges= new Stack();
fUndoNames= new Stack();
fireUndoStackChanged();
}
private void flushRedo() {
if (fRedoChanges != null) {
sendDispose(fRedoChanges);
}
fRedoChanges= new Stack();
fRedoNames= new Stack();
fireRedoStackChanged();
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public void addUndo(String refactoringName, Change change) {
Assert.isNotNull(refactoringName, "refactoring"); //$NON-NLS-1$
Assert.isNotNull(change, "change"); //$NON-NLS-1$
fUndoNames.push(refactoringName);
fUndoChanges.push(change);
flushRedo();
fireUndoStackChanged();
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public RefactoringStatus performUndo(IProgressMonitor pm) throws CoreException {
RefactoringStatus result= new RefactoringStatus();
if (fUndoChanges.empty())
return result;
Change change= (Change)fUndoChanges.pop();
Change redo= executeChange(result, change, pm);
if (!result.hasFatalError()) {
if (redo != null && !fUndoNames.isEmpty()) {
fRedoNames.push(fUndoNames.pop());
fRedoChanges.push(redo);
fireUndoStackChanged();
fireRedoStackChanged();
} else {
flush();
}
} else {
flush();
}
return result;
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public RefactoringStatus performRedo(IProgressMonitor pm) throws CoreException {
RefactoringStatus result= new RefactoringStatus();
if (fRedoChanges.empty())
return result;
Change change= (Change)fRedoChanges.pop();
Change undo= executeChange(result, change, pm);
if (!result.hasFatalError()) {
if (undo != null && !fRedoNames.isEmpty()) {
fUndoNames.push(fRedoNames.pop());
fUndoChanges.push(undo);
fireRedoStackChanged();
fireUndoStackChanged();
}
} else {
flush();
}
return result;
}
private Change executeChange(final RefactoringStatus status, final Change change, IProgressMonitor pm) throws CoreException {
final Change[] undo= new Change[1];
IWorkspaceRunnable runnable= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
boolean undoInitialized= false;
try {
monitor.beginTask("", 11); //$NON-NLS-1$
status.merge(change.isValid(new SubProgressMonitor(monitor, 2)));
if (status.hasFatalError()) {
return;
}
ResourcesPlugin.getWorkspace().checkpoint(false);
aboutToPerformChange(change);
undo[0]= change.perform(new SubProgressMonitor(monitor, 8));
ResourcesPlugin.getWorkspace().checkpoint(false);
changePerformed(change);
change.dispose();
if (undo[0] != null) {
undo[0].initializeValidationData(new SubProgressMonitor(monitor, 1));
undoInitialized= true;
}
} catch (CoreException e) {
flush();
if (undo[0] != null && undoInitialized) {
Change ch= undo[0];
undo[0]= null;
ch.dispose();
} else {
undo[0]= null;
}
throw e;
} catch (RuntimeException e) {
flush();
if (undo[0] != null && undoInitialized) {
Change ch= undo[0];
undo[0]= null;
ch.dispose();
} else {
undo[0]= null;
}
throw e;
} finally {
monitor.done();
}
}
};
ResourcesPlugin.getWorkspace().run(runnable, pm);
return undo[0];
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public boolean anythingToRedo() {
return !fRedoChanges.empty();
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public boolean anythingToUndo() {
return !fUndoChanges.empty();
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public String peekUndoName() {
if (fUndoNames.size() > 0)
return (String)fUndoNames.peek();
return null;
}
/*
* (Non-Javadoc) Method declared in IUndoManager.
*/
public String peekRedoName() {
if (fRedoNames.size() > 0)
return (String)fRedoNames.peek();
return null;
}
private void fireUndoStackChanged() {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
((IUndoManagerListener)listeners[i]).undoStackChanged(this);
}
}
private void fireRedoStackChanged() {
if (fListeners == null)
return;
Object[] listeners= fListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
((IUndoManagerListener)listeners[i]).redoStackChanged(this);
}
}
private void sendDispose(Collection collection) {
for (Iterator iter= collection.iterator(); iter.hasNext();) {
Change change= (Change)iter.next();
change.dispose();
}
}
//---- testing methods ---------------------------------------------
public boolean testHasNumberOfUndos(int number) {
return fUndoChanges.size() == number;
}
public boolean testHasNumberOfRedos(int number) {
return fRedoChanges.size() == number;
}
}