View Javadoc

1   /*
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.util.Iterator;
22  import java.util.LinkedList;
23  import java.util.List;
24  
25  /***
26  * NSNotificationQueue coalesces notifications to be
27  * posted to the NSNotificationCenter and can post them
28  * asynchronously.  While calling postNotification on
29  * the notification center does not return until all 
30  * receivers have been notified, calling enqueueNotification
31  * can return immediately.  Use this class when you want
32  * to coalesce notifications or notify asynchronously, or
33  * both, which is the typical case.
34  *
35  * @author michael@mpowers.net
36  * @author $Author: cgruber $
37  * @version $Revision: 893 $
38  */
39  public class NSNotificationQueue
40  {
41      private static NSNotificationQueue defaultQueue = null;
42      
43      /***
44      * Posting style specifying that the notification should
45      * be posted on the next available event loop.
46      */
47      public static final int PostASAP = 4;
48  
49      /***
50      * Posting style specifying that the notification should
51      * be posted on the next available idle loop.
52      */
53      public static final int PostWhenIdle = 8;
54  
55      /***
56      * Posting style specifying that the notification should
57      * be posted immediately.  The enqueue method will not
58      * return until all receivers have been notified.
59      */
60      public static final int PostNow = 16;
61      
62      /***
63      * Used to indicate that this notification should not
64      * be coalesced with other notifications. 
65      * Ignored if combined with other coalesce flags.
66      */
67      public static final int NotificationNoCoalescing = 0;
68  
69      /***
70      * Used to indicate that this notification should 
71      * be coalesced with other notifications with the
72      * same name.  
73      * May be combined with NotificationCoalescingOnSender.
74      */
75      public static final int NotificationCoalescingOnName = 1;
76  
77      /***
78      * Used to indicate that this notification should 
79      * be coalesced with other notifications with the
80      * same object argument (which is typically the sender).
81      * May be combined with NotificationCoalescingOnName.
82      */
83      public static final int NotificationCoalescingOnSender = 2;
84      
85      /***
86      * The ASAP queue, which should probably use a LinkedList.
87      */
88      private List queue;
89      
90      /***
91      * The idle queue, which should probably use a LinkedList.
92      */
93      private List idleQueue;
94      
95      /***
96      * The notification center we will be using.
97      */
98      private NSNotificationCenter center;
99      
100     /***
101     * Our private ASAP notifier.
102     */
103     private Notifier notifier;
104     
105     /***
106     * Our private idle notifier.
107     */
108     private Notifier idleNotifier;
109     
110     /***
111     * Default constructor creates a new notification queue
112     * that uses the default notification center.
113     */
114     public NSNotificationQueue()
115     {
116         this( NSNotificationCenter.defaultCenter() );
117     }
118     
119     /***
120     * Creates a new notification queue that uses the 
121     * specified notification center.
122     */
123     public NSNotificationQueue( 
124         NSNotificationCenter aCenter )
125     {
126         queue = new LinkedList();
127         idleQueue = new LinkedList();
128         center = aCenter;
129         notifier = new Notifier( this, queue );
130         idleNotifier = new Notifier( this, idleQueue );
131     }
132     
133     /***
134     * Returns the system default queue, creating one
135     * if it has not yet been created.  The system default
136     * queue uses the system default notification center.
137     */
138     static public NSNotificationQueue defaultQueue()
139     {
140         if ( defaultQueue == null )
141         {
142             defaultQueue = new NSNotificationQueue();   
143         }
144         return defaultQueue;
145     }
146 	
147     /***
148     * Removes notifications from the queue that match
149     * the specified notification, considering the 
150     * specified coalesce mask.
151     */ 
152     public void dequeueMatchingNotifications( 
153         NSNotification aNotification, 
154         int aCoalesceMask)
155     {
156         if ( aCoalesceMask == NotificationNoCoalescing ) return;
157         dequeueFromQueue( aNotification, aCoalesceMask, queue );
158         dequeueFromQueue( aNotification, aCoalesceMask, idleQueue );
159     }
160     
161     private void dequeueFromQueue( 
162         NSNotification aNotification, 
163         int aCoalesceMask, 
164         List aQueue )
165     {
166         synchronized ( aQueue )
167         {
168             int flag;
169             NSNotification notification;
170             Object name = aNotification.name();
171             Object object = aNotification.object();
172             Iterator it = aQueue.iterator();
173             while ( it.hasNext() )
174             {
175                 flag = 0;
176                 notification = (NSNotification) it.next();
177                 // if NotificationCoalescingOnName
178                 if ( ( aCoalesceMask == 1 ) || ( aCoalesceMask == 3 ) )
179                 {
180                     if ( name == null )
181                     {
182                         if ( notification.name() != null )
183                         {
184                             flag += NotificationCoalescingOnName;
185                         }
186                     }
187                     else
188                     {
189                         // compare by value
190                         if ( name.equals( notification.name() ) )
191                         {
192                             flag += NotificationCoalescingOnName;
193                         }
194                     }
195                 }
196                 // if NotificationCoalescingOnSender
197                 if ( aCoalesceMask >= 2 )
198                 {
199                     // compare by reference
200                     if ( object == notification.object() )
201                     {
202                         flag += NotificationCoalescingOnSender;
203                     }
204                 }
205                 
206                 if ( flag == aCoalesceMask )
207                 {
208                     it.remove();   
209                 }
210             }
211         }
212     }
213 
214     /***
215     * Adds the notification to the queue to be run at
216     * the time specified by the posting style.  The
217     * notification will be coalesced with other notifications
218     * that match the same name and object (sender) argument.
219     */ 
220     public void enqueueNotification( 
221         NSNotification aNotification, 
222         int aPostingStyle)
223     {
224         enqueueNotificationWithCoalesceMaskForModes(
225             aNotification, aPostingStyle,
226             NotificationCoalescingOnName + NotificationCoalescingOnSender,
227             null );
228     }
229 
230     /***
231     * Adds the notification to the queue to be run at
232     * the time specified by the posting style and coelesced
233     * as the specified mask indicates.
234     * aModeList is currently ignored and may be null.
235     */ 
236     public void enqueueNotificationWithCoalesceMaskForModes( 
237         NSNotification aNotification, 
238         int aPostingStyle, 
239         int aCoalesceMask, 
240         List aModeList)
241     {
242         dequeueMatchingNotifications( aNotification, aCoalesceMask );
243         
244         if ( aPostingStyle == PostNow )
245         {
246             center.postNotification( aNotification );
247             return;
248         }
249         
250         if ( aPostingStyle == PostASAP )
251         {
252             synchronized ( queue )
253             {
254                 queue.add( aNotification );
255                 if ( ! notifier.willRun )
256                 {
257                     // asap runs at the very first run loop ordering, plus one just in case
258                     NSRunLoop.invokeLaterWithOrder( notifier, 1 );
259                     notifier.willRun = true;
260                 }
261             }
262             return;
263         }
264         
265         if ( aPostingStyle == PostWhenIdle )
266         {
267             synchronized ( idleQueue )
268             {
269                 idleQueue.add( aNotification );
270                 if ( ! idleNotifier.willRun )
271                 {
272                     // when idle runs at the very last run loop ordering, minus one just in case
273                     NSRunLoop.invokeLaterWithOrder( idleNotifier, Integer.MAX_VALUE - 1 );
274                     idleNotifier.willRun = true;
275                 }
276             }
277             return;
278         }
279         
280     }
281     
282     private class Notifier implements Runnable
283     {
284         public boolean willRun;
285         
286         NSNotificationQueue parent;
287         List queue;
288         
289         public Notifier(
290             NSNotificationQueue aParent, List aQueue )
291         {
292             willRun = false;
293             parent = aParent;
294             queue = aQueue;
295         }
296         
297         public void run()
298         {
299             Iterator it = new LinkedList( queue ).iterator();
300             synchronized ( queue )
301             {
302                 queue.clear();
303             }
304             willRun = false;
305 
306             // queue must already be cleared and willRun reset
307             //   because this loop might queue more notifications                
308             while ( it.hasNext() )
309             {
310                 parent.center.postNotification( 
311                     (NSNotification) it.next() );
312             }
313         }
314     }
315 
316 }
317 
318 /*
319  * $Log$
320  * Revision 1.2  2006/02/16 13:15:00  cgruber
321  * Check in all sources in eclipse-friendly maven-enabled packages.
322  *
323  * Revision 1.6  2003/08/11 18:18:08  chochos
324  * improved encoding of strings, removed warnings
325  *
326  * Revision 1.5  2003/08/06 23:07:52  chochos
327  * general code cleanup (mostly, removing unused imports)
328  *
329  * Revision 1.4  2001/11/01 15:49:26  mpowers
330  * With NSRunLoop, we can now correctly implement PostASAP and PostWhenIdle.
331  *
332  * Revision 1.3  2001/06/25 14:47:24  mpowers
333  * Fixed serious error where some notifications were not being posted at all.
334  * A simple change to Notifier class fixed the problem.  Thanks to glista.
335  *
336  * Revision 1.2  2001/02/26 15:53:22  mpowers
337  * Fine-tuning notification firing.
338  * Child display groups now update properly after parent save or invalidate.
339  *
340  * Revision 1.1  2001/02/24 17:03:22  mpowers
341  * Implemented the notification queue, and changed editing context to use it.
342  *
343  *
344  */
345