1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.foundation;
20
21 import java.awt.AWTEvent;
22 import java.awt.EventQueue;
23 import java.awt.Toolkit;
24 import java.awt.event.InvocationEvent;
25 import java.util.EmptyStackException;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.ListIterator;
29
30 /***
31 * NSRunLoop is provided specifically for EODelayedObserverQueue
32 * and EOEditingContext, which assume the existence of a
33 * prioritized event queue that Java does not provide. <br><br>
34 *
35 * This extends java.awt.EventQueue and does not conform to the
36 * NSRunLoop specifications. The only supported methods are
37 * NSRunLoop.currentRunLoop, performSelectorWithOrder, and
38 * cancelSelectorWithOrder. Note that in Swing there is only
39 * one AWT thread and one event queue; newly created threads
40 * will not get their own run loop as in OpenStep.<br><br>
41 *
42 * That said, this event queue is servicable as a replacement
43 * for the default event queue and will provide prioritized
44 * execution of selectors before and after normal AWT events.
45 * <br><br>
46 *
47 * Each run loop dispatches the lowest order event from
48 * the queue. When queued events have the same ordering,
49 * they are dispatched as first-in, first-out (FIFO). Because
50 * all AWT events have the same ordering (AWTEventsRunLoopOrdering),
51 * they are processed FIFO, just like the default event queue. <br><br>
52 *
53 * Note that because EventQueue is not well-factored for
54 * subclassing, pushing a new event queue onto the stack
55 * on top of this one will only copy the existing AWT events
56 * to the new queue. For this reason, pushing new event
57 * queues onto the stack is not supported and will throw
58 * an exception.
59 *
60 * @author michael@mpowers.net
61 * @author $Author: cgruber $
62 * @version $Revision: 893 $
63 */
64 public class NSRunLoop extends EventQueue
65 {
66 /***
67 * This is the ordering at which the conventional AWT event
68 * queue will be executed. Selectors with this ordering
69 * or less will be executed before AWT events, and
70 * selectors with ordering greater than this ordering will be
71 * be executed after AWT events.
72 */
73 public static final int AWTEventsRunLoopOrdering = 500000;
74
75 /***
76 * The singleton instance.
77 */
78 protected static NSRunLoop instance;
79
80 private LinkedList earlyQueue;
81 private LinkedList lateQueue;
82
83 /***
84 * Needed because JDK1.4 made our lives more difficult.
85 */
86 private static Toolkit toolkit;
87
88 /***
89 * Because SunToolkit.flushPendingEvents was changed
90 * to a static method in 1.4, you can't compile the library
91 * in such a way that it works with both 1.3 and 1.4.
92 * So we have to rely on dynamic method invocation,
93 * which is slower, but we try to make it as fast as
94 * humanly possible.
95 */
96 private static NSSelector flushPendingEvents;
97
98 /***
99 * Create a new instance of NSRunLoop.
100 */
101 protected NSRunLoop ()
102 {
103 earlyQueue = new LinkedList();
104 lateQueue = new LinkedList();
105 }
106
107 /***
108 * Returns the singleton instance of NSRunLoop.
109 * This returns the same instance no matter what
110 * thread calls it, which is different from OpenStep.
111 * NSRunLoop is limited to a singleton instance
112 * because there is no way of obtaining the stack
113 * of event queues from EventQueue because it is
114 * private state.
115 */
116 public synchronized static NSRunLoop currentRunLoop ()
117 {
118 if ( instance == null )
119 {
120
121 flushPendingEvents = new NSSelector( "flushPendingEvents" );
122 toolkit = Toolkit.getDefaultToolkit();
123 instance = new NSRunLoop();
124
125 toolkit.getSystemEventQueue().push( instance );
126 }
127
128 return instance;
129 }
130
131 /***
132 * Post a 1.1-style event to the EventQueue. If there is an
133 * existing event on the queue with the same ID and event source,
134 * the source Component's coalesceEvents method will be called.
135 *
136 * @param theEvent an instance of java.awt.AWTEvent, or a
137 * subclass of it.
138 */
139 public void postEvent(AWTEvent theEvent)
140 {
141 if ( theEvent instanceof OrderedInvocationEvent )
142 {
143 OrderedInvocationEvent event = (OrderedInvocationEvent) theEvent;
144 if ( event.getOrdering() > AWTEventsRunLoopOrdering )
145 {
146 insertEventIntoQueue( event, lateQueue );
147 }
148 else
149 {
150 insertEventIntoQueue( event, earlyQueue );
151 }
152 }
153 else
154 {
155 super.postEvent( theEvent );
156 }
157 }
158
159 private synchronized void insertEventIntoQueue( OrderedInvocationEvent e, LinkedList q )
160 {
161 OrderedInvocationEvent o;
162 int ordering = e.getOrdering();
163 ListIterator iterator =
164 q.listIterator();
165
166
167
168
169 while ( iterator.hasNext() )
170 {
171 o = (OrderedInvocationEvent) iterator.next();
172 if ( o.getOrdering() > ordering )
173 {
174
175 iterator.previous();
176 break;
177 }
178 }
179
180 iterator.add( e );
181 }
182
183 /***
184 * Useful method, but not in the spec.
185 * Dispatches the next AWT event in the queue.
186 * Returns whether a selector or an event was executed:
187 * if the event queue is empty, returns false.
188 */
189 public boolean dispatchNextEvent()
190 {
191
192 if ( peekEvent() == null )
193 {
194 return false;
195 }
196
197
198 try
199 {
200 dispatchEvent( getNextEvent() );
201 }
202 catch ( InterruptedException exc )
203 {
204 System.out.println( "NSRunLoop: error while dispatching event: " );
205 exc.printStackTrace();
206 }
207 return true;
208 }
209
210 /***
211 * Useful method, but not in the spec.
212 * Dispatches all events in the queue before returning.
213 */
214 public void dispatchAllEvents()
215 {
216 while ( dispatchNextEvent() );
217 }
218
219 /***
220 * Remove an event from the EventQueue and return it.
221 * This override will dispatch all selectors up to 5000,
222 * and then check if there are AWT events on the queue.
223 * If the queue is empty, all remaining selectors
224 * are dispatched. Then, this method calls the
225 * super class' implementation.
226 * @return the next AWTEvent
227 * @exception InterruptedException
228 * if another thread has interrupted this thread.
229 */
230 public AWTEvent getNextEvent() throws InterruptedException
231 {
232
233
234
235
236
237
238
239 AWTEvent result;
240
241 while ( true )
242 {
243
244
245
246
247 try
248 {
249 flushPendingEvents.invoke( toolkit );
250 }
251 catch ( Throwable t )
252 {
253 System.out.println( "NSRunLoop.getNextEvent: " + Thread.currentThread() );
254 System.err.println( "Unexpected error while flushing pending events: " );
255 t.printStackTrace();
256 };
257
258 synchronized( this )
259 {
260 result = popNextEarlyEvent();
261 if ( result != null )
262 {
263
264 return result;
265 }
266 }
267
268 if ( ( result = peekEvent() ) != null )
269 {
270
271 return super.getNextEvent();
272 }
273
274 synchronized( this )
275 {
276 result = popNextLateEvent();
277 if ( result != null )
278 {
279
280 return result;
281 }
282
283
284
285 wait();
286
287 }
288 }
289 }
290
291 private AWTEvent popNextEarlyEvent()
292 {
293 if ( earlyQueue == null ) return null;
294 if ( earlyQueue.isEmpty() ) return null;
295 return (AWTEvent) earlyQueue.removeFirst();
296 }
297
298 private AWTEvent popNextLateEvent()
299 {
300 if ( lateQueue == null ) return null;
301 if ( lateQueue.isEmpty() ) return null;
302 return (AWTEvent) lateQueue.removeFirst();
303 }
304
305 /***
306 * This implementation calls super and then throws an
307 * UnsupportedOperationException. Catch that exception
308 * and ignore it if you know what you are doing.
309 */
310 public synchronized void push(EventQueue newEventQueue)
311 {
312 super.push( newEventQueue );
313 throw new UnsupportedOperationException(
314 "NSRunLoop may not function properly with push()" );
315 }
316
317 /***
318 * This implementation calls super and then throws an
319 * UnsupportedOperationException. Catch that exception
320 * and ignore it if you know what you are doing.
321 */
322 protected void pop() throws EmptyStackException
323 {
324 super.pop();
325 throw new UnsupportedOperationException(
326 "NSRunLoop may not function properly with pop()" );
327 }
328
329 /***
330 * Schedules the specified selector with the specified target and parameter
331 * to be invoked on the next event loop with the specified ordering.
332 * The selector must be able to be invoked on the target and the target method
333 * must accept the parameter. aModeList is currently ignored.
334 */
335 public void performSelectorWithOrder(
336 NSSelector aSelector, Object aTarget, Object aParameter, int anOrdering, List aModeList )
337 {
338 postEvent( new OrderedInvocationEvent( aSelector, aTarget, aParameter, anOrdering, aModeList ) );
339 }
340
341 /***
342 * Cancels the next scheduled invocation of the specified selector, target, and parameter.
343 * If no such invocation is scheduled, does nothing.
344 */
345 public synchronized void cancelPerformSelectorWithOrder(
346 NSSelector aSelector, Object aTarget, Object aParameter )
347 {
348 ListIterator i;
349 i = earlyQueue.listIterator();
350 while ( i.hasNext() )
351 {
352 if ( ((OrderedInvocationEvent)i.next()).compareTo(
353 aSelector, aTarget, aParameter ) )
354 {
355 i.remove();
356 return;
357 }
358 }
359 i = lateQueue.listIterator();
360 while ( i.hasNext() )
361 {
362 if ( ((OrderedInvocationEvent)i.next()).compareTo(
363 aSelector, aTarget, aParameter ) )
364 {
365 i.remove();
366 return;
367 }
368 }
369 }
370
371 /***
372 * Causes runnable to have its run() method on the next
373 * event loop with the specified priority ordering.
374 */
375 public static void invokeLaterWithOrder(Runnable aRunnable, int anOrdering) {
376 currentRunLoop().postEvent(
377 new OrderedInvocationEvent( Toolkit.getDefaultToolkit(), aRunnable, anOrdering ) );
378 }
379
380 /***
381 * An invocation event that can specify a priority for execution.
382 * The prioritization only works if the current event queue is an
383 * NSRunLoop; otherwise, performs as a normal invocation event.
384 */
385 private static class OrderedInvocationEvent extends InvocationEvent
386 {
387 int ordering;
388 NSSelector selector = null;
389 Object target = null;
390 Object parameter = null;
391
392 /***
393 * Constructs an InvocationEvent with the specified source which will
394 * execute the runnable's run() method when dispatched at the specified ordering.
395 */
396 public OrderedInvocationEvent(Object source,
397 Runnable runnable, int anOrdering)
398 {
399 super( source, runnable );
400 ordering = anOrdering;
401 }
402
403 /***
404 * Constructs an InvocationEvent with the specified source which will
405 * execute the runnable's run() method when dispatched at the specified ordering.
406 * If notifier is non-null, notifyAll() will be called on it immediately after run() returns.
407 */
408 public OrderedInvocationEvent(Object source,
409 Runnable runnable,
410 Object notifier,
411 boolean catchExceptions, int anOrdering)
412 {
413 super( source, runnable, notifier, catchExceptions );
414 ordering = anOrdering;
415 }
416
417 OrderedInvocationEvent(
418 final NSSelector aSelector,
419 final Object aTarget,
420 final Object aParameter,
421 int anOrdering, List aModeList)
422 {
423 this( Toolkit.getDefaultToolkit(), new Runnable()
424 {
425 public void run()
426 {
427 try
428 {
429 aSelector.invoke( aTarget, aParameter );
430 }
431 catch ( Exception exc )
432 {
433 System.out.println( "NSRunLoop: error invoking selector: " );
434 exc.printStackTrace();
435 }
436 }
437 }, anOrdering );
438
439 selector = aSelector;
440 target = aTarget;
441 parameter = aParameter;
442 }
443
444 /***
445 * Called by cancelPerformSelectorWithOrder.
446 * Compares against the specified arguments.
447 */
448 boolean compareTo( NSSelector aSelector, Object aTarget, Object aParameter )
449 {
450 return (
451 compareByValue( selector, aSelector ) &&
452 compareByValue( target, aTarget ) &&
453 compareByValue( parameter, aParameter ) );
454 }
455
456 private boolean compareByValue( Object first, Object second )
457 {
458 if ( first == second ) return true;
459 if ( first == null ) return second.equals( first );
460 return first.equals( second );
461
462 }
463
464 /***
465 * Returns the ordering for this event in the run loop.
466 */
467 public int getOrdering()
468 {
469 return ordering;
470 }
471
472 }
473
474 }
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522