Coverage Report - net.wotonomy.control.EODelayedObserverQueue
 
Classes in this File Line Coverage Branch Coverage Complexity
EODelayedObserverQueue
0% 
0% 
2.778
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 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.control;
 20  
 
 21  
 import java.util.Iterator;
 22  
 import java.util.LinkedList;
 23  
 import java.util.List;
 24  
 
 25  
 import net.wotonomy.foundation.NSRunLoop;
 26  
 import net.wotonomy.foundation.NSSelector;
 27  
 
 28  
 /**
 29  
 * EODelayedObserverQueue allows EODelayedObservers 
 30  
 * to receive only one subjectChanged() message
 31  
 * after numerous willChange() messages have 
 32  
 * been sent.  Observers are then notified in order 
 33  
 * of their priority property,
 34  
 * so that certain observers can be notified before
 35  
 * others for whatever application-specific purpose. 
 36  
 * This class is not thread-safe and should be used
 37  
 * only for single-threaded GUI clients (AWT and Swing).
 38  
 * <br><br>
 39  
 * 
 40  
 * Important note: because AWT's event queue does
 41  
 * not allow for priority-based scheduling, this
 42  
 * class installs a custom event queue, replacing
 43  
 * the existing queue on the AWT dispatch thread.
 44  
 * We know of no way around this problem.
 45  
 * <br><br>
 46  
 *
 47  
 * Implementation note: this queue relies on the
 48  
 * result of equals() for maintaining a set of 
 49  
 * objects on the queue.  If two EODelayedObservers
 50  
 * evaluate to the same value using equals(), only
 51  
 * one of them will exist on the queue.  If this,
 52  
 * starts to suck, we can change it.
 53  
 *
 54  
 * @author michael@mpowers.net
 55  
 * @author $Author: cgruber $
 56  
 * @version $Revision: 894 $
 57  
 */
 58  
 
 59  0
 public class EODelayedObserverQueue
 60  
 {
 61  
     /**
 62  
     * The default run loop ordering flushes the delayed observers
 63  
     * up to ObserverPrioritySixth before dispatching the AWT event
 64  
     * queue.  ObserverPriorityLater is run last.
 65  
     */
 66  0
     public static int FlushDelayedObserversRunLoopOrdering = 400000;
 67  
     
 68  
         private static EODelayedObserverQueue 
 69  0
                 defaultObserverQueue = null;
 70  
         
 71  0
     private static NSSelector runLaterSelector = 
 72  0
         new NSSelector( "flushObserverQueue",
 73  0
             new Class[] { Object.class } );
 74  
 
 75  
         private boolean willRunLater;
 76  
          private LinkedList priorityQueue;
 77  
  
 78  
     /**
 79  
     * Default constructor.
 80  
     */
 81  0
     public EODelayedObserverQueue ()
 82  0
     {
 83  0
                 willRunLater = false;
 84  0
         priorityQueue = new LinkedList();
 85  0
     }
 86  
 
 87  
     /**
 88  
     * Returns the system default observer queue.
 89  
     */
 90  
     public static EODelayedObserverQueue defaultObserverQueue ()
 91  
     {
 92  0
         if ( defaultObserverQueue == null )
 93  
                 {
 94  0
                         defaultObserverQueue = new EODelayedObserverQueue();        
 95  
                 }
 96  0
                 return defaultObserverQueue;
 97  
     }
 98  
 
 99  
     /**
 100  
     * Removes the specified observer from the queue.
 101  
     */
 102  
     public void dequeueObserver ( 
 103  
         EODelayedObserver anObserver )
 104  
     {
 105  
 //System.out.println( "dequeueObserver: " + anObserver );
 106  
                 //synchronized ( priorityQueue )
 107  
                 //{
 108  0
                         priorityQueue.remove( anObserver );
 109  
                 //}
 110  0
     }
 111  
 
 112  
     /**
 113  
     * Adds the specified observer to the queue.
 114  
     * An already enqueued observer will not be
 115  
         * added again.
 116  
         * If the observer's priority is 
 117  
         * ObserverPriorityImmediate, it will be 
 118  
         * notified immediately and not added to the
 119  
         * queue.  
 120  
         * Otherwise, the queue sets itself up to
 121  
         * call notifyObserversUpToPriority during the
 122  
     * run loop as specified by 
 123  
     * FlushDelayedObserversRunLoopOrdering.
 124  
     */
 125  
     public void enqueueObserver ( 
 126  
         EODelayedObserver anObserver )
 127  
     {
 128  
         // syntactic glue for Runnables
 129  0
         final EODelayedObserver observer = anObserver;
 130  
         
 131  0
                 if ( observer.priority() == 
 132  
                         EODelayedObserver.ObserverPriorityImmediate )
 133  
                 {
 134  
             // invoke immediately
 135  0
                         observer.subjectChanged();
 136  0
                 }
 137  
         else
 138  
                 {
 139  
             // place in the delayed observer queue
 140  
             
 141  
                         //synchronized ( priorityQueue )
 142  
                         //{
 143  0
                 int i = 0;
 144  0
                 int priority = observer.priority();
 145  
                 Object o;
 146  
 
 147  0
                 Iterator iterator = priorityQueue.iterator();
 148  
 
 149  
                 // scan entire list to ensure we're not already queued
 150  0
                 while ( iterator.hasNext() )
 151  
                 {
 152  0
                     o = iterator.next();
 153  0
                     if ( o == observer ) 
 154  
                     {
 155  
                         // already queued
 156  0
                         return;
 157  
                     }
 158  0
                     if ( ((EODelayedObserver)o).priority() > priority )
 159  
                     {
 160  
                         // insert at this index: break now
 161  0
                         break;
 162  
                     }
 163  0
                     i++;
 164  0
                 }
 165  
                 
 166  
                 // if we broke early, we found a threshhold:
 167  
                 // continue scanning to ensure we're not already queued
 168  0
                 while ( iterator.hasNext() )
 169  
                 {
 170  0
                     if ( iterator.next() == observer ) 
 171  
                     {
 172  
                         // already queued
 173  0
                         return;
 174  
                     }
 175  
                 }
 176  
                 
 177  
                 // insert before items of lower priority,
 178  
                 // otherwise insert at end of list.
 179  0
                 priorityQueue.add( i, observer );
 180  
                 
 181  
                         //}
 182  0
                         runLater();
 183  
                 }
 184  
 //System.out.println( "enqueueObserver: " + anObserver + " : " + priorityQueue );
 185  0
     }
 186  
 
 187  
     /**
 188  
     * Notifies all observers with priority equal to
 189  
     * or greater than the specified priority.
 190  
     */
 191  
     public void notifyObserversUpToPriority ( int priority )
 192  
     {
 193  
 //System.out.println( "notifyObserversUpToPriority: priorityQueue size = " + priorityQueue.size() );
 194  
                 EODelayedObserver o;
 195  0
                 while ( ! priorityQueue.isEmpty() )
 196  
                 {
 197  0
                         o = (EODelayedObserver) priorityQueue.getFirst();
 198  0
                         if ( o.priority() > priority ) break;
 199  0
             priorityQueue.removeFirst();
 200  
             
 201  
             try
 202  
             {
 203  0
                             o.subjectChanged();
 204  
             }
 205  0
             catch ( Exception exc )
 206  
             {
 207  0
                 System.out.println( "Error notifying observer: " + o );
 208  0
                 exc.printStackTrace();
 209  0
             }
 210  0
                 }
 211  0
         }
 212  
         
 213  
         /**
 214  
         * Called to ensure that notifyObserversUpToPriority
 215  
         * will be called on the next event loop.
 216  
         */
 217  
         private void runLater()
 218  
         {
 219  0
                 if ( ! willRunLater )
 220  
                 {
 221  0
                         willRunLater = true;
 222  0
             NSRunLoop.currentRunLoop().performSelectorWithOrder(
 223  0
                 runLaterSelector, this, null, FlushDelayedObserversRunLoopOrdering, null );
 224  
                 }
 225  0
         }
 226  
         
 227  
         /**
 228  
         * This method is called by the event queue run loop
 229  
         * and calls notifyObserversUpToPriority with 
 230  
     * ObserverPriorityLater.
 231  
         * NOTE: This method is not part of the specification.
 232  
         */
 233  
         public void flushObserverQueue( Object anObject )
 234  
         {
 235  
 //System.out.println( "EODelayedObserverQueue: running" );                
 236  0
             notifyObserversUpToPriority( EODelayedObserver.ObserverPrioritySixth );
 237  0
         if ( ! priorityQueue.isEmpty() )
 238  
         {
 239  
             // assumes all remaining on queue are ObserverPriorityLater
 240  0
             NSRunLoop.invokeLater(
 241  0
                 new PriorityLaterRunnable( new LinkedList( priorityQueue ) ) );
 242  0
             priorityQueue.clear();
 243  
         }
 244  0
                 willRunLater = false;
 245  0
         }
 246  
     
 247  
     /**
 248  
     * A runnable for dispatching remaining observers running at ObserverPriorityLater.
 249  
     */
 250  
     class PriorityLaterRunnable implements Runnable
 251  
     {
 252  
         List observers;
 253  
         
 254  0
         public PriorityLaterRunnable( List anObserverList )
 255  0
         {
 256  0
             observers = anObserverList;
 257  0
         }
 258  
         
 259  
         public void run()
 260  
         {
 261  0
             EODelayedObserver o = null;
 262  0
             Iterator i = observers.iterator();
 263  0
             while ( i.hasNext() )
 264  
             {
 265  
                 try
 266  
                 {
 267  0
                     o = (EODelayedObserver) i.next();
 268  0
                     o.subjectChanged();
 269  
                 }
 270  0
                 catch ( Exception exc )
 271  
                 {
 272  0
                     System.out.println( "Error notifying observer: " + o );
 273  0
                     exc.printStackTrace();
 274  0
                 }
 275  0
             }
 276  0
         }
 277  
     }
 278  
         
 279  
 }    
 280  
 
 281  
 /*
 282  
  * $Log$
 283  
  * Revision 1.2  2006/02/16 16:47:14  cgruber
 284  
  * Move some classes in to "internal" packages and re-work imports, etc.
 285  
  *
 286  
  * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
 287  
  *
 288  
  * Revision 1.1  2006/02/16 13:19:57  cgruber
 289  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 290  
  *
 291  
  * Revision 1.8  2003/08/19 01:53:12  chochos
 292  
  * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now.
 293  
  *
 294  
  * Revision 1.7  2002/05/20 15:08:35  mpowers
 295  
  * Optimization for enqueueObserver: we were scanning the entire list anyway;
 296  
  * now we compare priorities and ensure we're not double-queued on same pass.
 297  
  *
 298  
  * Revision 1.6  2002/05/15 13:45:57  mpowers
 299  
  * RunLater now appropriately runs later: at the end of the current awt queue.
 300  
  *
 301  
  * Revision 1.5  2002/03/11 03:18:39  mpowers
 302  
  * Now properly handling ObserverChangesLater.
 303  
  *
 304  
  * Revision 1.4  2001/10/26 18:37:15  mpowers
 305  
  * Now using NSRunLoop instead of AWT EventQueue.
 306  
  *
 307  
  * Revision 1.3  2001/10/22 21:54:16  mpowers
 308  
  * Removed swing dependency in favor of jdk1.3 event queue.
 309  
  * Optimized priority queue population.
 310  
  *
 311  
  * Revision 1.2  2001/10/12 18:01:59  mpowers
 312  
  * Now catching exceptions before they disrupt the awt event queue.
 313  
  *
 314  
  * Revision 1.1.1.1  2000/12/21 15:46:42  mpowers
 315  
  * Contributing wotonomy.
 316  
  *
 317  
  * Revision 1.5  2000/12/20 16:25:35  michael
 318  
  * Added log to all files.
 319  
  *
 320  
  *
 321  
  */
 322  
     
 323