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.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
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
190 if ( name.equals( notification.name() ) )
191 {
192 flag += NotificationCoalescingOnName;
193 }
194 }
195 }
196
197 if ( aCoalesceMask >= 2 )
198 {
199
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
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
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
307
308 while ( it.hasNext() )
309 {
310 parent.center.postNotification(
311 (NSNotification) it.next() );
312 }
313 }
314 }
315
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345