/******************************************************************************* * Copyright (c) 2000, 2010 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.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.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.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.ChangeDescriptor; import org.eclipse.ltk.core.refactoring.IValidationCheckResultQuery; import org.eclipse.ltk.core.refactoring.RefactoringCore; import org.eclipse.ltk.core.refactoring.RefactoringStatus; /** * @author Stas Negara - Added getAllAffectedObjects() (in order to avoid changing * getAffectedObjects()) * */ 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 ChangeDescriptor fChangeDescriptor; 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; Change reverseChange; RefactoringStatus validationStatus; public ExecuteResult() { validationStatus= new RefactoringStatus(); } } public UndoableOperation2ChangeAdapter(Change change) { fExecuteChange= change; fActiveChange= change; fChangeDescriptor= change.getDescriptor(); // remember it now: can't access anymore from executed change } public void setUndoChange(Change undoChange) { fUndoChange= undoChange; fActiveChange= fUndoChange; fExecuteChange= null; fRedoChange= null; } public Change getChange() { return fActiveChange; } public ChangeDescriptor getChangeDescriptor() { return fChangeDescriptor; } public void setChangeDescriptor(ChangeDescriptor descriptor) { fChangeDescriptor= descriptor; } 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(); } //CODINGSPECTATOR: Added the method getAllAffectedObjects. public Object[] getAllAffectedObjects() { if (fActiveChange == null) return null; return fActiveChange.getAllAffectedObjects(); } 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 Status.OK_STATUS; } 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 Status.OK_STATUS; } 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 Status.OK_STATUS; } } 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 Status.OK_STATUS; } 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 Status.OK_STATUS; } } 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.reverseChange= fActiveChange.perform(new SubProgressMonitor(monitor, 9)); 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 Status.OK_STATUS; } else { return status.getEntryWithHighestSeverity().toStatus(); } } private IValidationCheckResultQuery getQuery(IAdaptable info, String title) { if (info == null) return RefactoringCore.getQueryFactory().create(null); 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; } }