/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 Intersect Software Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.foundation; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * NSNotificationQueue coalesces notifications to be posted to the * NSNotificationCenter and can post them asynchronously. While calling * postNotification on the notification center does not return until all * receivers have been notified, calling enqueueNotification can return * immediately. Use this class when you want to coalesce notifications or notify * asynchronously, or both, which is the typical case. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 893 $ */ public class NSNotificationQueue { private static NSNotificationQueue defaultQueue = null; /** * Posting style specifying that the notification should be posted on the next * available event loop. */ public static final int PostASAP = 4; /** * Posting style specifying that the notification should be posted on the next * available idle loop. */ public static final int PostWhenIdle = 8; /** * Posting style specifying that the notification should be posted immediately. * The enqueue method will not return until all receivers have been notified. */ public static final int PostNow = 16; /** * Used to indicate that this notification should not be coalesced with other * notifications. Ignored if combined with other coalesce flags. */ public static final int NotificationNoCoalescing = 0; /** * Used to indicate that this notification should be coalesced with other * notifications with the same name. May be combined with * NotificationCoalescingOnSender. */ public static final int NotificationCoalescingOnName = 1; /** * Used to indicate that this notification should be coalesced with other * notifications with the same object argument (which is typically the sender). * May be combined with NotificationCoalescingOnName. */ public static final int NotificationCoalescingOnSender = 2; /** * The ASAP queue, which should probably use a LinkedList. */ private List queue; /** * The idle queue, which should probably use a LinkedList. */ private List idleQueue; /** * The notification center we will be using. */ private NSNotificationCenter center; /** * Our private ASAP notifier. */ private Notifier notifier; /** * Our private idle notifier. */ private Notifier idleNotifier; /** * Default constructor creates a new notification queue that uses the default * notification center. */ public NSNotificationQueue() { this(NSNotificationCenter.defaultCenter()); } /** * Creates a new notification queue that uses the specified notification center. */ public NSNotificationQueue(NSNotificationCenter aCenter) { queue = new LinkedList(); idleQueue = new LinkedList(); center = aCenter; notifier = new Notifier(this, queue); idleNotifier = new Notifier(this, idleQueue); } /** * Returns the system default queue, creating one if it has not yet been * created. The system default queue uses the system default notification * center. */ static public NSNotificationQueue defaultQueue() { if (defaultQueue == null) { defaultQueue = new NSNotificationQueue(); } return defaultQueue; } /** * Removes notifications from the queue that match the specified notification, * considering the specified coalesce mask. */ public void dequeueMatchingNotifications(NSNotification aNotification, int aCoalesceMask) { if (aCoalesceMask == NotificationNoCoalescing) return; dequeueFromQueue(aNotification, aCoalesceMask, queue); dequeueFromQueue(aNotification, aCoalesceMask, idleQueue); } private void dequeueFromQueue(NSNotification aNotification, int aCoalesceMask, List aQueue) { synchronized (aQueue) { int flag; NSNotification notification; Object name = aNotification.name(); Object object = aNotification.object(); Iterator it = aQueue.iterator(); while (it.hasNext()) { flag = 0; notification = (NSNotification) it.next(); // if NotificationCoalescingOnName if ((aCoalesceMask == 1) || (aCoalesceMask == 3)) { if (name == null) { if (notification.name() != null) { flag += NotificationCoalescingOnName; } } else { // compare by value if (name.equals(notification.name())) { flag += NotificationCoalescingOnName; } } } // if NotificationCoalescingOnSender if (aCoalesceMask >= 2) { // compare by reference if (object == notification.object()) { flag += NotificationCoalescingOnSender; } } if (flag == aCoalesceMask) { it.remove(); } } } } /** * Adds the notification to the queue to be run at the time specified by the * posting style. The notification will be coalesced with other notifications * that match the same name and object (sender) argument. */ public void enqueueNotification(NSNotification aNotification, int aPostingStyle) { enqueueNotificationWithCoalesceMaskForModes(aNotification, aPostingStyle, NotificationCoalescingOnName + NotificationCoalescingOnSender, null); } /** * Adds the notification to the queue to be run at the time specified by the * posting style and coelesced as the specified mask indicates. aModeList is * currently ignored and may be null. */ public void enqueueNotificationWithCoalesceMaskForModes(NSNotification aNotification, int aPostingStyle, int aCoalesceMask, List aModeList) { dequeueMatchingNotifications(aNotification, aCoalesceMask); if (aPostingStyle == PostNow) { center.postNotification(aNotification); return; } if (aPostingStyle == PostASAP) { synchronized (queue) { queue.add(aNotification); if (!notifier.willRun) { // asap runs at the very first run loop ordering, plus one just in case NSRunLoop.invokeLaterWithOrder(notifier, 1); notifier.willRun = true; } } return; } if (aPostingStyle == PostWhenIdle) { synchronized (idleQueue) { idleQueue.add(aNotification); if (!idleNotifier.willRun) { // when idle runs at the very last run loop ordering, minus one just in case NSRunLoop.invokeLaterWithOrder(idleNotifier, Integer.MAX_VALUE - 1); idleNotifier.willRun = true; } } return; } } private class Notifier implements Runnable { public boolean willRun; NSNotificationQueue parent; List queue; public Notifier(NSNotificationQueue aParent, List aQueue) { willRun = false; parent = aParent; queue = aQueue; } public void run() { Iterator it = new LinkedList(queue).iterator(); synchronized (queue) { queue.clear(); } willRun = false; // queue must already be cleared and willRun reset // because this loop might queue more notifications while (it.hasNext()) { parent.center.postNotification((NSNotification) it.next()); } } } } /* * $Log$ Revision 1.2 2006/02/16 13:15:00 cgruber Check in all sources in * eclipse-friendly maven-enabled packages. * * Revision 1.6 2003/08/11 18:18:08 chochos improved encoding of strings, * removed warnings * * Revision 1.5 2003/08/06 23:07:52 chochos general code cleanup (mostly, * removing unused imports) * * Revision 1.4 2001/11/01 15:49:26 mpowers With NSRunLoop, we can now correctly * implement PostASAP and PostWhenIdle. * * Revision 1.3 2001/06/25 14:47:24 mpowers Fixed serious error where some * notifications were not being posted at all. A simple change to Notifier class * fixed the problem. Thanks to glista. * * Revision 1.2 2001/02/26 15:53:22 mpowers Fine-tuning notification firing. * Child display groups now update properly after parent save or invalidate. * * Revision 1.1 2001/02/24 17:03:22 mpowers Implemented the notification queue, * and changed editing context to use it. * * */