| /******************************************************************************* |
| * 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.ui.actions; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.StatusUtil; |
| import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; |
| /** |
| * The abstract superclass for actions which invoke commands |
| * implemented in org.eclipse.core.* on a set of selected resources. |
| * |
| * It iterates over all selected resources; errors are collected and |
| * displayed to the user via a problems dialog at the end of the operation. |
| * User requests to cancel the operation are passed along to the core. |
| * <p> |
| * Subclasses must implement the following methods: |
| * <ul> |
| * <li><code>invokeOperation</code> - to perform the operation on one of the |
| * selected resources</li> |
| * <li><code>getOperationMessage</code> - to furnish a title for the progress |
| * dialog</li> |
| * </ul> |
| * </p> |
| * <p> |
| * Subclasses may override the following methods: |
| * <ul> |
| * <li><code>shouldPerformResourcePruning</code> - reimplement to turn off</li> |
| * <li><code>updateSelection</code> - extend to refine enablement criteria</li> |
| * <li><code>getProblemsTitle</code> - reimplement to furnish a title for the |
| * problems dialog</li> |
| * <li><code>getProblemsMessage</code> - reimplement to furnish a message for |
| * the problems dialog</li> |
| * <li><code>run</code> - extend to </li> |
| * </ul> |
| * </p> |
| */ |
| public abstract class WorkspaceAction extends SelectionListenerAction { |
| /** |
| * Multi status containing the errors detected when running the operation or |
| * <code>null</code> if no errors detected. |
| */ |
| private MultiStatus errorStatus; |
| /** |
| * The shell in which to show the progress and problems dialog. |
| */ |
| private Shell shell; |
| /** |
| * Creates a new action with the given text. |
| * |
| * @param shell the shell (for the modal progress dialog and error messages) |
| * @param text the string used as the text for the action, |
| * or <code>null</code> if there is no text |
| */ |
| protected WorkspaceAction(Shell shell, String text) { |
| super(text); |
| if (shell == null) { |
| throw new IllegalArgumentException(); |
| } |
| this.shell = shell; |
| } |
| /** |
| * Opens an error dialog to display the given message. |
| * <p> |
| * Note that this method must be called from UI thread. |
| * </p> |
| * |
| * @param message the message |
| */ |
| void displayError(String message) { |
| if (message == null) { |
| message = IDEWorkbenchMessages.getString("WorkbenchAction.internalError"); //$NON-NLS-1$ |
| } |
| MessageDialog.openError(shell, getProblemsTitle(), message); |
| } |
| /** |
| * Runs <code>invokeOperation</code> on each of the selected resources, reporting |
| * progress and fielding cancel requests from the given progress monitor. |
| * |
| * @param monitor a progress monitor |
| */ |
| final void execute(IProgressMonitor monitor) { |
| //1FTIMQN: ITPCORE:WIN - clients required to do too much iteration work |
| List resources = getActionResources(); |
| if (shouldPerformResourcePruning()) { |
| resources = pruneResources(resources); |
| } |
| Iterator resourcesEnum = resources.iterator(); |
| // 1FV0B3Y: ITPUI:ALL - sub progress monitors granularity issues |
| monitor.beginTask("", resources.size() * 1000); //$NON-NLS-1$ |
| // Fix for bug 31768 - Don't provide a task name in beginTask |
| // as it will be appended to each subTask message. Need to |
| // call setTaskName as its the only was to assure the task name is |
| // set in the monitor (see bug 31824) |
| monitor.setTaskName(getOperationMessage()); |
| try { |
| while (resourcesEnum.hasNext()) { |
| IResource resource = (IResource) resourcesEnum.next(); |
| try { |
| // 1FV0B3Y: ITPUI:ALL - sub progress monitors granularity issues |
| invokeOperation(resource, new SubProgressMonitor(monitor, 1000)); |
| } catch (CoreException e) { |
| recordError(e); |
| } |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| /** |
| * Returns the string to display for this action's operation. |
| * <p> |
| * Note that this hook method is invoked in a non-UI thread. |
| * </p> |
| * <p> |
| * Subclasses must implement this method. |
| * </p> |
| * |
| * @return the message |
| */ |
| abstract String getOperationMessage(); |
| /** |
| * Returns the string to display for this action's problems dialog. |
| * <p> |
| * The <code>WorkspaceAction</code> implementation of this method returns a |
| * vague message (localized counterpart of something like "The following |
| * problems occurred."). Subclasses may reimplement to provide something more |
| * suited to the particular action. |
| * </p> |
| * |
| * @return the problems message |
| */ |
| String getProblemsMessage() { |
| return IDEWorkbenchMessages.getString("WorkbenchAction.problemsMessage"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns the title for this action's problems dialog. |
| * <p> |
| * The <code>WorkspaceAction</code> implementation of this method returns a |
| * generic title (localized counterpart of "Problems"). Subclasses may |
| * reimplement to provide something more suited to the particular action. |
| * </p> |
| * |
| * @return the problems dialog title |
| */ |
| String getProblemsTitle() { |
| return IDEWorkbenchMessages.getString("WorkspaceAction.problemsTitle"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns the shell for this action. This shell is used for the modal progress |
| * and error dialogs. |
| * |
| * @return the shell |
| */ |
| Shell getShell() { |
| return shell; |
| } |
| /** |
| * Performs this action's operation on each of the selected resources, reporting |
| * progress to, and fielding cancel requests from, the given progress monitor. |
| * <p> |
| * Note that this method is invoked in a non-UI thread. |
| * </p> |
| * <p> |
| * Subclasses must implement this method. |
| * </p> |
| * |
| * @param resource one of the selected resources |
| * @param monitor a progress monitor |
| * @exception CoreException if the operation fails |
| */ |
| abstract void invokeOperation(IResource resource, IProgressMonitor monitor) |
| throws CoreException; |
| /** |
| * Returns whether the given resource is accessible, where files and folders |
| * are always considered accessible, and where a project is accessible iff it |
| * is open. |
| * |
| * @param resource the resource |
| * @return <code>true</code> if the resource is accessible, and |
| * <code>false</code> if it is not |
| */ |
| boolean isAccessible(IResource resource) { |
| switch (resource.getType()) { |
| case IResource.FILE : |
| return true; |
| case IResource.FOLDER : |
| return true; |
| case IResource.PROJECT : |
| return ((IProject) resource).isOpen(); |
| default : |
| return false; |
| } |
| } |
| /** |
| * Returns whether the given resource is a descendent of any of the resources |
| * in the given list. |
| * |
| * @param resources the list of resources (element type: <code>IResource</code>) |
| * @param child the resource to check |
| * @return <code>true</code> if <code>child</code> is a descendent of any of the |
| * elements of <code>resources</code> |
| */ |
| boolean isDescendent(List resources, IResource child) { |
| IResource parent = child.getParent(); |
| return parent != null && (resources.contains(parent) || isDescendent(resources, parent)); |
| } |
| /** |
| * Performs pruning on the given list of resources, as described in |
| * <code>shouldPerformResourcePruning</code>. |
| * |
| * @param resourceCollection the list of resources (element type: |
| * <code>IResource</code>) |
| * @return the list of resources (element type: <code>IResource</code>) |
| * after pruning. |
| * @see #shouldPerformResourcePruning |
| */ |
| List pruneResources(List resourceCollection) { |
| List prunedList = new ArrayList(resourceCollection); |
| Iterator elementsEnum = prunedList.iterator(); |
| while (elementsEnum.hasNext()) { |
| IResource currentResource = (IResource) elementsEnum.next(); |
| if (isDescendent(prunedList, currentResource)) |
| elementsEnum.remove(); //Removes currentResource |
| } |
| return prunedList; |
| } |
| /** |
| * Records the core exception to be displayed to the user |
| * once the action is finished. |
| * |
| * @param error a <code>CoreException</code> |
| */ |
| private void recordError(CoreException error) { |
| if (errorStatus == null) |
| errorStatus = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH, IStatus.ERROR, |
| getProblemsMessage(), error); |
| errorStatus.merge(error.getStatus()); |
| } |
| /** |
| * The <code>CoreWrapperAction</code> implementation of this <code>IAction</code> |
| * method uses a <code>ProgressMonitorDialog</code> to run the operation. The |
| * operation calls <code>execute</code> (which, in turn, calls |
| * <code>invokeOperation</code>). Afterwards, any <code>CoreException</code>s |
| * encountered while running the operation are reported to the user via a |
| * problems dialog. |
| * <p> |
| * Subclasses may extend this method. |
| * </p> |
| */ |
| public void run() { |
| try { |
| WorkspaceModifyOperation op = new WorkspaceModifyOperation() { |
| public void execute(IProgressMonitor monitor) { |
| WorkspaceAction.this.execute(monitor); |
| } |
| }; |
| new ProgressMonitorJobsDialog(shell).run(true, true, op); |
| } catch (InterruptedException e) { |
| return; |
| } catch (InvocationTargetException e) { |
| // we catch CoreException in execute(), but unexpected runtime exceptions or errors may still occur |
| String msg = IDEWorkbenchMessages.format("WorkspaceAction.logTitle", new Object[]{//$NON-NLS-1$ |
| getClass().getName(), e.getTargetException()}); |
| IDEWorkbenchPlugin.log(msg, StatusUtil.newStatus(IStatus.ERROR, msg, e |
| .getTargetException())); |
| displayError(e.getTargetException().getMessage()); |
| } |
| // If errors occurred, open an Error dialog & build a multi status error for it |
| if (errorStatus != null) { |
| ErrorDialog.openError(shell, getProblemsTitle(), null, // no special message |
| errorStatus); |
| } |
| errorStatus = null; |
| } |
| /** |
| * Returns whether this action should attempt to optimize the resources being |
| * operated on. This kind of pruning makes sense when the operation has depth |
| * infinity semantics (when the operation is applied explicitly to a resource |
| * then it is also applied implicitly to all the resource's descendents). |
| * <p> |
| * The <code>WorkspaceAction</code> implementation of this method returns |
| * <code>true</code>. Subclasses should reimplement to return <code>false</code> |
| * if pruning is not required. |
| * </p> |
| * |
| * @return <code>true</code> if pruning should be performed, |
| * and <code>false</code> if pruning is not desired |
| */ |
| boolean shouldPerformResourcePruning() { |
| return true; |
| } |
| /** |
| * The <code>WorkspaceAction</code> implementation of this |
| * <code>SelectionListenerAction</code> method ensures that this action is |
| * disabled if any of the selected resources are inaccessible. Subclasses may |
| * extend to react to selection changes; however, if the super method returns |
| * <code>false</code>, the overriding method should also return <code>false</code>. |
| */ |
| protected boolean updateSelection(IStructuredSelection selection) { |
| if (!super.updateSelection(selection) || selection.isEmpty()) { |
| return false; |
| } |
| for (Iterator i = getSelectedResources().iterator(); i.hasNext();) { |
| IResource r = (IResource) i.next(); |
| if (!isAccessible(r)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| /** |
| * Returns the elements that the action is to be performed on. |
| * By default return the selected resources. |
| * |
| * @return list of resource elements (element type: <code>IResource</code>) |
| */ |
| protected List getActionResources() { |
| return getSelectedResources(); |
| } |
| |
| /** |
| * Run the action in the background rather than with the |
| * progress dialog. |
| * @param rule The rule to apply to the background job or |
| * <code>null</code> if there isn't one. |
| */ |
| public void runInBackground(ISchedulingRule rule){ |
| |
| Job backgroundJob = new Job(removeMnemonics(getText())){ |
| /* (non-Javadoc) |
| * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| protected IStatus run(IProgressMonitor monitor) { |
| |
| monitor.beginTask("", 1); //$NON-NLS-1$ |
| // Fix for bug 31768 - Don't provide a task name in beginTask |
| // as it will be appended to each subTask message. Need to |
| // call setTaskName as its the only was to assure the task name is |
| // set in the monitor (see bug 31824) |
| monitor.setTaskName(getOperationMessage()); |
| WorkspaceAction.this.execute(monitor); |
| monitor.done(); |
| |
| IStatus returnStatus = Status.OK_STATUS; |
| |
| //If errors occurred, open an Error dialog & build a multi status error for it |
| if (errorStatus != null) { |
| returnStatus = errorStatus; |
| errorStatus = null; |
| } |
| return returnStatus; |
| } |
| |
| |
| }; |
| |
| if(rule != null) |
| backgroundJob.setRule(rule); |
| backgroundJob.setUser(true); |
| backgroundJob.schedule(); |
| |
| |
| } |
| } |