API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPNotificationCenter.j
Go to the documentation of this file.
1 /*
2  * CPNotificationCenter.j
3  * Foundation
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
25 
39 @implementation CPNotificationCenter : CPObject
40 {
41  CPMutableDictionary _namedRegistries;
42  _CPNotificationRegistry _unnamedRegistry;
43 }
44 
48 + (CPNotificationCenter)defaultCenter
49 {
52 
54 }
55 
56 - (id)init
57 {
58  self = [super init];
59 
60  if (self)
61  {
62  _namedRegistries = @{};
63  _unnamedRegistry = [[_CPNotificationRegistry alloc] init];
64  }
65  return self;
66 }
67 
76 - (void)addObserver:(id)anObserver selector:(SEL)aSelector name:(CPString)aNotificationName object:(id)anObject
77 {
78  var registry,
79  observer = [[_CPNotificationObserver alloc] initWithObserver:anObserver selector:aSelector];
80 
81  if (aNotificationName == nil)
82  registry = _unnamedRegistry;
83  else if (!(registry = [_namedRegistries objectForKey:aNotificationName]))
84  {
85  registry = [[_CPNotificationRegistry alloc] init];
86  [_namedRegistries setObject:registry forKey:aNotificationName];
87  }
88 
89  [registry addObserver:observer object:anObject];
90 }
91 
96 - (void)removeObserver:(id)anObserver
97 {
98  var name = nil,
99  names = [_namedRegistries keyEnumerator];
100 
101  while ((name = [names nextObject]) !== nil)
102  [[_namedRegistries objectForKey:name] removeObserver:anObserver object:nil];
103 
104  [_unnamedRegistry removeObserver:anObserver object:nil];
105 }
106 
113 - (void)removeObserver:(id)anObserver name:(CPString)aNotificationName object:(id)anObject
114 {
115  if (aNotificationName == nil)
116  {
117  var name = nil,
118  names = [_namedRegistries keyEnumerator];
119 
120  while ((name = [names nextObject]) !== nil)
121  [[_namedRegistries objectForKey:name] removeObserver:anObserver object:anObject];
122 
123  [_unnamedRegistry removeObserver:anObserver object:anObject];
124  }
125  else
126  [[_namedRegistries objectForKey:aNotificationName] removeObserver:anObserver object:anObject];
127 }
128 
134 - (void)postNotification:(CPNotification)aNotification
135 {
136  if (!aNotification)
137  [CPException raise:CPInvalidArgumentException reason:"postNotification: does not except 'nil' notifications"];
138 
139  _CPNotificationCenterPostNotification(self, aNotification);
140 }
141 
148 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject userInfo:(CPDictionary)aUserInfo
149 {
150  _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:aUserInfo]);
151 }
152 
158 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject
159 {
160  _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:nil]);
161 }
162 
163 @end
164 
165 var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ self, /* CPNotification */ aNotification)
166 {
167  [self._unnamedRegistry postNotification:aNotification];
168  [[self._namedRegistries objectForKey:[aNotification name]] postNotification:aNotification];
169 };
170 
171 /*
172  Mapping of Notification Name to listening object/selector.
173  @ignore
174  */
175 @implementation _CPNotificationRegistry : CPObject
176 {
177  CPDictionary _objectObservers;
178 }
179 
180 - (id)init
181 {
182  self = [super init];
183 
184  if (self)
185  {
186  _objectObservers = @{};
187  }
188 
189  return self;
190 }
191 
192 - (void)addObserver:(_CPNotificationObserver)anObserver object:(id)anObject
193 {
194  // If there's no object, then we're listening to this
195  // notification regardless of whom sends it.
196  if (!anObject)
197  anObject = [CPNull null];
198 
199  // Grab all the listeners for this notification/object pair
200  var observers = [_objectObservers objectForKey:[anObject UID]];
201 
202  if (!observers)
203  {
204  observers = [CPMutableSet set];
205  [_objectObservers setObject:observers forKey:[anObject UID]];
206  }
207 
208  // Add this observer.
209  [observers addObject:anObserver];
210 }
211 
212 - (void)removeObserver:(id)anObserver object:(id)anObject
213 {
214  var removedKeys = [];
215 
216  // This means we're getting rid of EVERY instance of this observer.
217  if (anObject == nil)
218  {
219  var key = nil,
220  keys = [_objectObservers keyEnumerator];
221 
222  // Iterate through every set of observers
223  while ((key = [keys nextObject]) !== nil)
224  {
225  var observers = [_objectObservers objectForKey:key],
226  observer = nil,
227  observersEnumerator = [observers objectEnumerator];
228 
229  while ((observer = [observersEnumerator nextObject]) !== nil)
230  if ([observer observer] == anObserver)
231  [observers removeObject:observer];
232 
233  if (![observers count])
234  removedKeys.push(key);
235  }
236  }
237  else
238  {
239  var key = [anObject UID],
240  observers = [_objectObservers objectForKey:key],
241  observer = nil,
242  observersEnumerator = [observers objectEnumerator];
243 
244  while ((observer = [observersEnumerator nextObject]) !== nil)
245  if ([observer observer] == anObserver)
246  [observers removeObject:observer];
247 
248  if (![observers count])
249  removedKeys.push(key);
250  }
251 
252  var count = removedKeys.length;
253 
254  while (count--)
255  [_objectObservers removeObjectForKey:removedKeys[count]];
256 }
257 
258 - (void)postNotification:(CPNotification)aNotification
259 {
260  // We don't want to erroneously send notifications to observers that get removed
261  // during the posting of this notification, nor observers that get added. The
262  // best way to do this is to make a copy of the current observers (this avoids
263  // new observers from being notified) and double checking every observer against
264  // the current set (this avoids removed observers from receiving notifications).
265  var object = [aNotification object],
266  currentObservers = nil;
267 
268  if (object != nil && (currentObservers = [_objectObservers objectForKey:[object UID]]))
269  {
270  var observers = [currentObservers copy],
271  observer = nil,
272  observersEnumerator = [observers objectEnumerator];
273 
274  while ((observer = [observersEnumerator nextObject]) !== nil)
275  {
276  // CPSet containsObject is N(1) so this is a fast check.
277  if ([currentObservers containsObject:observer])
278  [observer postNotification:aNotification];
279  }
280  }
281 
282  // Now do the same for the nil object observers...
283  currentObservers = [_objectObservers objectForKey:[[CPNull null] UID]];
284 
285  if (!currentObservers)
286  return;
287 
288  var observers = [currentObservers copy],
289  observersEnumerator = [observers objectEnumerator];
290 
291  while ((observer = [observersEnumerator nextObject]) !== nil)
292  {
293  // CPSet containsObject is N(1) so this is a fast check.
294  if ([currentObservers containsObject:observer])
295  [observer postNotification:aNotification];
296  }
297 }
298 
299 - (unsigned)count
300 {
301  return [_objectObservers count];
302 }
303 
304 @end
305 
306 /* @ignore */
307 @implementation _CPNotificationObserver : CPObject
308 {
309  id _observer;
310  SEL _selector;
311 }
312 
313 - (id)initWithObserver:(id)anObserver selector:(SEL)aSelector
314 {
315  if (self)
316  {
317  _observer = anObserver;
318  _selector = aSelector;
319  }
320 
321  return self;
322 }
323 
324 - (id)observer
325 {
326  return _observer;
327 }
328 
329 - (void)postNotification:(CPNotification)aNotification
330 {
331  [_observer performSelector:_selector withObject:aNotification];
332 }
333 
334 @end