KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > operation > ModalContext


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.jface.operation;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14
15 import org.eclipse.core.runtime.IProgressMonitor;
16 import org.eclipse.core.runtime.OperationCanceledException;
17 import org.eclipse.core.runtime.ProgressMonitorWrapper;
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.swt.widgets.Display;
20
21 /**
22  * Utility class for supporting modal operations.
23  * The runnable passed to the <code>run</code> method is executed in a
24  * separate thread, depending on the value of the passed fork argument.
25  * If the runnable is executed in a separate thread then the current thread
26  * either waits until the new thread ends or, if the current thread is the
27  * UI thread, it polls the SWT event queue and dispatches each event.
28  * <p>
29  * This class is not intended to be subclassed.
30  * </p>
31  */

32 public class ModalContext {
33
34     /**
35      * Indicated whether ModalContext is in debug mode;
36      * <code>false</code> by default.
37      */

38     private static boolean debug = false;
39     
40     /**
41      * The number of nested modal runs, or 0 if not inside a modal run.
42      * This is global state.
43      */

44     private static int modalLevel = 0;
45
46     /**
47      * Indicates whether operations should be run in a separate thread.
48      * Defaults to true.
49      * For internal debugging use, set to false to run operations in the calling thread.
50      */

51     private static boolean runInSeparateThread = true;
52
53     /**
54      * Thread which runs the modal context.
55      */

56     private static class ModalContextThread extends Thread JavaDoc {
57         /**
58          * The operation to be run.
59          */

60         private IRunnableWithProgress runnable;
61
62         /**
63          * The exception thrown by the operation starter.
64          */

65         private Throwable JavaDoc throwable;
66
67         /**
68          * The progress monitor used for progress and cancelation.
69          */

70         private IProgressMonitor progressMonitor;
71
72         /**
73          * The display used for event dispatching.
74          */

75         private Display display;
76
77         /**
78          * Indicates whether to continue event queue dispatching.
79          */

80         private volatile boolean continueEventDispatching = true;
81         
82         /**
83          * The thread that forked this modal context thread.
84          *
85          * @since 3.1
86          */

87         private Thread JavaDoc callingThread;
88         
89         /**
90          * Creates a new modal context.
91          *
92          * @param operation the runnable to run
93          * @param monitor the progress monitor to use to display progress and receive
94          * requests for cancelation
95          * @param display the display to be used to read and dispatch events
96          */

97         private ModalContextThread(IRunnableWithProgress operation,
98                 IProgressMonitor monitor, Display display) {
99             super("ModalContext"); //$NON-NLS-1$
100
Assert.isTrue(monitor != null && display != null);
101             runnable = operation;
102             progressMonitor = new AccumulatingProgressMonitor(monitor, display);
103             this.display = display;
104             this.callingThread = Thread.currentThread();
105         }
106
107         /* (non-Javadoc)
108          * Method declared on Thread.
109          */

110         public void run() {
111             try {
112                 if (runnable != null) {
113                     runnable.run(progressMonitor);
114                 }
115             } catch (InvocationTargetException JavaDoc e) {
116                 throwable = e;
117             } catch (InterruptedException JavaDoc e) {
118                 throwable = e;
119             } catch (RuntimeException JavaDoc e) {
120                 throwable = e;
121             } catch (ThreadDeath JavaDoc e) {
122                 // Make sure to propagate ThreadDeath, or threads will never fully terminate
123
throw e;
124             } catch (Error JavaDoc e) {
125                 throwable = e;
126             } finally {
127                 //notify the operation of change of thread of control
128
if (runnable instanceof IThreadListener) {
129                     ((IThreadListener)runnable).threadChange(callingThread);
130                 }
131                 
132                 // Make sure that all events in the asynchronous event queue
133
// are dispatched.
134
display.syncExec(new Runnable JavaDoc() {
135                     public void run() {
136                         // do nothing
137
}
138                 });
139
140                 // Stop event dispatching
141
continueEventDispatching = false;
142
143                 // Force the event loop to return from sleep () so that
144
// it stops event dispatching.
145
display.asyncExec(null);
146             }
147         }
148
149         /**
150          * Processes events or waits until this modal context thread terminates.
151          */

152         public void block() {
153             if (display == Display.getCurrent()) {
154                 while (continueEventDispatching) {
155                     // Run the event loop. Handle any uncaught exceptions caused
156
// by UI events.
157
try {
158                         if (!display.readAndDispatch()) {
159                             display.sleep();
160                         }
161                     }
162                     // ThreadDeath is a normal error when the thread is dying. We must
163
// propagate it in order for it to properly terminate.
164
catch (ThreadDeath JavaDoc e) {
165                         throw (e);
166                     }
167                     // For all other exceptions, log the problem.
168
catch (Throwable JavaDoc e) {
169                         System.err.println("Unhandled event loop exception during blocked modal context."); //$NON-NLS-1$
170
e.printStackTrace();
171                     }
172                 }
173             } else {
174                 try {
175                     join();
176                 } catch (InterruptedException JavaDoc e) {
177                     throwable = e;
178                 }
179             }
180         }
181     }
182
183     /**
184      * Returns whether the first progress monitor is the same as, or
185      * a wrapper around, the second progress monitor.
186      *
187      * @param monitor1 the first progress monitor
188      * @param monitor2 the second progress monitor
189      * @return <code>true</code> if the first is the same as, or
190      * a wrapper around, the second
191      * @see ProgressMonitorWrapper
192      */

193     public static boolean canProgressMonitorBeUsed(IProgressMonitor monitor1,
194             IProgressMonitor monitor2) {
195         if (monitor1 == monitor2) {
196             return true;
197         }
198
199         while (monitor1 instanceof ProgressMonitorWrapper) {
200             monitor1 = ((ProgressMonitorWrapper) monitor1)
201                     .getWrappedProgressMonitor();
202             if (monitor1 == monitor2) {
203                 return true;
204             }
205         }
206         return false;
207     }
208
209     /**
210      * Checks with the given progress monitor and throws
211      * <code>InterruptedException</code> if it has been canceled.
212      * <p>
213      * Code in a long-running operation should call this method
214      * regularly so that a request to cancel will be honored.
215      * </p>
216      * <p>
217      * Convenience for:
218      * <pre>
219      * if (monitor.isCanceled())
220      * throw new InterruptedException();
221      * </pre>
222      * </p>
223      *
224      * @param monitor the progress monitor
225      * @exception InterruptedException if cancelling the operation has been requested
226      * @see IProgressMonitor#isCanceled()
227      */

228     public static void checkCanceled(IProgressMonitor monitor)
229             throws InterruptedException JavaDoc {
230         if (monitor.isCanceled()) {
231             throw new InterruptedException JavaDoc();
232         }
233     }
234
235     /**
236      * Returns the currently active modal context thread, or null if no modal context is active.
237      */

238     private static ModalContextThread getCurrentModalContextThread() {
239         Thread JavaDoc t = Thread.currentThread();
240         if (t instanceof ModalContextThread) {
241             return (ModalContextThread) t;
242         }
243         return null;
244     }
245
246     /**
247      * Returns the modal nesting level.
248      * <p>
249      * The modal nesting level increases by one each time the
250      * <code>ModalContext.run</code> method is called within the
251      * dynamic scope of another call to <code>ModalContext.run</code>.
252      * </p>
253      *
254      * @return the modal nesting level, or <code>0</code> if
255      * this method is called outside the dynamic scope of any
256      * invocation of <code>ModalContext.run</code>
257      */

258     public static int getModalLevel() {
259         return modalLevel;
260     }
261
262     /**
263      * Returns whether the given thread is running a modal context.
264      *
265      * @param thread The thread to be checked
266      * @return <code>true</code> if the given thread is running a modal context, <code>false</code> if not
267      */

268     public static boolean isModalContextThread(Thread JavaDoc thread) {
269         return thread instanceof ModalContextThread;
270     }
271
272     /**
273      * Runs the given runnable in a modal context, passing it a progress monitor.
274      * <p>
275      * The modal nesting level is increased by one from the perspective
276      * of the given runnable.
277      * </p>
278      *<p>
279      * If the supplied operation implements <code>IThreadListener</code>, it
280      * will be notified of any thread changes required to execute the operation.
281      * Specifically, the operation will be notified of the thread that will call its
282      * <code>run</code> method before it is called, and will be notified of the
283      * change of control back to the thread calling this method when the operation
284      * completes. These thread change notifications give the operation an
285      * opportunity to transfer any thread-local state to the execution thread before
286      * control is transferred to the new thread.
287      *</p>
288      * @param operation the runnable to run
289      * @param fork <code>true</code> if the runnable should run in a separate thread,
290      * and <code>false</code> if in the same thread
291      * @param monitor the progress monitor to use to display progress and receive
292      * requests for cancelation
293      * @param display the display to be used to read and dispatch events
294      * @exception InvocationTargetException if the run method must propagate a checked exception,
295      * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions and errors are automatically
296      * wrapped in an <code>InvocationTargetException</code> by this method
297      * @exception InterruptedException if the operation detects a request to cancel,
298      * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
299      * <code>InterruptedException</code>; this method propagates the exception
300      */

301     public static void run(IRunnableWithProgress operation, boolean fork,
302             IProgressMonitor monitor, Display display)
303             throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
304         Assert.isTrue(operation != null && monitor != null);
305
306         modalLevel++;
307         try {
308             if (monitor != null) {
309                 monitor.setCanceled(false);
310             }
311             // Is the runnable supposed to be execute in the same thread.
312
if (!fork || !runInSeparateThread) {
313                 runInCurrentThread(operation, monitor);
314             } else {
315                 ModalContextThread t = getCurrentModalContextThread();
316                 if (t != null) {
317                     Assert.isTrue(canProgressMonitorBeUsed(monitor,
318                             t.progressMonitor));
319                     runInCurrentThread(operation, monitor);
320                 } else {
321                     t = new ModalContextThread(operation, monitor, display);
322                     if (operation instanceof IThreadListener) {
323                         ((IThreadListener)operation).threadChange(t);
324                     }
325                     t.start();
326                     t.block();
327                     Throwable JavaDoc throwable = t.throwable;
328                     if (throwable != null) {
329                         if (debug
330                                 && !(throwable instanceof InterruptedException JavaDoc)
331                                 && !(throwable instanceof OperationCanceledException)) {
332                             System.err
333                                     .println("Exception in modal context operation:"); //$NON-NLS-1$
334
throwable.printStackTrace();
335                             System.err.println("Called from:"); //$NON-NLS-1$
336
// Don't create the InvocationTargetException on the throwable,
337
// otherwise it will print its stack trace (from the other thread).
338
new InvocationTargetException JavaDoc(null)
339                                     .printStackTrace();
340                         }
341                         if (throwable instanceof InvocationTargetException JavaDoc) {
342                             throw (InvocationTargetException JavaDoc) throwable;
343                         } else if (throwable instanceof InterruptedException JavaDoc) {
344                             throw (InterruptedException JavaDoc) throwable;
345                         } else if (throwable instanceof OperationCanceledException) {
346                             // See 1GAN3L5: ITPUI:WIN2000 - ModalContext converts OperationCancelException into InvocationTargetException
347
throw new InterruptedException JavaDoc(throwable
348                                     .getMessage());
349                         } else {
350                             throw new InvocationTargetException JavaDoc(throwable);
351                         }
352                     }
353                 }
354             }
355         } finally {
356             modalLevel--;
357         }
358     }
359
360     /**
361      * Run a runnable. Convert all thrown exceptions to
362      * either InterruptedException or InvocationTargetException
363      */

364     private static void runInCurrentThread(IRunnableWithProgress runnable,
365             IProgressMonitor progressMonitor) throws InterruptedException JavaDoc,
366             InvocationTargetException JavaDoc {
367         try {
368             if (runnable != null) {
369                 runnable.run(progressMonitor);
370             }
371         } catch (InvocationTargetException JavaDoc e) {
372             throw e;
373         } catch (InterruptedException JavaDoc e) {
374             throw e;
375         } catch (OperationCanceledException e) {
376             throw new InterruptedException JavaDoc();
377         } catch (ThreadDeath JavaDoc e) {
378             // Make sure to propagate ThreadDeath, or threads will never fully terminate
379
throw e;
380         } catch (RuntimeException JavaDoc e) {
381             throw new InvocationTargetException JavaDoc(e);
382         } catch (Error JavaDoc e) {
383             throw new InvocationTargetException JavaDoc(e);
384         }
385     }
386
387     /**
388      * Sets whether ModalContext is running in debug mode.
389      *
390      * @param debugMode <code>true</code> for debug mode,
391      * and <code>false</code> for normal mode (the default)
392      */

393     public static void setDebugMode(boolean debugMode) {
394         debug = debugMode;
395     }
396
397     /**
398      * Sets whether ModalContext may process events (by calling <code>Display.readAndDispatch()</code>)
399      * while running operations. By default, ModalContext will process events while running operations.
400      * Use this method to disallow event processing temporarily.
401      * @param allowReadAndDispatch <code>true</code> (the default) if events may be processed while
402      * running an operation, <code>false</code> if Display.readAndDispatch() should not be called
403      * from ModalContext.
404      * @since 3.2
405      */

406     public static void setAllowReadAndDispatch(boolean allowReadAndDispatch) {
407         // use a separate thread if and only if it is OK to spin the event loop
408
runInSeparateThread = allowReadAndDispatch;
409     }
410 }
411
Popular Tags