/*******************************************************************************
 * Copyright (c) 2006, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.ide.undo;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
import org.eclipse.core.commands.operations.IAdvancedUndoableOperation2;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.commands.operations.OperationStatus;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceRuleFactory;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.core.resources.mapping.ResourceChangeValidator;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.ICoreRunnable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.action.Action;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.undo.UndoMessages;

/**
 * An AbstractWorkspaceOperation represents an undoable operation that affects
 * the workspace. It handles common workspace operation activities such as
 * tracking which resources are affected by an operation, prompting the user
 * when there are possible side effects of operations, building execution
 * exceptions from core exceptions, etc. Clients may call the public API from a
 * background thread.
 * <p>
 * This class is not intended to be subclassed by clients.
 * </p>
 * @noextend This class is not intended to be subclassed by clients.
 * @since 3.3
 */
public abstract class AbstractWorkspaceOperation extends AbstractOperation
		implements IAdvancedUndoableOperation, IAdvancedUndoableOperation2 {

	private static String ELLIPSIS = "..."; //$NON-NLS-1$

	protected static int EXECUTE = 1;

	protected static int UNDO = 2;

	protected static int REDO = 3;

	protected IResource[] resources;

	private boolean isValid = true;

	/*
	 * Specifies whether any user prompting is appropriate while computing
	 * status.
	 */
	protected boolean quietCompute = false;

	String[] modelProviderIds;

	/**
	 * Create an AbstractWorkspaceOperation with the specified name.
	 *
	 * @param name
	 *            the name used to describe the operation
	 */
	AbstractWorkspaceOperation(String name) {
		// Many operation names are based on the triggering action's name, so
		// we strip out the any mnemonics that may be embedded in the name.
		super(Action.removeMnemonics(name));

		// For the same reason, check for an ellipsis and strip out
		String label = this.getLabel();
		if (label.endsWith(ELLIPSIS)) {
			this.setLabel(label
					.substring(0, label.length() - ELLIPSIS.length()));
		}
	}

	/**
	 * Set the ids of any model providers for the resources involved.
	 *
	 * @param ids
	 *            the array of String model provider ids that provide models
	 *            associated with the resources involved in this operation
	 */
	public void setModelProviderIds(String[] ids) {
		modelProviderIds = ids;
	}

	/**
	 * Set the resources which are affected by this operation
	 *
	 * @param resources
	 *            an array of resources
	 */
	protected void setTargetResources(IResource[] resources) {
		this.resources = resources;
	}

	/**
	 * Return the workspace manipulated by this operation.
	 *
	 * @return the IWorkspace used by this operation.
	 */
	protected IWorkspace getWorkspace() {
		return ResourcesPlugin.getWorkspace();
	}

	/**
	 * Return the workspace rule factory associated with this operation.
	 *
	 * @return the IResourceRuleFactory associated with this operation.
	 */
	protected IResourceRuleFactory getWorkspaceRuleFactory() {
		return getWorkspace().getRuleFactory();
	}

	/**
	 * Mark this operation invalid due to some external change. May be used by
	 * subclasses.
	 *
	 */
	protected void markInvalid() {
		isValid = false;
	}

	/*
	 * This implementation checks a validity flag.
	 */
	@Override
	public boolean canExecute() {
		return isValid();
	}

	/*
	 * This implementation checks a validity flag.
	 */
	@Override
	public boolean canUndo() {
		return isValid();
	}

	/*
	 * This implementation checks a validity flag.
	 */
	@Override
	public boolean canRedo() {
		return isValid();
	}

	/**
	 * Execute the specified operation. This implementation executes the
	 * operation in a workspace runnable and catches any CoreExceptions
	 * resulting from the operation. Unhandled CoreExceptions are propagated as
	 * ExecutionExceptions.
	 *
	 * @param monitor
	 *            the progress monitor to use for the operation
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 * @return the IStatus of the execution. The status severity should be set
	 *         to <code>OK</code> if the operation was successful, and
	 *         <code>ERROR</code> if it was not. Any other status is assumed
	 *         to represent an incompletion of the execution.
	 * @throws ExecutionException
	 *             if an exception occurred during execution.
	 *
	 * @see org.eclipse.core.commands.operations.IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
	 *      org.eclipse.core.runtime.IAdaptable)
	 */
	@Override
	public IStatus execute(IProgressMonitor monitor, final IAdaptable uiInfo)
			throws ExecutionException {
		try {
			getWorkspace().run(monitor1 -> doExecute(monitor1, uiInfo), getExecuteSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
		} catch (final CoreException e) {
			throw new ExecutionException(NLS.bind(
					UndoMessages.AbstractWorkspaceOperation_ExecuteErrorTitle,
					getLabel()), e);
		}
		isValid = true;
		return Status.OK_STATUS;
	}

	/**
	 * Redo the specified operation. This implementation redoes the operation in
	 * a workspace runnable and catches any CoreExceptions resulting from the
	 * operation. Unhandled CoreExceptions are propagated as
	 * ExecutionExceptions.
	 *
	 * @param monitor
	 *            the progress monitor to use for the operation
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 * @return the IStatus of the redo. The status severity should be set to
	 *         <code>OK</code> if the operation was successful, and
	 *         <code>ERROR</code> if it was not. Any other status is assumed
	 *         to represent an incompletion of the redo.
	 * @throws ExecutionException
	 *             if an exception occurred during execution.
	 * @see org.eclipse.core.commands.operations.IUndoableOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
	 *      org.eclipse.core.runtime.IAdaptable)
	 */
	@Override
	public IStatus redo(IProgressMonitor monitor, final IAdaptable uiInfo)
			throws ExecutionException {
		try {
			getWorkspace().run(monitor1 -> doExecute(monitor1, uiInfo), getRedoSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
		} catch (final CoreException e) {
			throw new ExecutionException(NLS.bind(
					UndoMessages.AbstractWorkspaceOperation_RedoErrorTitle,
					getLabel()), e);

		}
		isValid = true;
		return Status.OK_STATUS;
	}

	/**
	 * Undo the specified operation. This implementation undoes the operation in
	 * a workspace runnable and catches any CoreExceptions resulting from the
	 * operation. Unhandled CoreExceptions are propagated as
	 * ExecutionExceptions.
	 *
	 * @param monitor
	 *            the progress monitor to use for the operation
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 * @return the IStatus of the undo. The status severity should be set to
	 *         <code>OK</code> if the operation was successful, and
	 *         <code>ERROR</code> if it was not. Any other status is assumed
	 *         to represent an incompletion of the undo. *
	 * @throws ExecutionException
	 *             if an exception occurred during execution.
	 * @see org.eclipse.core.commands.operations.IUndoableOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
	 *      org.eclipse.core.runtime.IAdaptable)
	 */
	@Override
	public IStatus undo(IProgressMonitor monitor, final IAdaptable uiInfo)
			throws ExecutionException {
		try {
			getWorkspace().run(monitor1 -> doUndo(monitor1, uiInfo), getUndoSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
		} catch (final CoreException e) {
			throw new ExecutionException(NLS.bind(
					UndoMessages.AbstractWorkspaceOperation_UndoErrorTitle,
					getLabel()), e);

		}
		isValid = true;
		return Status.OK_STATUS;
	}

	/**
	 * Perform the specific work involved in undoing this operation.
	 *
	 * @param monitor
	 *            the progress monitor to use for the operation
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 * @throws CoreException
	 *             propagates any CoreExceptions thrown from the resources API
	 */
	protected abstract void doUndo(IProgressMonitor monitor, IAdaptable uiInfo)
			throws CoreException;

	/**
	 * Perform the specific work involved in executing this operation.
	 *
	 * @param monitor
	 *            the progress monitor to use for the operation
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 * @throws CoreException
	 *             propagates any CoreExceptions thrown from the resources API
	 *
	 */
	protected abstract void doExecute(IProgressMonitor monitor,
			IAdaptable uiInfo) throws CoreException;

	/**
	 * Return whether the proposed operation is valid. The default
	 * implementation simply checks to see if the flag has been marked as
	 * invalid, relying on subclasses to mark the flag invalid when appropriate.
	 *
	 * @return the validity flag
	 */
	protected boolean isValid() {
		return isValid;
	}

	@Override
	public void aboutToNotify(OperationHistoryEvent event) {
		// do nothing
	}

	@Override
	public Object[] getAffectedObjects() {
		return resources;
	}

	/**
	 * Return a status indicating the projected outcome of executing the
	 * receiver. This method is not called by the operation history, but instead
	 * is used by clients (such as implementers of
	 * {@link org.eclipse.core.commands.operations.IOperationApprover2}) who
	 * wish to perform advanced validation of an operation before attempting to
	 * execute it.
	 *
	 * If an ERROR status is returned, the operation will not proceed and the
	 * user notified if deemed necessary by the caller. The validity flag on the
	 * operation should be marked as invalid. If an OK status is returned, the
	 * operation will proceed. The caller must interpret any other returned
	 * status severity, and may choose to prompt the user as to how to proceed.
	 *
	 * If there are multiple conditions that result in an ambiguous status
	 * severity, it is best for the implementor of this method to consult the
	 * user as to how to proceed for each one, and return an OK or ERROR status
	 * that accurately reflects the user's wishes, or to return a multi-status
	 * that accurately describes all of the issues at hand, so that the caller
	 * may potentially consult the user. (Note that the user should not be
	 * consulted at all if a client has called {@link #setQuietCompute(boolean)}
	 * with a value of <code>true</code>.)
	 *
	 * This implementation computes the validity of execution by computing the
	 * resource delta that would be generated on execution, and checking whether
	 * any registered model providers are affected by the operation.
	 *
	 * @param monitor
	 *            the progress monitor to be used for computing the status
	 * @return the status indicating the projected outcome of executing the
	 *         receiver
	 *
	 * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
	 * @see #setQuietCompute(boolean)
	 */
	@Override
	public IStatus computeExecutionStatus(IProgressMonitor monitor) {
		IStatus status = Status.OK_STATUS;

		// If we are not to prompt the user, nothing to do.
		if (quietCompute) {
			return status;
		}

		IResourceChangeDescriptionFactory factory = ResourceChangeValidator
				.getValidator().createDeltaFactory();
		if (updateResourceChangeDescriptionFactory(factory, EXECUTE)) {
			boolean proceed = IDE
					.promptToConfirm(
							getShell(null),
							UndoMessages.AbstractWorkspaceOperation_SideEffectsWarningTitle,
							NLS
									.bind(
											UndoMessages.AbstractWorkspaceOperation_ExecuteSideEffectsWarningMessage,
											getLabel()), factory.getDelta(),
							modelProviderIds, true /* syncExec */);
			if (!proceed) {
				status = Status.CANCEL_STATUS;
			}
		}
		return status;

	}

	/**
	 * Return a status indicating the projected outcome of undoing the receiver.
	 * This method is not called by the operation history, but instead is used
	 * by clients (such as implementers of
	 * {@link org.eclipse.core.commands.operations.IOperationApprover2}) who
	 * wish to perform advanced validation of an operation before attempting to
	 * undo it.
	 *
	 * If an ERROR status is returned, the undo will not proceed and the user
	 * notified if deemed necessary by the caller. The validity flag on the
	 * operation should be marked as invalid. If an OK status is returned, the
	 * undo will proceed. The caller must interpret any other returned status
	 * severity, and may choose to prompt the user as to how to proceed.
	 *
	 * If there are multiple conditions that result in an ambiguous status
	 * severity, it is best for the implementor of this method to consult the
	 * user as to how to proceed for each one, and return an OK or ERROR status
	 * that accurately reflects the user's wishes, or to return a multi-status
	 * that accurately describes all of the issues at hand, so that the caller
	 * may potentially consult the user. (Note that the user should not be
	 * consulted at all if a client has called {@link #setQuietCompute(boolean)}
	 * with a value of <code>true</code>.)
	 *
	 * This implementation computes the validity of undo by computing the
	 * resource delta that would be generated on undo, and checking whether any
	 * registered model providers are affected by the operation.
	 *
	 * @param monitor
	 *            the progress monitor to be used for computing the status
	 * @return the status indicating the projected outcome of undoing the
	 *         receiver
	 *
	 * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
	 * @see #setQuietCompute(boolean)
	 */
	@Override
	public IStatus computeUndoableStatus(IProgressMonitor monitor) {
		IStatus status = Status.OK_STATUS;
		// If we are not to prompt the user, nothing to do.
		if (quietCompute) {
			return status;
		}

		IResourceChangeDescriptionFactory factory = ResourceChangeValidator
				.getValidator().createDeltaFactory();
		if (updateResourceChangeDescriptionFactory(factory, UNDO)) {
			boolean proceed = IDE
					.promptToConfirm(
							getShell(null),
							UndoMessages.AbstractWorkspaceOperation_SideEffectsWarningTitle,
							NLS
									.bind(
											UndoMessages.AbstractWorkspaceOperation_UndoSideEffectsWarningMessage,
											getLabel()), factory.getDelta(),
							modelProviderIds, true /* syncExec */);
			if (!proceed) {
				status = Status.CANCEL_STATUS;
			}
		}
		return status;

	}

	/**
	 * Return a status indicating the projected outcome of redoing the receiver.
	 * This method is not called by the operation history, but instead is used
	 * by clients (such as implementers of
	 * {@link org.eclipse.core.commands.operations.IOperationApprover2}) who
	 * wish to perform advanced validation of an operation before attempting to
	 * redo it.
	 *
	 * If an ERROR status is returned, the redo will not proceed and the user
	 * notified if deemed necessary by the caller. The validity flag on the
	 * operation should be marked as invalid. If an OK status is returned, the
	 * redo will proceed. The caller must interpret any other returned status
	 * severity, and may choose to prompt the user as to how to proceed.
	 *
	 * If there are multiple conditions that result in an ambiguous status
	 * severity, it is best for the implementor of this method to consult the
	 * user as to how to proceed for each one, and return an OK or ERROR status
	 * that accurately reflects the user's wishes, or to return a multi-status
	 * that accurately describes all of the issues at hand, so that the caller
	 * may potentially consult the user. (Note that the user should not be
	 * consulted at all if a client has called {@link #setQuietCompute(boolean)}
	 * with a value of <code>true</code>.)
	 *
	 * This implementation computes the validity of redo by computing the
	 * resource delta that would be generated on redo, and checking whether any
	 * registered model providers are affected by the operation.
	 *
	 * @param monitor
	 *            the progress monitor to be used for computing the status
	 * @return the status indicating the projected outcome of redoing the
	 *         receiver
	 *
	 * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
	 * @see #setQuietCompute(boolean)
	 */
	@Override
	public IStatus computeRedoableStatus(IProgressMonitor monitor) {
		IStatus status = Status.OK_STATUS;
		// If we are not to prompt the user, nothing to do.
		if (quietCompute) {
			return status;
		}

		IResourceChangeDescriptionFactory factory = ResourceChangeValidator
				.getValidator().createDeltaFactory();
		if (updateResourceChangeDescriptionFactory(factory, REDO)) {
			boolean proceed = IDE
					.promptToConfirm(
							getShell(null),
							UndoMessages.AbstractWorkspaceOperation_SideEffectsWarningTitle,
							NLS
									.bind(
											UndoMessages.AbstractWorkspaceOperation_RedoSideEffectsWarningMessage,
											getLabel()), factory.getDelta(),
							modelProviderIds, true /* syncExec */);
			if (!proceed) {
				status = Status.CANCEL_STATUS;
			}
		}
		return status;
	}

	/**
	 * Update the provided resource change description factory so it can
	 * generate a resource delta describing the result of an undo or redo.
	 * Return a boolean indicating whether any update was done. The default
	 * implementation does not update the factory. Subclasses are expected to
	 * override this method to more specifically describe their modifications to
	 * the workspace.
	 *
	 * @param factory
	 *            the factory to update
	 * @param operation
	 *            an integer indicating whether the change is part of an
	 *            execute, undo, or redo
	 * @return a boolean indicating whether the factory was updated.
	 */
	protected boolean updateResourceChangeDescriptionFactory(
			IResourceChangeDescriptionFactory factory, int operation) {
		return false;
	}

	/**
	 * Return an error status describing an invalid operation using the provided
	 * message.
	 *
	 * @param message
	 *            the message to be used in the status, or <code>null</code>
	 *            if a generic message should be used
	 * @return the error status
	 */
	protected IStatus getErrorStatus(String message) {
		String statusMessage = message;
		if (statusMessage == null) {
			statusMessage = NLS
					.bind(
							UndoMessages.AbstractWorkspaceOperation_ErrorInvalidMessage,
							getLabel());
		}
		return new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH,
				OperationStatus.OPERATION_INVALID, statusMessage, null);
	}

	/**
	 * Return a warning status describing the warning state of an operation
	 * using the provided message and code.
	 *
	 * @param message
	 *            the message to be used in the status, or <code>null</code>
	 *            if a generic message should be used
	 * @param code
	 *            the integer code to be assigned to the status
	 * @return the warning status
	 */
	protected IStatus getWarningStatus(String message, int code) {
		String statusMessage = message;
		if (statusMessage == null) {
			statusMessage = NLS
					.bind(
							UndoMessages.AbstractWorkspaceOperation_GenericWarningMessage,
							getLabel());
		}
		return new Status(IStatus.WARNING, IDEWorkbenchPlugin.IDE_WORKBENCH,
				code, statusMessage, null);
	}

	/**
	 * Return whether the resources known by this operation currently exist.
	 *
	 * @return <code>true</code> if there are existing resources and
	 *         <code>false</code> if there are no known resources or any one
	 *         of them does not exist
	 */
	protected boolean resourcesExist() {
		if (resources == null || resources.length == 0) {
			return false;
		}
		for (int i = 0; i < resources.length; i++) {
			if (resources[i] == null || !resources[i].exists()) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Return whether the resources known by this operation contain any
	 * projects.
	 *
	 * @return <code>true</code> if there is one or more projects known by
	 *         this operation and false if there are no projects.
	 */
	protected boolean resourcesIncludesProjects() {
		if (resources == null || resources.length == 0) {
			return false;
		}
		for (IResource resource : resources) {
			if (resource.getType() == IResource.PROJECT) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return a scheduling rule appropriate for executing this operation.
	 *
	 * The default implementation is to return a rule that locks out the entire
	 * workspace. Subclasses are encouraged to provide more specific rules that
	 * affect only their resources.
	 *
	 * @return the scheduling rule to use when executing this operation, or
	 *         <code>null</code> if there are no scheduling restrictions for
	 *         this operation.
	 *
	 * @see IWorkspace#run(ICoreRunnable, ISchedulingRule, int,
	 *      IProgressMonitor)
	 */
	protected ISchedulingRule getExecuteSchedulingRule() {
		return getWorkspace().getRoot();
	}

	/**
	 * Return a scheduling rule appropriate for undoing this operation.
	 *
	 * The default implementation is to return a rule that locks out the entire
	 * workspace. Subclasses are encouraged to provide more specific rules that
	 * affect only their resources.
	 *
	 * @return the scheduling rule to use when undoing this operation, or
	 *         <code>null</code> if there are no scheduling restrictions for
	 *         this operation.
	 *
	 * @see IWorkspace#run(ICoreRunnable, ISchedulingRule, int,
	 *      IProgressMonitor)
	 */
	protected ISchedulingRule getUndoSchedulingRule() {
		return getWorkspace().getRoot();
	}

	/**
	 * Return a scheduling rule appropriate for redoing this operation.
	 *
	 * The default implementation considers the redo scheduling rule the same as
	 * the original execution scheduling rule.
	 *
	 * @return the scheduling rule to use when redoing this operation, or
	 *         <code>null</code> if there are no scheduling restrictions for
	 *         this operation.
	 *
	 * @see IWorkspace#run(ICoreRunnable, ISchedulingRule, int,
	 *      IProgressMonitor)
	 */
	protected ISchedulingRule getRedoSchedulingRule() {
		return getExecuteSchedulingRule();
	}

	@Override
	public void setQuietCompute(boolean quiet) {
		quietCompute = quiet;
	}

	@Override
	public String toString() {
		StringBuffer text = new StringBuffer(super.toString());
		text.append("\n"); //$NON-NLS-1$
		text.append(this.getClass().getName());
		appendDescriptiveText(text);
		return text.toString();
	}

	/**
	 * Append any descriptive text to the specified string buffer to be shown in
	 * the receiver's {@link #toString()} text.
	 * <p>Note that this method is not intend to be subclassed by clients.
	 *
	 * @param text
	 *            the StringBuffer on which to append the text
	 */
	protected void appendDescriptiveText(StringBuffer text) {
		text.append(" resources: "); //$NON-NLS-1$
		text.append(resources);
		text.append('\'');
	}

	/**
	 * Return the shell described by the specified adaptable, or the active
	 * shell if no shell has been specified in the adaptable.
	 *
	 * @param uiInfo
	 *            the IAdaptable (or <code>null</code>) provided by the
	 *            caller in order to supply UI information for prompting the
	 *            user if necessary. When this parameter is not
	 *            <code>null</code>, it contains an adapter for the
	 *            org.eclipse.swt.widgets.Shell.class
	 *
	 * @return the shell specified in the adaptable, or the active shell if no
	 *         shell has been specified
	 *
	 */
	protected Shell getShell(IAdaptable uiInfo) {
		Shell shell = Adapters.adapt(uiInfo, Shell.class);
		if (shell != null) {
			return shell;
		}
		return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
	}

	@Override
	public boolean runInBackground() {
		return true;
	}
}
