Coverage Report - net.wotonomy.foundation.NSRunLoop
 
Classes in this File Line Coverage Branch Coverage Complexity
NSRunLoop
0% 
0% 
2.762
 
 1  0
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2001 Intersect Software Corporation
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 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  0
     protected NSRunLoop ()
 102  0
     {
 103  0
         earlyQueue = new LinkedList();
 104  0
         lateQueue  = new LinkedList();
 105  0
     }
 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  0
         if ( instance == null )
 119  
         {          
 120  
              // create and initialize
 121  0
              flushPendingEvents = new NSSelector( "flushPendingEvents" );
 122  0
              toolkit = Toolkit.getDefaultToolkit();
 123  0
              instance = new NSRunLoop();
 124  
              
 125  0
              toolkit.getSystemEventQueue().push( instance );
 126  
         }
 127  
             
 128  0
         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  0
         if ( theEvent instanceof OrderedInvocationEvent )
 142  
         {
 143  0
             OrderedInvocationEvent event = (OrderedInvocationEvent) theEvent;
 144  0
             if ( event.getOrdering() > AWTEventsRunLoopOrdering )
 145  
             {
 146  0
                 insertEventIntoQueue( event, lateQueue );
 147  0
             }
 148  
             else
 149  
             {
 150  0
                 insertEventIntoQueue( event, earlyQueue );
 151  
             }
 152  0
         }
 153  
         else
 154  
         {
 155  0
             super.postEvent( theEvent );
 156  
         }
 157  0
     }
 158  
     
 159  
     private synchronized void insertEventIntoQueue( OrderedInvocationEvent e, LinkedList q )
 160  
     {
 161  
         OrderedInvocationEvent o;
 162  0
         int ordering = e.getOrdering();
 163  0
         ListIterator iterator = 
 164  0
             q.listIterator();
 165  
     
 166  
         // iterate forwards until we find a priority
 167  
         //   greater than our priority,
 168  
         //   then insert ourself before that element.
 169  0
         while ( iterator.hasNext() )
 170  
         {
 171  0
             o = (OrderedInvocationEvent) iterator.next();   
 172  0
             if ( o.getOrdering() > ordering )
 173  
             {
 174  
                 // back up one
 175  0
                 iterator.previous();
 176  0
                 break;
 177  
             }
 178  
         }
 179  
         // add after the current element
 180  0
         iterator.add( e );
 181  0
     }
 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  
         // check for empty queue to avoid blocking
 192  0
         if ( peekEvent() == null ) 
 193  
         {
 194  0
             return false;
 195  
         }
 196  
         
 197  
         // queue not empty: dispatch the next event
 198  
         try
 199  
         {
 200  0
             dispatchEvent( getNextEvent() );
 201  
         }
 202  0
         catch ( InterruptedException exc )
 203  
         {
 204  0
             System.out.println( "NSRunLoop: error while dispatching event: " );
 205  0
             exc.printStackTrace();
 206  0
         }
 207  0
         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  0
         while ( dispatchNextEvent() );
 217  0
     }
 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  
         //NOTE: it's currently unclear to me whether we should 
 233  
         // be operating as a run loop or as a priority queue.
 234  
         // I'm opting for priority queue now, but that means that
 235  
         // selectors that requeue themselves could hang the application.
 236  
         // In the future, we could fake a run loop by putting a marker 
 237  
         // event on the AWT queue to mark the boundary between loops.
 238  
         
 239  
         AWTEvent result;
 240  
         
 241  0
         while ( true )
 242  
         {
 243  
             //NOTE: as of java 1.4, we have to flush pending events
 244  
             // using this cheesy undocumented method on suntoolkit.
 245  
             // Unsurprisingly, java.awt.EventQueue got worse, not better.
 246  
             // See notes above about our use of an NSSelector.
 247  
             try
 248  
             {
 249  0
                 flushPendingEvents.invoke( toolkit );
 250  
             } 
 251  0
             catch ( Throwable t ) 
 252  
             {
 253  0
                 System.out.println( "NSRunLoop.getNextEvent: " + Thread.currentThread() );        
 254  0
                 System.err.println( "Unexpected error while flushing pending events: " );
 255  0
                 t.printStackTrace();
 256  0
             };
 257  
             
 258  0
             synchronized( this )
 259  
             {
 260  0
                 result = popNextEarlyEvent();
 261  0
                 if ( result != null )
 262  
                 { 
 263  
 //System.out.println( "getNextEvent: early : " + result );            
 264  0
                     return result;
 265  
                 }
 266  0
             }
 267  
             
 268  0
             if ( ( result = peekEvent() ) != null )
 269  
             {
 270  
 //System.out.println( "getNextEvent: AWT : " + result );      
 271  0
                 return super.getNextEvent();
 272  
             }
 273  
             
 274  0
             synchronized( this )
 275  
             {
 276  0
                 result = popNextLateEvent();
 277  0
                 if ( result != null )
 278  
                 {
 279  
 //System.out.println( "getNextEvent: late : " + result );            
 280  0
                     return result;
 281  
                 }
 282  
                 
 283  
                 // yield
 284  
 //System.out.println( "getNextEvent: wait" );            
 285  0
                 wait();
 286  
 //System.out.println( "getNextEvent: notified" );            
 287  0
             }
 288  0
         }
 289  
     }
 290  
 
 291  
     private AWTEvent popNextEarlyEvent()
 292  
     {
 293  0
         if ( earlyQueue == null ) return null; // shouldn't be necessary, but is
 294  0
         if ( earlyQueue.isEmpty() ) return null;
 295  0
         return (AWTEvent) earlyQueue.removeFirst();
 296  
     }
 297  
     
 298  
     private AWTEvent popNextLateEvent()
 299  
     {
 300  0
         if ( lateQueue == null ) return null; // shouldn't be necessary, but is
 301  0
         if ( lateQueue.isEmpty() ) return null;
 302  0
         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  0
         super.push( newEventQueue );
 313  0
         throw new UnsupportedOperationException(
 314  0
             "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  0
         super.pop();
 325  0
         throw new UnsupportedOperationException(
 326  0
             "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  0
         postEvent( new OrderedInvocationEvent( aSelector, aTarget, aParameter, anOrdering, aModeList ) );
 339  0
     }
 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  0
         i = earlyQueue.listIterator();
 350  0
         while ( i.hasNext() )
 351  
         {
 352  0
             if ( ((OrderedInvocationEvent)i.next()).compareTo(
 353  0
                 aSelector, aTarget, aParameter ) )
 354  
             {
 355  0
                 i.remove();
 356  0
                 return;
 357  
             }
 358  
         }
 359  0
         i = lateQueue.listIterator();
 360  0
         while ( i.hasNext() )
 361  
         {
 362  0
             if ( ((OrderedInvocationEvent)i.next()).compareTo(
 363  0
                 aSelector, aTarget, aParameter ) )
 364  
             {
 365  0
                 i.remove();
 366  0
                 return;
 367  
             }
 368  
         }
 369  0
     }
 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  0
         currentRunLoop().postEvent( 
 377  0
             new OrderedInvocationEvent( Toolkit.getDefaultToolkit(), aRunnable, anOrdering ) );
 378  0
     }
 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  0
         NSSelector selector = null;
 389  0
         Object target = null;
 390  0
         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  0
             super( source, runnable );
 400  0
             ordering = anOrdering;
 401  0
         }
 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  0
             super( source, runnable, notifier, catchExceptions );
 414  0
             ordering = anOrdering;
 415  0
         }
 416  
         
 417  
         OrderedInvocationEvent( 
 418  
             final NSSelector aSelector, 
 419  
             final Object aTarget, 
 420  
             final Object aParameter, 
 421  
             int anOrdering, List aModeList)
 422  
         {
 423  0
             this( Toolkit.getDefaultToolkit(), new Runnable() 
 424  
             {
 425  0
                 public void run()
 426  
                 {
 427  
                     try
 428  
                     {
 429  0
                         aSelector.invoke( aTarget, aParameter );
 430  
                     } 
 431  0
                     catch ( Exception exc )
 432  
                     {
 433  0
                         System.out.println( "NSRunLoop: error invoking selector: " );
 434  0
                         exc.printStackTrace();
 435  0
                     }
 436  0
                 }
 437  0
             }, anOrdering );
 438  
             
 439  0
             selector = aSelector;
 440  0
             target = aTarget;
 441  0
             parameter = aParameter;
 442  0
         }
 443  
          
 444  
         /**
 445  
         * Called by cancelPerformSelectorWithOrder.
 446  
         * Compares against the specified arguments.
 447  
         */
 448  
         boolean compareTo( NSSelector aSelector, Object aTarget, Object aParameter )
 449  
         {
 450  0
             return (
 451  0
                 compareByValue( selector, aSelector ) &&
 452  0
                 compareByValue( target, aTarget ) &&
 453  0
                 compareByValue( parameter, aParameter ) );
 454  
         }
 455  
         
 456  
         private boolean compareByValue( Object first, Object second )
 457  
         {
 458  0
             if ( first == second ) return true;
 459  0
             if ( first == null ) return second.equals( first );
 460  0
             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  0
             return ordering;
 470  
         }
 471  
          
 472  
     }
 473  
     
 474  
 }
 475  
 
 476  
 /*
 477  
  * $Log$
 478  
  * Revision 1.2  2006/02/16 13:15:00  cgruber
 479  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 480  
  *
 481  
  * Revision 1.13  2003/06/06 20:48:19  mpowers
 482  
  * Fixed race condition when run loop is started from main thread.
 483  
  * That was causing the dispatch thread to call getNextEvent before the
 484  
  * static fields had been initialized.
 485  
  *
 486  
  * Revision 1.12  2003/06/03 14:52:11  mpowers
 487  
  * Super constructor was calling getNextEvent before selector was created.
 488  
  *
 489  
  * Revision 1.10  2002/05/28 21:59:19  mpowers
 490  
  * We now can compile against 1.3 and 1.4, as well as run against both too.
 491  
  *
 492  
  * Revision 1.9  2002/04/09 18:10:45  mpowers
 493  
  * Fixes for 1.4.  Commented out until we start building on 1.4.
 494  
  *
 495  
  * Revision 1.8  2002/02/13 21:20:15  mpowers
 496  
  * Updated comments.
 497  
  *
 498  
  * Revision 1.7  2001/11/01 15:48:49  mpowers
 499  
  * Additional debug code.
 500  
  *
 501  
  * Revision 1.6  2001/10/30 22:14:35  mpowers
 502  
  * Constructor is now protected, not private.
 503  
  *
 504  
  * Revision 1.5  2001/10/29 20:41:49  mpowers
 505  
  * Improved docs, better support for potential subclassing, invokeLater.
 506  
  *
 507  
  * Revision 1.4  2001/10/26 18:46:30  mpowers
 508  
  * Now running AWT events with the appropriate ordering.
 509  
  * Added invokeLaterWithOrder for java compatibility.
 510  
  *
 511  
  * Revision 1.3  2001/10/26 14:39:46  mpowers
 512  
  * Completed implementation.
 513  
  *
 514  
  * Revision 1.2  2001/10/25 22:20:21  mpowers
 515  
  * Got to check in an interim version - this will briefly break the build.
 516  
  *
 517  
  * Revision 1.1  2001/10/24 19:30:38  mpowers
 518  
  * Initial check-in: incomplete implementation.
 519  
  *
 520  
  *
 521  
  */
 522