View Javadoc

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  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      public static int FlushDelayedObserversRunLoopOrdering = 400000;
67      
68  	private static EODelayedObserverQueue 
69  		defaultObserverQueue = null;
70  	
71      private static NSSelector runLaterSelector = 
72          new NSSelector( "flushObserverQueue",
73              new Class[] { Object.class } );
74  
75  	private boolean willRunLater;
76   	private LinkedList priorityQueue;
77   
78      /***
79      * Default constructor.
80      */
81      public EODelayedObserverQueue ()
82      {
83  		willRunLater = false;
84          priorityQueue = new LinkedList();
85      }
86  
87      /***
88      * Returns the system default observer queue.
89      */
90      public static EODelayedObserverQueue defaultObserverQueue ()
91      {
92          if ( defaultObserverQueue == null )
93  		{
94  			defaultObserverQueue = new EODelayedObserverQueue();	
95  		}
96  		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 			priorityQueue.remove( anObserver );
109 		//}
110     }
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         final EODelayedObserver observer = anObserver;
130         
131 		if ( observer.priority() == 
132 			EODelayedObserver.ObserverPriorityImmediate )
133 		{
134             // invoke immediately
135 			observer.subjectChanged();
136 		}
137         else
138 		{
139             // place in the delayed observer queue
140             
141 			//synchronized ( priorityQueue )
142 			//{
143                 int i = 0;
144                 int priority = observer.priority();
145                 Object o;
146 
147                 Iterator iterator = priorityQueue.iterator();
148 
149                 // scan entire list to ensure we're not already queued
150                 while ( iterator.hasNext() )
151                 {
152                     o = iterator.next();
153                     if ( o == observer ) 
154                     {
155                         // already queued
156                         return;
157                     }
158                     if ( ((EODelayedObserver)o).priority() > priority )
159                     {
160                         // insert at this index: break now
161                         break;
162                     }
163                     i++;
164                 }
165                 
166                 // if we broke early, we found a threshhold:
167                 // continue scanning to ensure we're not already queued
168                 while ( iterator.hasNext() )
169                 {
170                     if ( iterator.next() == observer ) 
171                     {
172                         // already queued
173                         return;
174                     }
175                 }
176                 
177                 // insert before items of lower priority,
178                 // otherwise insert at end of list.
179                 priorityQueue.add( i, observer );
180                 
181 			//}
182 			runLater();
183 		}
184 //System.out.println( "enqueueObserver: " + anObserver + " : " + priorityQueue );
185     }
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 		while ( ! priorityQueue.isEmpty() )
196 		{
197 			o = (EODelayedObserver) priorityQueue.getFirst();
198 			if ( o.priority() > priority ) break;
199             priorityQueue.removeFirst();
200             
201             try
202             {
203 			    o.subjectChanged();
204             }
205             catch ( Exception exc )
206             {
207                 System.out.println( "Error notifying observer: " + o );
208                 exc.printStackTrace();
209             }
210 		}
211 	}
212 	
213 	/***
214 	* Called to ensure that notifyObserversUpToPriority
215 	* will be called on the next event loop.
216 	*/
217 	private void runLater()
218 	{
219 		if ( ! willRunLater )
220 		{
221 			willRunLater = true;
222             NSRunLoop.currentRunLoop().performSelectorWithOrder(
223                 runLaterSelector, this, null, FlushDelayedObserversRunLoopOrdering, null );
224 		}
225 	}
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     	notifyObserversUpToPriority( EODelayedObserver.ObserverPrioritySixth );
237         if ( ! priorityQueue.isEmpty() )
238         {
239             // assumes all remaining on queue are ObserverPriorityLater
240             NSRunLoop.invokeLater(
241                 new PriorityLaterRunnable( new LinkedList( priorityQueue ) ) );
242             priorityQueue.clear();
243         }
244 		willRunLater = false;
245 	}
246     
247     /***
248     * A runnable for dispatching remaining observers running at ObserverPriorityLater.
249     */
250     class PriorityLaterRunnable implements Runnable
251     {
252         List observers;
253         
254         public PriorityLaterRunnable( List anObserverList )
255         {
256             observers = anObserverList;
257         }
258         
259         public void run()
260         {
261             EODelayedObserver o = null;
262             Iterator i = observers.iterator();
263             while ( i.hasNext() )
264             {
265                 try
266                 {
267                     o = (EODelayedObserver) i.next();
268                     o.subjectChanged();
269                 }
270                 catch ( Exception exc )
271                 {
272                     System.out.println( "Error notifying observer: " + o );
273                     exc.printStackTrace();
274                 }
275             }
276         }
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