| /******************************************************************************* |
| * Copyright (c) 2000, 2007 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.core.refactoring; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.ltk.internal.core.refactoring.NotCancelableProgressMonitor; |
| |
| /** |
| * Operation that, when run, performs a {@link Change} object. The operation |
| * can be created in two different ways: with a given change or with a |
| * {@link CreateChangeOperation}. If created the second way the given create |
| * change operation will be used to create the actual change to perform. |
| * <p> |
| * If the change has been performed successfully (e.g. {@link #changeExecuted()} returns |
| * <code>true</code>) then the operation has called {@link Change#dispose()} as well |
| * to clear-up internal state in the change object. If it hasn't been executed the |
| * change, the change is still intact and the client is responsible to dispose the |
| * change object. |
| * </p> |
| * <p> |
| * If an undo change has been provided by the change to execute then the operation |
| * calls {@link Change#initializeValidationData(IProgressMonitor)} to initialize the |
| * undo change's validation data. |
| * </p> |
| * <p> |
| * If an undo manager has been set via the method {@link #setUndoManager(IUndoManager, String)} |
| * then the undo object, if any has been provided, will be pushed onto the manager's |
| * undo stack. |
| * </p> |
| * <p> |
| * The operation should be executed via the run method offered by |
| * <code>IWorkspace</code> to achieve proper delta batching. |
| * </p> |
| * <p> |
| * Note: this class is not intended to be extended outside of the refactoring framework. |
| * </p> |
| * |
| * @since 3.0 |
| */ |
| public class PerformChangeOperation implements IWorkspaceRunnable { |
| |
| private Change fChange; |
| private CreateChangeOperation fCreateChangeOperation; |
| private RefactoringStatus fValidationStatus; |
| |
| private Change fUndoChange; |
| private String fUndoName; |
| private IUndoManager fUndoManager; |
| |
| private boolean fChangeExecuted; |
| private boolean fChangeExecutionFailed; |
| private ISchedulingRule fSchedulingRule; |
| |
| /** |
| * Creates a new perform change operation instance for the given change. |
| * |
| * @param change the change to be applied to the workbench |
| */ |
| public PerformChangeOperation(Change change) { |
| Assert.isNotNull(change); |
| fChange= change; |
| fSchedulingRule= ResourcesPlugin.getWorkspace().getRoot(); |
| } |
| |
| /** |
| * Creates a new <code>PerformChangeOperation</code> for the given {@link |
| * CreateChangeOperation}. The create change operation is used to create |
| * the actual change to execute. |
| * |
| * @param op the <code>CreateChangeOperation</code> used to create the |
| * actual change object |
| */ |
| public PerformChangeOperation(CreateChangeOperation op) { |
| Assert.isNotNull(op); |
| fCreateChangeOperation= op; |
| fSchedulingRule= ResourcesPlugin.getWorkspace().getRoot(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the change execution failed. |
| * |
| * @return <code>true</code> if the change execution failed; |
| * <code>false</code> otherwise |
| * |
| */ |
| public boolean changeExecutionFailed() { |
| return fChangeExecutionFailed; |
| } |
| |
| /** |
| * Returns <code>true</code> if the change has been executed. Otherwise <code> |
| * false</code> is returned. |
| * |
| * @return <code>true</code> if the change has been executed, otherwise |
| * <code>false</code> |
| */ |
| public boolean changeExecuted() { |
| return fChangeExecuted; |
| } |
| |
| /** |
| * Returns the status of the condition checking. Returns <code>null</code> if |
| * no condition checking has been requested. |
| * |
| * @return the status of the condition checking |
| */ |
| public RefactoringStatus getConditionCheckingStatus() { |
| if (fCreateChangeOperation != null) |
| return fCreateChangeOperation.getConditionCheckingStatus(); |
| return null; |
| } |
| |
| /** |
| * Returns the change used by this operation. This is either the change passed to |
| * the constructor or the one create by the <code>CreateChangeOperation</code>. |
| * Method returns <code>null</code> if the create operation did not create |
| * a corresponding change or hasn't been executed yet. |
| * |
| * @return the change used by this operation or <code>null</code> if no change |
| * has been created |
| */ |
| public Change getChange() { |
| return fChange; |
| } |
| |
| /** |
| * Returns the undo change of the change performed by this operation. Returns |
| * <code>null</code> if the change hasn't been performed yet or if the change |
| * doesn't provide a undo. |
| * |
| * @return the undo change of the performed change or <code>null</code> |
| */ |
| public Change getUndoChange() { |
| return fUndoChange; |
| } |
| |
| /** |
| * Returns the refactoring status returned from the call <code>IChange#isValid()</code>. |
| * Returns <code>null</code> if the change has not been executed. |
| * |
| * @return the change's validation status |
| */ |
| public RefactoringStatus getValidationStatus() { |
| return fValidationStatus; |
| } |
| |
| /** |
| * Sets the undo manager. If the executed change provides an undo change, |
| * then the undo change is pushed onto this manager. |
| * |
| * @param manager the undo manager to use or <code>null</code> if no |
| * undo recording is desired |
| * @param undoName the name used to present the undo change on the undo |
| * stack. Must be a human-readable string. Must not be <code>null</code> |
| * if manager is unequal <code>null</code> |
| */ |
| public void setUndoManager(IUndoManager manager, String undoName) { |
| if (manager != null) { |
| Assert.isNotNull(undoName); |
| } |
| fUndoManager= manager; |
| fUndoName= undoName; |
| } |
| |
| /** |
| * Sets the scheduling rule used to execute this operation. If |
| * not set then the workspace root is used. The Change operation |
| * must be able to be performed in the provided scheduling rule. |
| * |
| * @param rule the Rule to use, not null |
| * @since 3.3 |
| */ |
| public void setSchedulingRule(ISchedulingRule rule) { |
| Assert.isNotNull(rule); |
| |
| fSchedulingRule= rule; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void run(IProgressMonitor pm) throws CoreException { |
| if (pm == null) |
| pm= new NullProgressMonitor(); |
| try { |
| fChangeExecuted= false; |
| if (createChange()) { |
| pm.beginTask("", 4); //$NON-NLS-1$ |
| pm.subTask(""); //$NON-NLS-1$ |
| fCreateChangeOperation.run(new SubProgressMonitor(pm, 3)); |
| // Check for cancellation before executing the change, since canceling |
| // during change execution is not supported |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=187265 ): |
| if (pm.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| fChange= fCreateChangeOperation.getChange(); |
| if (fChange != null) { |
| executeChange(new SubProgressMonitor(pm, 1)); |
| } else { |
| pm.worked(1); |
| } |
| } else { |
| executeChange(pm); |
| } |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| /** |
| * Actually executes the change. |
| * |
| * @param pm a progress monitor to report progress |
| * |
| * @throws CoreException if an unexpected error occurs during |
| * change execution |
| */ |
| protected void executeChange(IProgressMonitor pm) throws CoreException { |
| fChangeExecuted= false; |
| if (!fChange.isEnabled()) |
| return; |
| IWorkspaceRunnable runnable= new IWorkspaceRunnable() { |
| public void run(IProgressMonitor monitor) throws CoreException { |
| boolean undoInitialized= false; |
| try { |
| monitor.beginTask("", 10); //$NON-NLS-1$ |
| fValidationStatus= fChange.isValid(new SubProgressMonitor(monitor, 1)); |
| if (fValidationStatus.hasFatalError()) |
| return; |
| boolean aboutToPerformChangeCalled= false; |
| try { |
| if (fUndoManager != null) { |
| ResourcesPlugin.getWorkspace().checkpoint(false); |
| fUndoManager.aboutToPerformChange(fChange); |
| aboutToPerformChangeCalled= true; |
| } |
| fChangeExecutionFailed= true; |
| fUndoChange= fChange.perform(new SubProgressMonitor(monitor, 9)); |
| fChangeExecutionFailed= false; |
| fChangeExecuted= true; |
| } finally { |
| if (fUndoManager != null) { |
| ResourcesPlugin.getWorkspace().checkpoint(false); |
| if (aboutToPerformChangeCalled) |
| fUndoManager.changePerformed(fChange, !fChangeExecutionFailed); |
| } |
| } |
| fChange.dispose(); |
| if (fUndoChange != null) { |
| fUndoChange.initializeValidationData(new NotCancelableProgressMonitor( |
| new SubProgressMonitor(monitor, 1))); |
| undoInitialized= true; |
| } |
| if (fUndoManager != null) { |
| if (fUndoChange != null) { |
| fUndoManager.addUndo(fUndoName, fUndoChange); |
| } else { |
| fUndoManager.flush(); |
| } |
| } |
| } catch (CoreException e) { |
| if (fUndoManager != null) |
| fUndoManager.flush(); |
| if (fUndoChange != null && undoInitialized) { |
| Change ch= fUndoChange; |
| fUndoChange= null; |
| ch.dispose(); |
| } |
| fUndoChange= null; |
| throw e; |
| } catch (RuntimeException e) { |
| if (fUndoManager != null) |
| fUndoManager.flush(); |
| if (fUndoChange != null && undoInitialized) { |
| Change ch= fUndoChange; |
| fUndoChange= null; |
| ch.dispose(); |
| } |
| fUndoChange= null; |
| throw e; |
| } finally { |
| monitor.done(); |
| } |
| } |
| }; |
| ResourcesPlugin.getWorkspace().run(runnable, fSchedulingRule, IWorkspace.AVOID_UPDATE, pm); |
| } |
| |
| private boolean createChange() { |
| return fCreateChangeOperation != null; |
| } |
| } |