KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > actions > WorkspaceAction


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.actions;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17
18 import org.eclipse.core.resources.IResource;
19 import org.eclipse.core.resources.WorkspaceJob;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.MultiStatus;
24 import org.eclipse.core.runtime.OperationCanceledException;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.SubProgressMonitor;
27 import org.eclipse.core.runtime.jobs.ISchedulingRule;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.jface.dialogs.ErrorDialog;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.operation.IRunnableWithProgress;
32 import org.eclipse.jface.viewers.IStructuredSelection;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
36 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
37 import org.eclipse.ui.internal.ide.StatusUtil;
38 import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog;
39
40 /**
41  * The abstract superclass for actions which invoke commands implemented in
42  * org.eclipse.core.* on a set of selected resources.
43  *
44  * It iterates over all selected resources; errors are collected and displayed
45  * to the user via a problems dialog at the end of the operation. User requests
46  * to cancel the operation are passed along to the core.
47  * <p>
48  * Subclasses must implement the following methods:
49  * <ul>
50  * <li><code>invokeOperation</code> - to perform the operation on one of the
51  * selected resources</li>
52  * <li><code>getOperationMessage</code> - to furnish a title for the progress
53  * dialog</li>
54  * </ul>
55  * </p>
56  * <p>
57  * Subclasses may override the following methods:
58  * <ul>
59  * <li><code>shouldPerformResourcePruning</code> - reimplement to turn off</li>
60  * <li><code>updateSelection</code> - extend to refine enablement criteria</li>
61  * <li><code>getProblemsTitle</code> - reimplement to furnish a title for the
62  * problems dialog</li>
63  * <li><code>getProblemsMessage</code> - reimplement to furnish a message for
64  * the problems dialog</li>
65  * <li><code>run</code> - extend to </li>
66  * </ul>
67  * </p>
68  */

69 public abstract class WorkspaceAction extends SelectionListenerAction {
70     /**
71      * The shell in which to show the progress and problems dialog.
72      */

73     private final Shell shell;
74
75     /**
76      * Creates a new action with the given text.
77      *
78      * @param shell
79      * the shell (for the modal progress dialog and error messages)
80      * @param text
81      * the string used as the text for the action, or
82      * <code>null</code> if there is no text
83      */

84     protected WorkspaceAction(Shell shell, String JavaDoc text) {
85         super(text);
86         if (shell == null) {
87             throw new IllegalArgumentException JavaDoc();
88         }
89         this.shell = shell;
90     }
91
92     /**
93      * Opens an error dialog to display the given message.
94      * <p>
95      * Note that this method must be called from UI thread.
96      * </p>
97      *
98      * @param message
99      * the message
100      */

101     void displayError(String JavaDoc message) {
102         if (message == null) {
103             message = IDEWorkbenchMessages.WorkbenchAction_internalError;
104         }
105         MessageDialog.openError(shell, getProblemsTitle(), message);
106     }
107
108     /**
109      * Runs <code>invokeOperation</code> on each of the selected resources,
110      * reporting progress and fielding cancel requests from the given progress
111      * monitor.
112      * <p>
113      * Note that if an action is running in the background, the same action
114      * instance can be executed multiple times concurrently. This method must
115      * not access or modify any mutable state on action class.
116      *
117      * @param monitor
118      * a progress monitor
119      * @return The result of the execution
120      */

121     final IStatus execute(List JavaDoc resources, IProgressMonitor monitor) {
122         MultiStatus errors = null;
123         // 1FTIMQN: ITPCORE:WIN - clients required to do too much iteration work
124
if (shouldPerformResourcePruning()) {
125             resources = pruneResources(resources);
126         }
127         // 1FV0B3Y: ITPUI:ALL - sub progress monitors granularity issues
128
monitor.beginTask("", resources.size() * 1000); //$NON-NLS-1$
129
// Fix for bug 31768 - Don't provide a task name in beginTask
130
// as it will be appended to each subTask message. Need to
131
// call setTaskName as its the only was to assure the task name is
132
// set in the monitor (see bug 31824)
133
monitor.setTaskName(getOperationMessage());
134         Iterator JavaDoc resourcesEnum = resources.iterator();
135         try {
136             while (resourcesEnum.hasNext()) {
137                 IResource resource = (IResource) resourcesEnum.next();
138                 try {
139                     // 1FV0B3Y: ITPUI:ALL - sub progress monitors granularity
140
// issues
141
invokeOperation(resource, new SubProgressMonitor(monitor,
142                             1000));
143                 } catch (CoreException e) {
144                     errors = recordError(errors, e);
145                 }
146                 if (monitor.isCanceled()) {
147                     throw new OperationCanceledException();
148                 }
149             }
150             return errors == null ? Status.OK_STATUS : errors;
151         } finally {
152             monitor.done();
153         }
154     }
155
156     /**
157      * Returns the string to display for this action's operation.
158      * <p>
159      * Note that this hook method is invoked in a non-UI thread.
160      * </p>
161      * <p>
162      * Subclasses must implement this method.
163      * </p>
164      *
165      * @return the message
166      *
167      * @since 3.1
168      */

169     protected abstract String JavaDoc getOperationMessage();
170
171     /**
172      * Returns the string to display for this action's problems dialog.
173      * <p>
174      * The <code>WorkspaceAction</code> implementation of this method returns
175      * a vague message (localized counterpart of something like "The following
176      * problems occurred."). Subclasses may reimplement to provide something
177      * more suited to the particular action.
178      * </p>
179      *
180      * @return the problems message
181      *
182      * @since 3.1
183      */

184     protected String JavaDoc getProblemsMessage() {
185         return IDEWorkbenchMessages.WorkbenchAction_problemsMessage;
186     }
187
188     /**
189      * Returns the title for this action's problems dialog.
190      * <p>
191      * The <code>WorkspaceAction</code> implementation of this method returns
192      * a generic title (localized counterpart of "Problems"). Subclasses may
193      * reimplement to provide something more suited to the particular action.
194      * </p>
195      *
196      * @return the problems dialog title
197      *
198      * @since 3.1
199      */

200     protected String JavaDoc getProblemsTitle() {
201         return IDEWorkbenchMessages.WorkspaceAction_problemsTitle;
202     }
203
204     /**
205      * Returns the shell for this action. This shell is used for the modal
206      * progress and error dialogs.
207      *
208      * @return the shell
209      */

210     Shell getShell() {
211         return shell;
212     }
213
214     /**
215      * Performs this action's operation on each of the selected resources,
216      * reporting progress to, and fielding cancel requests from, the given
217      * progress monitor.
218      * <p>
219      * Note that this method is invoked in a non-UI thread.
220      * </p>
221      * <p>
222      * Subclasses must implement this method.
223      * <p>
224      * @deprecated Since 3.3, subclasses should instead implement the method
225      * {@link #createOperation(IStatus[])} and provide an empty implementation
226      * for this method.
227      * </p>
228      *
229      * @param resource
230      * one of the selected resources
231      * @param monitor
232      * a progress monitor
233      * @exception CoreException
234      * if the operation fails
235      *
236      * @since 3.1
237      */

238     protected abstract void invokeOperation(IResource resource,
239             IProgressMonitor monitor) throws CoreException;
240
241     /**
242      * Returns whether the given resource is a descendent of any of the
243      * resources in the given list.
244      *
245      * @param resources
246      * the list of resources (element type: <code>IResource</code>)
247      * @param child
248      * the resource to check
249      * @return <code>true</code> if <code>child</code> is a descendent of
250      * any of the elements of <code>resources</code>
251      */

252     boolean isDescendent(List JavaDoc resources, IResource child) {
253         IResource parent = child.getParent();
254         return parent != null
255                 && (resources.contains(parent) || isDescendent(resources,
256                         parent));
257     }
258
259     /**
260      * Performs pruning on the given list of resources, as described in
261      * <code>shouldPerformResourcePruning</code>.
262      *
263      * @param resourceCollection
264      * the list of resources (element type: <code>IResource</code>)
265      * @return the list of resources (element type: <code>IResource</code>)
266      * after pruning.
267      * @see #shouldPerformResourcePruning
268      */

269     List JavaDoc pruneResources(List JavaDoc resourceCollection) {
270         List JavaDoc prunedList = new ArrayList JavaDoc(resourceCollection);
271         Iterator JavaDoc elementsEnum = prunedList.iterator();
272         while (elementsEnum.hasNext()) {
273             IResource currentResource = (IResource) elementsEnum.next();
274             if (isDescendent(prunedList, currentResource)) {
275                 elementsEnum.remove(); // Removes currentResource
276
}
277         }
278         return prunedList;
279     }
280
281     /**
282      * Records the core exception to be displayed to the user once the action is
283      * finished.
284      *
285      * @param error
286      * a <code>CoreException</code>
287      */

288     MultiStatus recordError(MultiStatus errors, CoreException error) {
289         if (errors == null) {
290             errors = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH,
291                     IStatus.ERROR, getProblemsMessage(), null);
292         }
293         errors.merge(error.getStatus());
294         return errors;
295     }
296
297     /**
298      * The <code>CoreWrapperAction</code> implementation of this
299      * <code>IAction</code> method uses a <code>ProgressMonitorDialog</code>
300      * to run the operation. The operation calls <code>execute</code> (which,
301      * in turn, calls <code>invokeOperation</code>). Afterwards, any
302      * <code>CoreException</code>s encountered while running the operation
303      * are reported to the user via a problems dialog.
304      * <p>
305      * Subclasses may extend this method.
306      * </p>
307      */

308     public void run() {
309         IStatus[] errorStatus = new IStatus[1];
310         try {
311             new ProgressMonitorJobsDialog(shell).run(true, true,
312                     createOperation(errorStatus));
313         } catch (InterruptedException JavaDoc e) {
314             return;
315         } catch (InvocationTargetException JavaDoc e) {
316             // we catch ExecutionException in the created operation, but unexpected runtime
317
// exceptions or errors may still occur
318
String JavaDoc msg = NLS.bind(
319                     IDEWorkbenchMessages.WorkspaceAction_logTitle, getClass()
320                             .getName(), e.getTargetException());
321             IDEWorkbenchPlugin.log(msg, StatusUtil.newStatus(IStatus.ERROR,
322                     msg, e.getTargetException()));
323             displayError(e.getTargetException().getMessage());
324         }
325         // If errors occurred, open an Error dialog & build a multi status error
326
// for it
327
if (errorStatus[0] != null && !errorStatus[0].isOK()) {
328             ErrorDialog.openError(shell, getProblemsTitle(), null, // no
329
// special
330
// message
331
errorStatus[0]);
332         }
333     }
334
335     /**
336      * Returns whether this action should attempt to optimize the resources
337      * being operated on. This kind of pruning makes sense when the operation
338      * has depth infinity semantics (when the operation is applied explicitly to
339      * a resource then it is also applied implicitly to all the resource's
340      * descendents).
341      * <p>
342      * The <code>WorkspaceAction</code> implementation of this method returns
343      * <code>true</code>. Subclasses should reimplement to return
344      * <code>false</code> if pruning is not required.
345      * </p>
346      *
347      * @return <code>true</code> if pruning should be performed, and
348      * <code>false</code> if pruning is not desired
349      *
350      * @since 3.1
351      */

352     protected boolean shouldPerformResourcePruning() {
353         return true;
354     }
355
356     /**
357      * The <code>WorkspaceAction</code> implementation of this
358      * <code>SelectionListenerAction</code> method ensures that this action is
359      * disabled if any of the selected resources are inaccessible. Subclasses
360      * may extend to react to selection changes; however, if the super method
361      * returns <code>false</code>, the overriding method should also return
362      * <code>false</code>.
363      */

364     protected boolean updateSelection(IStructuredSelection selection) {
365         if (!super.updateSelection(selection) || selection.isEmpty()) {
366             return false;
367         }
368         for (Iterator JavaDoc i = getSelectedResources().iterator(); i.hasNext();) {
369             IResource r = (IResource) i.next();
370             if (!r.isAccessible()) {
371                 return false;
372             }
373         }
374         return true;
375     }
376
377     /**
378      * Returns the elements that the action is to be performed on. By default
379      * return the selected resources.
380      * <p>
381      * Subclasses may override this method.
382      *
383      * @return list of resource elements (element type: <code>IResource</code>)
384      */

385     protected List JavaDoc getActionResources() {
386         return getSelectedResources();
387     }
388
389     /**
390      * Run the action in the background rather than with the progress dialog.
391      *
392      * @param rule
393      * The rule to apply to the background job or <code>null</code>
394      * if there isn't one.
395      */

396     public void runInBackground(ISchedulingRule rule) {
397         runInBackground(rule, (Object JavaDoc[]) null);
398     }
399
400     /**
401      * Run the action in the background rather than with the progress dialog.
402      *
403      * @param rule
404      * The rule to apply to the background job or <code>null</code>
405      * if there isn't one.
406      * @param jobFamily
407      * a single family that the job should belong to or
408      * <code>null</code> if none.
409      *
410      * @since 3.1
411      */

412     public void runInBackground(ISchedulingRule rule, Object JavaDoc jobFamily) {
413         if (jobFamily == null) {
414             runInBackground(rule, (Object JavaDoc[]) null);
415         } else {
416             runInBackground(rule, new Object JavaDoc[] { jobFamily });
417         }
418     }
419
420     /**
421      * Run the action in the background rather than with the progress dialog.
422      *
423      * @param rule
424      * The rule to apply to the background job or <code>null</code>
425      * if there isn't one.
426      * @param jobFamilies
427      * the families the job should belong to or <code>null</code>
428      * if none.
429      *
430      * @since 3.1
431      */

432     public void runInBackground(ISchedulingRule rule, final Object JavaDoc[] jobFamilies) {
433         // obtain a copy of the selected resources before the job is forked
434
final List JavaDoc resources = new ArrayList JavaDoc(getActionResources());
435         Job job = new WorkspaceJob(removeMnemonics(getText())) {
436
437             /*
438              * (non-Javadoc)
439              *
440              * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
441              */

442             public boolean belongsTo(Object JavaDoc family) {
443                 if (jobFamilies == null || family == null) {
444                     return false;
445                 }
446                 for (int i = 0; i < jobFamilies.length; i++) {
447                     if (family.equals(jobFamilies[i])) {
448                         return true;
449                     }
450                 }
451                 return false;
452             }
453
454             /*
455              * (non-Javadoc)
456              *
457              * @see org.eclipse.core.resources.WorkspaceJob#runInWorkspace(org.eclipse.core.runtime.IProgressMonitor)
458              */

459             public IStatus runInWorkspace(IProgressMonitor monitor) {
460                 return WorkspaceAction.this.execute(resources, monitor);
461             }
462         };
463         if (rule != null) {
464             job.setRule(rule);
465         }
466         job.setUser(true);
467         job.schedule();
468     }
469
470     /**
471      * Returns the operation to perform when this action runs. The returned
472      * operation must be an {@link IRunnableWithProgress} that will perform the
473      * action's work. The default implementation returns an operation that will
474      * iterate over the selected resources and call
475      * {@link #invokeOperation(IResource, IProgressMonitor)} for each resource.
476      * Subclasses must either implement
477      * {@link #invokeOperation(IResource, IProgressMonitor)} or override this
478      * method to provide a different operation. Subclasses typically override
479      * this method when an undoable operation is to be provided.
480      *
481      * @param errorStatus
482      * an array of error status objects to which the result of
483      * running the operation should be added.
484      *
485      * @return the operation to perform when this action runs.
486      * @since 3.3
487      */

488     protected IRunnableWithProgress createOperation(final IStatus[] errorStatus) {
489         return new WorkspaceModifyOperation() {
490             public void execute(IProgressMonitor monitor) {
491                 errorStatus[0] = WorkspaceAction.this.execute(
492                         getActionResources(), monitor);
493             }
494         };
495     }
496
497 }
498
Popular Tags