KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > operations > AdvancedValidationUserApprover


1 /*******************************************************************************
2  * Copyright (c) 2005, 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.internal.operations;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14
15 import org.eclipse.core.commands.ExecutionException;
16 import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
17 import org.eclipse.core.commands.operations.IAdvancedUndoableOperation2;
18 import org.eclipse.core.commands.operations.IOperationApprover;
19 import org.eclipse.core.commands.operations.IOperationApprover2;
20 import org.eclipse.core.commands.operations.IOperationHistory;
21 import org.eclipse.core.commands.operations.IUndoContext;
22 import org.eclipse.core.commands.operations.IUndoableOperation;
23 import org.eclipse.core.runtime.IAdaptable;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.OperationCanceledException;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.jface.dialogs.ErrorDialog;
29 import org.eclipse.jface.dialogs.IDialogConstants;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.operation.IRunnableWithProgress;
32 import org.eclipse.jface.window.Window;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.ui.PlatformUI;
36 import org.eclipse.ui.internal.Workbench;
37 import org.eclipse.ui.internal.WorkbenchMessages;
38 import org.eclipse.ui.internal.WorkbenchPlugin;
39 import org.eclipse.ui.internal.misc.StatusUtil;
40 import org.eclipse.ui.internal.util.Util;
41
42 /**
43  * <p>
44  * An operation approver that rechecks the validity of a proposed undo or redo
45  * operation using
46  * {@link IAdvancedUndoableOperation#computeUndoableStatus(IProgressMonitor)} or
47  * {@link IAdvancedUndoableOperation#computeRedoableStatus(IProgressMonitor)}.
48  * Some complex operations do not compute their validity in canUndo() or
49  * canRedo() because it is too time-consuming. To save time on complex
50  * validations, the true validity is not determined until it is time to perform
51  * the operation.
52  * </p>
53  * <p>
54  * Since 3.3, this operation approver also checks the validity of a proposed
55  * execute by determining whether the redo is viable.
56  *
57  * @since 3.1
58  */

59 public class AdvancedValidationUserApprover implements IOperationApprover,
60         IOperationApprover2 {
61     
62     /**
63      * Static to prevent opening of error dialogs for automated testing.
64      *
65      * @since 3.3
66      */

67     public static boolean AUTOMATED_MODE = false;
68
69     private IUndoContext context;
70
71     private static final int EXECUTING = 1;
72
73     private static final int UNDOING = 2;
74
75     private static final int REDOING = 3;
76
77     private class StatusReportingRunnable implements IRunnableWithProgress {
78         IStatus status;
79
80         int doing;
81
82         IUndoableOperation operation;
83
84         IOperationHistory history;
85
86         IAdaptable uiInfo;
87
88         StatusReportingRunnable(IUndoableOperation operation,
89                 IOperationHistory history, IAdaptable uiInfo, int doing) {
90             super();
91             this.operation = operation;
92             this.history = history;
93             this.doing = doing;
94             this.uiInfo = uiInfo;
95         }
96
97         // The casts to IAdvancedUndoableOperation and
98
// IAdvancedUndoableOperation2 are safe because these types were checked
99
// in the call chain.
100
public void run(IProgressMonitor pm) {
101             try {
102                 switch (doing) {
103                 case UNDOING:
104                     status = ((IAdvancedUndoableOperation) operation)
105                             .computeUndoableStatus(pm);
106                     break;
107                 case REDOING:
108                     status = ((IAdvancedUndoableOperation) operation)
109                             .computeRedoableStatus(pm);
110                     break;
111                 case EXECUTING:
112                     status = ((IAdvancedUndoableOperation2) operation)
113                             .computeExecutionStatus(pm);
114                     break;
115                 }
116
117             } catch (ExecutionException e) {
118                 reportException(e, uiInfo);
119                 status = IOperationHistory.OPERATION_INVALID_STATUS;
120             }
121         }
122
123         IStatus getStatus() {
124             return status;
125         }
126     }
127
128     /**
129      * Create an AdvancedValidationUserApprover that performs advanced
130      * validations on proposed undo and redo operations for a given undo
131      * context.
132      *
133      * @param context -
134      * the undo context of operations in question.
135      */

136     public AdvancedValidationUserApprover(IUndoContext context) {
137         super();
138         this.context = context;
139     }
140
141     /*
142      * (non-Javadoc)
143      *
144      * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation,
145      * org.eclipse.core.commands.operations.IOperationHistory,
146      * org.eclipse.core.runtime.IAdaptable)
147      */

148     public IStatus proceedRedoing(IUndoableOperation operation,
149             IOperationHistory history, IAdaptable uiInfo) {
150         return proceedWithOperation(operation, history, uiInfo, REDOING);
151     }
152
153     /*
154      * (non-Javadoc)
155      *
156      * @see org.eclipse.core.commands.operations.IOperationApprover#proceedUndoing(org.eclipse.core.commands.operations.IUndoableOperation,
157      * org.eclipse.core.commands.operations.IOperationHistory,
158      * org.eclipse.core.runtime.IAdaptable)
159      */

160     public IStatus proceedUndoing(IUndoableOperation operation,
161             IOperationHistory history, IAdaptable uiInfo) {
162
163         return proceedWithOperation(operation, history, uiInfo, UNDOING);
164     }
165
166     /*
167      * (non-Javadoc)
168      *
169      * @see org.eclipse.core.commands.operations.IOperationApprover2#proceedExecuting(org.eclipse.core.commands.operations.IUndoableOperation,
170      * org.eclipse.core.commands.operations.IOperationHistory,
171      * org.eclipse.core.runtime.IAdaptable)
172      */

173     public IStatus proceedExecuting(IUndoableOperation operation,
174             IOperationHistory history, IAdaptable uiInfo) {
175         return proceedWithOperation(operation, history, uiInfo, EXECUTING);
176     }
177
178     /*
179      * Determine whether the operation in question is still valid.
180      */

181     private IStatus proceedWithOperation(final IUndoableOperation operation,
182             final IOperationHistory history, final IAdaptable uiInfo,
183             final int doing) {
184
185         // return immediately if the operation is not relevant
186
if (!operation.hasContext(context)) {
187             return Status.OK_STATUS;
188         }
189
190         // if the operation does not support advanced validation,
191
// then we assume it is valid.
192
if (doing == EXECUTING) {
193             if (!(operation instanceof IAdvancedUndoableOperation2)) {
194                 return Status.OK_STATUS;
195             }
196         } else {
197             if (!(operation instanceof IAdvancedUndoableOperation)) {
198                 return Status.OK_STATUS;
199             }
200         }
201
202         // The next two methods make a number of UI calls, so we wrap the
203
// whole thing up in a syncExec.
204
final IStatus[] status = new IStatus[1];
205         Workbench.getInstance().getDisplay().syncExec(new Runnable JavaDoc() {
206             public void run() {
207                 // Compute the undoable or redoable status
208
status[0] = computeOperationStatus(operation, history, uiInfo,
209                         doing);
210
211                 // Report non-OK statuses to the user. In some cases, the user
212
// may choose to proceed, and the returned status will be
213
// different than what is reported.
214
if (!status[0].isOK()) {
215                     status[0] = reportAndInterpretStatus(status[0], uiInfo,
216                             operation, doing);
217                 }
218
219             }
220         });
221
222         // If the operation is still not OK, inform the history that the
223
// operation has changed, since it was previously believed to be valid.
224
// We rely here on the ability of an IAdvancedUndoableOperation to
225
// correctly report canUndo() and canRedo() once the undoable and
226
// redoable status have been computed.
227
if (!status[0].isOK()) {
228             history.operationChanged(operation);
229         }
230         return status[0];
231     }
232
233     private IStatus computeOperationStatus(IUndoableOperation operation,
234             IOperationHistory history, IAdaptable uiInfo, int doing) {
235         try {
236             StatusReportingRunnable runnable = new StatusReportingRunnable(
237                     operation, history, uiInfo, doing);
238             TimeTriggeredProgressMonitorDialog progressDialog = new TimeTriggeredProgressMonitorDialog(
239                     getShell(uiInfo), PlatformUI.getWorkbench()
240                             .getProgressService().getLongOperationTime());
241
242             progressDialog.run(false, true, runnable);
243             return runnable.getStatus();
244         } catch (OperationCanceledException e) {
245             return Status.CANCEL_STATUS;
246         } catch (InvocationTargetException JavaDoc e) {
247             reportException(e, uiInfo);
248             return IOperationHistory.OPERATION_INVALID_STATUS;
249         } catch (InterruptedException JavaDoc e) {
250             // Operation was cancelled and acknowledged by runnable with this
251
// exception. Do nothing.
252
return Status.CANCEL_STATUS;
253         }
254     }
255
256     /*
257      * Report the specified execution exception to the log and to the user.
258      */

259     private void reportException(Exception JavaDoc e, IAdaptable uiInfo) {
260         Throwable JavaDoc nestedException = StatusUtil.getCause(e);
261         Throwable JavaDoc exception = (nestedException == null) ? e : nestedException;
262         String JavaDoc title = WorkbenchMessages.Error;
263         String JavaDoc message = WorkbenchMessages.WorkbenchWindow_exceptionMessage;
264         String JavaDoc exceptionMessage = exception.getMessage();
265         if (exceptionMessage == null) {
266             exceptionMessage = message;
267         }
268         IStatus status = new Status(IStatus.ERROR,
269                 WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception);
270         WorkbenchPlugin.log(message, status);
271
272         boolean createdShell = false;
273         Shell shell = getShell(uiInfo);
274         if (shell == null) {
275             createdShell = true;
276             shell = new Shell();
277         }
278         ErrorDialog.openError(shell, title, message, status);
279         if (createdShell) {
280             shell.dispose();
281         }
282     }
283
284     /*
285      * Report a non-OK status to the user
286      */

287     private IStatus reportAndInterpretStatus(IStatus status, IAdaptable uiInfo,
288             IUndoableOperation operation, int doing) {
289         // Nothing to report if we are running automated tests. We will treat
290
// warnings as if they were authorized by the user.
291
if (AUTOMATED_MODE) {
292             if (status.getSeverity() == IStatus.WARNING) {
293                 return Status.OK_STATUS;
294             }
295             return status;
296         }
297         
298         // CANCEL status is assumed to be initiated by the user, so there
299
// is nothing to report.
300
if (status.getSeverity() == IStatus.CANCEL) {
301             return status;
302         }
303
304         // Other status severities are reported with a message dialog.
305
// First obtain a shell and set up the dialog title.
306
boolean createdShell = false;
307         IStatus reportedStatus = status;
308
309         Shell shell = getShell(uiInfo);
310         if (shell == null) {
311             createdShell = true;
312             shell = new Shell();
313         }
314
315         // Set up the dialog. For non-error statuses, we use a warning dialog
316
// that allows the user to proceed or to cancel out of the operation.
317

318         if (!(status.getSeverity() == IStatus.ERROR)) {
319             String JavaDoc warning, title;
320             switch (doing) {
321             case UNDOING:
322                 warning = WorkbenchMessages.Operations_proceedWithNonOKUndoStatus;
323                 if (status.getSeverity() == IStatus.INFO) {
324                     title = WorkbenchMessages.Operations_undoInfo;
325                 } else {
326                     title = WorkbenchMessages.Operations_undoWarning;
327                 }
328                 break;
329             case REDOING:
330                 warning = WorkbenchMessages.Operations_proceedWithNonOKRedoStatus;
331                 if (status.getSeverity() == IStatus.INFO) {
332                     title = WorkbenchMessages.Operations_redoInfo;
333                 } else {
334                     title = WorkbenchMessages.Operations_redoWarning;
335                 }
336                 break;
337             default: // EXECUTING
338
warning = WorkbenchMessages.Operations_proceedWithNonOKExecuteStatus;
339                 if (status.getSeverity() == IStatus.INFO) {
340                     title = WorkbenchMessages.Operations_executeInfo;
341                 } else {
342                     title = WorkbenchMessages.Operations_executeWarning;
343                 }
344                 break;
345             }
346
347             String JavaDoc message = NLS.bind(warning, new Object JavaDoc[] { status.getMessage(), operation.getLabel() });
348             String JavaDoc[] buttons = new String JavaDoc[] { IDialogConstants.YES_LABEL,
349                     IDialogConstants.NO_LABEL };
350             MessageDialog dialog = new MessageDialog(shell, title, null,
351                     message, MessageDialog.WARNING, buttons, 0);
352             int dialogAnswer = dialog.open();
353             // The user has been given the specific status and has chosen
354
// to proceed or to cancel. The user choice determines what
355
// the status should be at this point, OK or CANCEL.
356
if (dialogAnswer == Window.OK) {
357                 reportedStatus = Status.OK_STATUS;
358             } else {
359                 reportedStatus = Status.CANCEL_STATUS;
360             }
361         } else {
362             String JavaDoc title, stopped;
363             switch (doing) {
364             case UNDOING:
365                 title = WorkbenchMessages.Operations_undoProblem;
366                 stopped = WorkbenchMessages.Operations_stoppedOnUndoErrorStatus;
367                 break;
368             case REDOING:
369                 title = WorkbenchMessages.Operations_redoProblem;
370                 stopped = WorkbenchMessages.Operations_stoppedOnRedoErrorStatus;
371                 break;
372             default: // EXECUTING
373
title = WorkbenchMessages.Operations_executeProblem;
374                 stopped = WorkbenchMessages.Operations_stoppedOnExecuteErrorStatus;
375
376                 break;
377             }
378
379             // It is an error condition. The user has no choice to proceed, so
380
// we only report what has gone on. We use a warning icon instead of
381
// an error icon since there has not yet been a failure.
382

383             String JavaDoc message = NLS.bind(stopped, status.getMessage(), operation
384                     .getLabel());
385
386             MessageDialog dialog = new MessageDialog(shell, title, null,
387                     message, MessageDialog.WARNING,
388                     new String JavaDoc[] { IDialogConstants.OK_LABEL }, 0); // ok
389
dialog.open();
390         }
391
392         if (createdShell) {
393             shell.dispose();
394         }
395
396         return reportedStatus;
397
398     }
399
400     /*
401      * Return the shell described by the supplied uiInfo, or null if no shell is
402      * described.
403      */

404     Shell getShell(IAdaptable uiInfo) {
405         if (uiInfo != null) {
406             Shell shell = (Shell) Util.getAdapter(uiInfo, Shell.class);
407             if (shell != null) {
408                 return shell;
409             }
410         }
411         return null;
412     }
413 }
414
Popular Tags