API 0.9.5
Foundation/CPNotificationCenter.j
Go to the documentation of this file.
00001 /*
00002  * CPNotificationCenter.j
00003  * Foundation
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 var CPNotificationDefaultCenter = nil;
00025 
00039 @implementation CPNotificationCenter : CPObject
00040 {
00041     CPMutableDictionary     _namedRegistries;
00042     _CPNotificationRegistry _unnamedRegistry;
00043 }
00044 
00048 + (CPNotificationCenter)defaultCenter
00049 {
00050     if (!CPNotificationDefaultCenter)
00051         CPNotificationDefaultCenter = [[CPNotificationCenter alloc] init];
00052 
00053     return CPNotificationDefaultCenter;
00054 }
00055 
00056 - (id)init
00057 {
00058     self = [super init];
00059 
00060     if (self)
00061     {
00062         _namedRegistries = [CPDictionary dictionary];
00063         _unnamedRegistry = [[_CPNotificationRegistry alloc] init];
00064     }
00065    return self;
00066 }
00067 
00076 - (void)addObserver:(id)anObserver selector:(SEL)aSelector name:(CPString)aNotificationName object:(id)anObject
00077 {
00078     var registry,
00079         observer = [[_CPNotificationObserver alloc] initWithObserver:anObserver selector:aSelector];
00080 
00081     if (aNotificationName == nil)
00082         registry = _unnamedRegistry;
00083 
00084     else if (!(registry = [_namedRegistries objectForKey:aNotificationName]))
00085     {
00086         registry = [[_CPNotificationRegistry alloc] init];
00087         [_namedRegistries setObject:registry forKey:aNotificationName];
00088     }
00089 
00090     [registry addObserver:observer object:anObject];
00091 }
00092 
00097 - (void)removeObserver:(id)anObserver
00098 {
00099     var name = nil,
00100         names = [_namedRegistries keyEnumerator];
00101 
00102     while (name = [names nextObject])
00103         [[_namedRegistries objectForKey:name] removeObserver:anObserver object:nil];
00104 
00105     [_unnamedRegistry removeObserver:anObserver object:nil];
00106 }
00107 
00114 - (void)removeObserver:(id)anObserver name:(CPString)aNotificationName object:(id)anObject
00115 {
00116     if (aNotificationName == nil)
00117     {
00118         var name = nil,
00119             names = [_namedRegistries keyEnumerator];
00120 
00121         while (name = [names nextObject])
00122             [[_namedRegistries objectForKey:name] removeObserver:anObserver object:anObject];
00123 
00124         [_unnamedRegistry removeObserver:anObserver object:anObject];
00125     }
00126     else
00127         [[_namedRegistries objectForKey:aNotificationName] removeObserver:anObserver object:anObject];
00128 }
00129 
00135 - (void)postNotification:(CPNotification)aNotification
00136 {
00137     if (!aNotification)
00138         [CPException raise:CPInvalidArgumentException reason:"postNotification: does not except 'nil' notifications"];
00139 
00140     _CPNotificationCenterPostNotification(self, aNotification);
00141 }
00142 
00149 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject userInfo:(CPDictionary)aUserInfo
00150 {
00151    _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:aUserInfo]);
00152 }
00153 
00159 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject
00160 {
00161    _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:nil]);
00162 }
00163 
00164 @end
00165 
00166 var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ self, /* CPNotification */ aNotification)
00167 {
00168     [self._unnamedRegistry postNotification:aNotification];
00169     [[self._namedRegistries objectForKey:[aNotification name]] postNotification:aNotification];
00170 }
00171 
00172 /*
00173     Mapping of Notification Name to listening object/selector.
00174     @ignore
00175  */
00176 @implementation _CPNotificationRegistry : CPObject
00177 {
00178     CPDictionary    _objectObservers;
00179     BOOL            _observerRemovalCount;
00180 }
00181 
00182 - (id)init
00183 {
00184     self = [super init];
00185 
00186     if (self)
00187     {
00188         _observerRemovalCount = 0;
00189         _objectObservers = [CPDictionary dictionary];
00190     }
00191 
00192     return self;
00193 }
00194 
00195 - (void)addObserver:(_CPNotificationObserver)anObserver object:(id)anObject
00196 {
00197     // If there's no object, then we're listening to this
00198     // notification regardless of whom sends it.
00199     if (!anObject)
00200         anObject = [CPNull null];
00201 
00202     // Grab all the listeners for this notification/object pair
00203     var observers = [_objectObservers objectForKey:[anObject UID]];
00204 
00205     if (!observers)
00206     {
00207         observers = [];
00208         [_objectObservers setObject:observers forKey:[anObject UID]];
00209     }
00210 
00211     // Add this observer.
00212     observers.push(anObserver);
00213 }
00214 
00215 - (void)removeObserver:(id)anObserver object:(id)anObject
00216 {
00217     var removedKeys = [];
00218 
00219     // This means we're getting rid of EVERY instance of this observer.
00220     if (anObject == nil)
00221     {
00222         var key = nil,
00223             keys = [_objectObservers keyEnumerator];
00224 
00225         // Iterate through every set of observers
00226         while (key = [keys nextObject])
00227         {
00228             var observers = [_objectObservers objectForKey:key],
00229                 count = observers ? observers.length : 0;
00230 
00231             while (count--)
00232                 if ([observers[count] observer] == anObserver)
00233                 {
00234                     ++_observerRemovalCount;
00235                     observers.splice(count, 1);
00236                 }
00237 
00238             if (!observers || observers.length == 0)
00239                 removedKeys.push(key);
00240         }
00241     }
00242     else
00243     {
00244         var key = [anObject UID],
00245             observers = [_objectObservers objectForKey:key],
00246             count = observers ? observers.length : 0;
00247 
00248         while (count--)
00249             if ([observers[count] observer] == anObserver)
00250             {
00251                 ++_observerRemovalCount;
00252                 observers.splice(count, 1)
00253             }
00254 
00255         if (!observers || observers.length == 0)
00256             removedKeys.push(key);
00257     }
00258 
00259     var count = removedKeys.length;
00260 
00261     while (count--)
00262         [_objectObservers removeObjectForKey:removedKeys[count]];
00263 }
00264 
00265 - (void)postNotification:(CPNotification)aNotification
00266 {
00267     // We don't want to erroneously send notifications to observers that get removed
00268     // during the posting of this notification, nor observers that get added.  The
00269     // best way to do this is to make a copy of the current observers (this avoids
00270     // new observers from being notified) and double checking every observer against
00271     // the current array (this avoids removed observers from receiving notifications).
00272     // However, this is a very expensive operation (O(N) => O(N^2)), so to avoid it,
00273     // we keep track of whether observers are added or removed, and only do our
00274     // rigorous testing in those cases.
00275     var observerRemovalCount = _observerRemovalCount,
00276         object = [aNotification object],
00277         observers = nil;
00278 
00279     if (object != nil && (observers = [[_objectObservers objectForKey:[object UID]] copy]))
00280     {
00281         var currentObservers = observers,
00282             count = observers.length;
00283 
00284         while (count--)
00285         {
00286             var observer = observers[count];
00287 
00288             // if there wasn't removal of an observer during this posting, or there
00289             // was but we are still in the observer list...
00290             if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound)
00291                 [observer postNotification:aNotification];
00292         }
00293     }
00294 
00295     // Now do the same for the nil object observers...
00296     observers = [[_objectObservers objectForKey:[[CPNull null] UID]] copy];
00297 
00298     if (!observers)
00299         return;
00300 
00301     var observerRemovalCount = _observerRemovalCount,
00302         count = observers.length,
00303         currentObservers = observers;
00304 
00305     while (count--)
00306     {
00307         var observer = observers[count];
00308 
00309         // if there wasn't removal of an observer during this posting, or there
00310         // was but we are still in the observer list...
00311         if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound)
00312             [observer postNotification:aNotification];
00313     }
00314 }
00315 
00316 - (unsigned)count
00317 {
00318     return [_objectObservers count];
00319 }
00320 
00321 @end
00322 
00323 /* @ignore */
00324 @implementation _CPNotificationObserver : CPObject
00325 {
00326     id  _observer;
00327     SEL _selector;
00328 }
00329 
00330 - (id)initWithObserver:(id)anObserver selector:(SEL)aSelector
00331 {
00332     if (self)
00333     {
00334         _observer = anObserver;
00335         _selector = aSelector;
00336     }
00337 
00338    return self;
00339 }
00340 
00341 - (id)observer
00342 {
00343     return _observer;
00344 }
00345 
00346 - (void)postNotification:(CPNotification)aNotification
00347 {
00348     [_observer performSelector:_selector withObject:aNotification];
00349 }
00350 
00351 @end
 All Classes Files Functions Variables Defines