blob: a6fd292eba436d7d57da9402a5c33f33037432e2 [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 java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
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.IValidationCheckResultQuery;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
public class UndoableOperation2ChangeAdapter implements IUndoableOperation, IAdvancedUndoableOperation {
private String fLabel;
private String fDescription;
private Change fExecuteChange;
private Change fUndoChange;
private Change fRedoChange;
private Change fActiveChange;
private List fContexts= new ArrayList();
private static class ContextAdapter implements IAdaptable {
private IAdaptable fInfoAdapter;
private String fTitle;
public ContextAdapter(IAdaptable infoAdapter, String title) {
fInfoAdapter= infoAdapter;
fTitle= title;
}
public Object getAdapter(Class adapter) {
if (String.class.equals(adapter))
return fTitle;
return fInfoAdapter.getAdapter(adapter);
}
}
private static class ExecuteResult {
boolean changeExecuted;
boolean changeExecutionFailed;
Change reverseChange;
RefactoringStatus validationStatus;
public ExecuteResult() {
validationStatus= new RefactoringStatus();
}
}
public UndoableOperation2ChangeAdapter(Change change) {
fExecuteChange= change;
fActiveChange= change;
}
public void setUndoChange(Change undoChange) {
fUndoChange= undoChange;
fActiveChange= fUndoChange;
fExecuteChange= null;
fRedoChange= null;
}
public Change getChange() {
return fActiveChange;
}
public void setLabel(String label) {
fLabel= label;
}
public String getLabel() {
if (fLabel != null)
return fLabel;
return fActiveChange.getName();
}
public String getDescription() {
if (fDescription != null)
return fDescription;
return fActiveChange.getName();
}
public Object[] getAffectedObjects() {
if (fActiveChange == null)
return null;
return fActiveChange.getAffectedObjects();
}
public void addContext(IUndoContext context) {
if (!fContexts.contains(context))
fContexts.add(context);
}
public boolean hasContext(IUndoContext context) {
if (context == null)
return false;
for (int i = 0; i< fContexts.size(); i++) {
IUndoContext otherContext = (IUndoContext)fContexts.get(i);
// have to check both ways because one context may be more general in
// its matching rules than another.
if (context.matches(otherContext) || otherContext.matches(context))
return true;
}
return false;
}
public void removeContext(IUndoContext context) {
fContexts.remove(context);
}
public IUndoContext[] getContexts() {
return (IUndoContext[])fContexts.toArray(new IUndoContext[fContexts.size()]);
}
public boolean canExecute() {
return fExecuteChange != null;
}
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if (monitor == null)
monitor= new NullProgressMonitor();
try {
ExecuteResult result= executeChange(
getQuery(
info,
RefactoringCoreMessages.Refactoring_execute_label),
monitor);
if (!result.changeExecuted) {
return createStatus(result);
}
fUndoChange= result.reverseChange;
fActiveChange= fUndoChange;
fExecuteChange= null;
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), 0, "", null); //$NON-NLS-1$
} catch (CoreException e) {
throw new ExecutionException(e.getStatus().getMessage(), e);
}
}
public boolean canUndo() {
return fUndoChange != null;
}
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if (monitor == null)
monitor= new NullProgressMonitor();
try {
ExecuteResult result= executeChange(
getQuery(
info,
RefactoringCoreMessages.Refactoring_undo_label),
monitor);
if (!result.changeExecuted) {
fUndoChange= null;
fRedoChange= null;
clearActiveChange();
return createStatus(result);
}
fRedoChange= result.reverseChange;
fActiveChange= fRedoChange;
fUndoChange= null;
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), 0, "", null); //$NON-NLS-1$
} catch (CoreException e) {
throw new ExecutionException(e.getStatus().getMessage(), e);
}
}
public IStatus computeUndoableStatus(IProgressMonitor monitor) throws ExecutionException {
if (fUndoChange == null)
return new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(), IStatus.ERROR,
RefactoringCoreMessages.UndoableOperation2ChangeAdapter_no_undo_available,
null);
try {
if (monitor == null)
monitor= new NullProgressMonitor();
RefactoringStatus status= fUndoChange.isValid(monitor);
if (status.hasFatalError()) {
// The operation can no longer be undo.
fUndoChange= null;
clearActiveChange();
return asStatus(status);
} else {
// return OK in all other cases. This by passes the dialog shown
// in the operation approver and allows refactoring to show its
// own dialog again inside the runnable.
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$
}
} catch (CoreException e) {
throw new ExecutionException(e.getStatus().getMessage(), e);
}
}
public boolean canRedo() {
return fRedoChange != null;
}
public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if (monitor == null)
monitor= new NullProgressMonitor();
try {
ExecuteResult result= executeChange(
getQuery(
info,
RefactoringCoreMessages.Refactoring_redo_label),
monitor);
if (!result.changeExecuted) {
fUndoChange= null;
fRedoChange= null;
clearActiveChange();
return createStatus(result);
}
fUndoChange= result.reverseChange;
fActiveChange= fUndoChange;
fRedoChange= null;
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), 0, "", null); //$NON-NLS-1$
} catch (CoreException e) {
throw new ExecutionException(e.getStatus().getMessage(), e);
}
}
public IStatus computeRedoableStatus(IProgressMonitor monitor) throws ExecutionException {
if (fRedoChange == null)
return new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(), IStatus.ERROR,
RefactoringCoreMessages.UndoableOperation2ChangeAdapter_no_redo_available,
null);
try {
if (monitor == null)
monitor= new NullProgressMonitor();
RefactoringStatus status= fRedoChange.isValid(monitor);
if (status.hasFatalError()) {
// The operation can no longer be redone.
fRedoChange= null;
clearActiveChange();
return asStatus(status);
} else {
// return OK in all other cases. This by passes the dialog shown
// in the operation approver and allows refactoring to show its
// own dialog again inside the runnable.
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$
}
} catch (CoreException e) {
throw new ExecutionException(e.getStatus().getMessage(), e);
}
}
public void aboutToNotify(OperationHistoryEvent event) {
switch(event.getEventType()) {
case OperationHistoryEvent.ABOUT_TO_EXECUTE:
case OperationHistoryEvent.ABOUT_TO_UNDO:
case OperationHistoryEvent.ABOUT_TO_REDO:
case OperationHistoryEvent.DONE:
case OperationHistoryEvent.UNDONE:
case OperationHistoryEvent.REDONE:
case OperationHistoryEvent.OPERATION_NOT_OK:
ResourcesPlugin.getWorkspace().checkpoint(false);
break;
}
}
public void dispose() {
// the active change could be cleared.
if (fActiveChange != null)
fActiveChange.dispose();
}
private ExecuteResult executeChange(final IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException {
final ExecuteResult result= new ExecuteResult();
if (fActiveChange == null || !fActiveChange.isEnabled())
return result;
IWorkspaceRunnable runnable= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
boolean reverseIsInitialized= false;
try {
monitor.beginTask("", 11); //$NON-NLS-1$
result.validationStatus= fActiveChange.isValid(new SubProgressMonitor(monitor, 2));
if (result.validationStatus.hasFatalError()) {
query.stopped(result.validationStatus);
// no need to dispose here. The framework disposes
// the undo since it couldn't be executed.
return;
}
if (!result.validationStatus.isOK() && !query.proceed(result.validationStatus)) {
return;
}
try {
result.changeExecutionFailed= true;
result.reverseChange= fActiveChange.perform(new SubProgressMonitor(monitor, 9));
result.changeExecutionFailed= false;
result.changeExecuted= true;
} finally {
ResourcesPlugin.getWorkspace().checkpoint(false);
}
fActiveChange.dispose();
if (result.reverseChange != null) {
result.reverseChange.initializeValidationData(new NotCancelableProgressMonitor(
new SubProgressMonitor(monitor, 1)));
reverseIsInitialized= true;
}
} catch (CoreException e) {
Change ch= result.reverseChange;
result.reverseChange= null;
if (ch != null && reverseIsInitialized) {
ch.dispose();
}
throw e;
} catch (RuntimeException e) {
Change ch= result.reverseChange;
result.reverseChange= null;
if (ch != null && reverseIsInitialized) {
ch.dispose();
}
throw e;
} finally {
monitor.done();
}
}
};
ResourcesPlugin.getWorkspace().run(runnable, pm);
return result;
}
private IStatus createStatus(ExecuteResult result) {
if (!result.validationStatus.isOK()) {
return result.validationStatus.getEntryWithHighestSeverity().toStatus();
} else {
return new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(), IStatus.ERROR,
RefactoringCoreMessages.UndoableOperation2ChangeAdapter_error_message,
null);
}
}
private IStatus asStatus(RefactoringStatus status) {
if (status.isOK()) {
return new Status(IStatus.OK, RefactoringCorePlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$
} else {
return status.getEntryWithHighestSeverity().toStatus();
}
}
private IValidationCheckResultQuery getQuery(IAdaptable info, String title) {
IValidationCheckResultQuery result= (IValidationCheckResultQuery)info.getAdapter(IValidationCheckResultQuery.class);
if (result != null)
return result;
ContextAdapter context= new ContextAdapter(info, title);
return RefactoringCore.getQueryFactory().create(context);
}
private void clearActiveChange() {
if (fLabel == null) {
fLabel= fActiveChange.getName();
}
if (fDescription == null) {
fDescription= fActiveChange.getName();
}
fActiveChange.dispose();
fActiveChange= null;
}
}