]> err.no Git - sope/blob - sope-core/EOControl/EOObserver.m
Drop apache 1 build-dependency
[sope] / sope-core / EOControl / EOObserver.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "EOObserver.h"
23 #include "common.h"
24
25 // THREAD, MT
26
27 typedef struct _EOObserverList {
28   struct _EOObserverList *next;
29   id<EOObserving>        observer;
30   void                   (*notify)(id, SEL, id);
31 } EOObserverList;
32
33 static void     mapValRetain(NSMapTable *self, const void *_value);
34 static void     mapValRelease(NSMapTable *self, void *_value);
35 static NSString *mapDescribe(NSMapTable *self, const void *_value);
36
37 const NSMapTableValueCallBacks EOObserverListMapValueCallBacks = {
38   (void (*)(NSMapTable *, const void *))mapValRetain,
39   (void (*)(NSMapTable *, void *))mapValRelease,
40   (NSString *(*)(NSMapTable *, const void *))mapDescribe
41 };
42
43 @implementation NSObject(EOObserver)
44
45 - (void)willChange {
46   static Class EOObserverCenterClass = Nil;
47   if (EOObserverCenterClass == Nil)
48     EOObserverCenterClass = [EOObserverCenter class];
49
50   [EOObserverCenterClass notifyObserversObjectWillChange:self];
51 }
52
53 @end /* NSObject(EOObserver) */
54
55 @implementation EOObserverCenter
56
57 static unsigned       observerNotificationSuppressCount = 0;
58 static EOObserverList *omniscientObservers = NULL;
59 static NSMapTable     *objectToObservers   = NULL;
60
61 + (void)initialize {
62   if (objectToObservers == NULL) {
63     objectToObservers = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
64                                          EOObserverListMapValueCallBacks,
65                                          256);
66   }
67 }
68
69 + (void)notifyObserversObjectWillChange:(id)_object {
70   static id lastObject = nil;
71   register EOObserverList *l;
72
73   /* check if notifications are suppressed */
74   if (observerNotificationSuppressCount > 0)
75     return;
76   
77   /* compress notifications for the same object */
78   if (_object == lastObject)
79     return;
80
81   /* notify usual observers */
82
83   for (l = NSMapGet(objectToObservers, _object); l != NULL; l = l->next) {
84     if (l->notify)
85       l->notify(l->observer, @selector(objectWillChange:), _object);
86     else
87       [l->observer objectWillChange:_object];
88   }
89   
90   /* notify omniscient observers */
91   
92   for (l = omniscientObservers; l != NULL; l = l->next) {
93     if (l->notify)
94       l->notify(l->observer, @selector(objectWillChange:), _object);
95     else
96       [l->observer objectWillChange:_object];
97   }
98 }
99
100 + (void)addObserver:(id<EOObserving>)_observer forObject:(id)_object {
101   register EOObserverList *l, *nl;
102   
103   if ((l = NSMapGet(objectToObservers, _object))) {
104     /* check whether the observer is already registered */
105     
106     for (nl = l; nl != NULL; nl = nl->next) {
107       if (nl->observer == _object)
108         return;
109     }
110   }
111
112 #if NeXT_RUNTIME
113   nl = malloc(sizeof(EOObserverList));
114 #else  
115   nl = objc_malloc(sizeof(EOObserverList));
116 #endif
117   nl->observer = [_observer retain];
118   nl->notify   = (void*)
119     [(id)_observer methodForSelector:@selector(objectWillChange:)];
120
121   if (l == NULL) {
122     /* this is the first observer defined */
123     nl->next = NULL;
124     NSMapInsert(objectToObservers, _object, nl);
125   }
126   else {
127     /*
128       insert at second position (so that we don't need to remove/add the new
129       entry in table or traverse the list to the end)
130     */
131     nl->next = l->next;
132     l->next = nl;
133   }
134 }
135
136 + (void)removeObserver:(id<EOObserving>)_observer forObject:(id)_object {
137   register EOObserverList *l, *ll, *first;
138
139   if ((first = NSMapGet(objectToObservers, _object)) == NULL)
140     /* no observers registered for object */
141     return;
142
143   l  = first;
144   ll = NULL;
145   while (l) {
146     if (l->observer == _observer) {
147       /* found matching list entry */
148       if (l != first) {
149         /* entry is not the first entry */
150         ll->next = l->next;
151         [l->observer release];
152 #if NeXT_RUNTIME
153         free(l);
154 #else    
155         objc_free(l);
156 #endif
157         break;
158       }
159       else if (l->next) {
160         /*
161           entry is the first entry, but there are more than one entries.
162           In this case we copy the second to the first and remove the second,
163           this way we save removing/inserting in the hash table.
164         */
165         [l->observer release];
166         ll = l->next;
167         l->observer = ll->observer;
168         l->notify   = ll->notify;
169         l->next     = ll->next;
170 #if NeXT_RUNTIME
171         free(ll);
172 #else    
173         objc_free(ll);
174 #endif
175         break;
176       }
177       else {
178         /* entry is the lone entry */
179         NSMapRemove(objectToObservers, _object);
180         [l->observer release];
181 #if NeXT_RUNTIME
182         free(l);
183 #else    
184         objc_free(l);
185 #endif
186         break;
187       }
188     }
189     
190     ll = l;
191     l = ll->next;
192   }
193 }
194
195 + (NSArray *)observersForObject:(id)_object {
196   EOObserverList *observers;
197   NSMutableArray *result;
198   
199   if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
200     return [NSArray array];
201   
202   result = [NSMutableArray arrayWithCapacity:16];
203   while ((observers)) {
204     if (observers->observer)
205       [result addObject:observers->observer];
206     observers = observers->next;
207   }
208   
209   return [[result copy] autorelease];
210 }
211
212 + (id)observerForObject:(id)_object ofClass:(Class)_targetClass {
213   register EOObserverList *observers;
214   
215   if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
216     return nil;
217
218   while ((observers)) {
219     if ([observers->observer class] == _targetClass)
220       return observers->observer;
221     observers = observers->next;
222   }
223   return nil;
224 }
225
226 + (void)addOmniscientObserver:(id<EOObserving>)_observer {
227   EOObserverList *l;
228   
229   /* first check whether we already added this observer to the list */
230   
231   for (l = omniscientObservers; l != NULL; l = l->next) {
232     if (l->observer == _observer)
233       return;
234   }
235
236 #if NeXT_RUNTIME
237   l = malloc(sizeof(EOObserverList));
238 #else  
239   l = objc_malloc(sizeof(EOObserverList));
240 #endif
241   l->next     = omniscientObservers;
242   l->observer = [_observer retain];
243   l->notify   = (void*)[(id)_observer methodForSelector:@selector(willChange:)];
244
245   omniscientObservers = l;
246 }
247 + (void)removeOmniscientObserver:(id<EOObserving>)_observer {
248   EOObserverList *l, *ll;
249   
250   /* first check whether we already added this observer to the list */
251   
252   for (l = omniscientObservers, ll = NULL; l != NULL; ) {
253     if (l->observer == _observer) {
254       /* matched */
255       if (ll == NULL)
256         omniscientObservers = l->next;
257       else
258         ll->next = l->next;
259
260       [l->observer release];
261       objc_free(l);
262       return;
263     }
264
265     ll = l;
266     l = ll->next;
267   }
268 }
269
270 /* suppressing notifications */
271
272 + (void)suppressObserverNotification {
273   observerNotificationSuppressCount++;
274 }
275 + (void)enableObserverNotification {
276   observerNotificationSuppressCount--;
277 }
278
279 + (unsigned)observerNotificationSuppressCount {
280   return observerNotificationSuppressCount;
281 }
282
283 @end /* EOObserverCenter */
284
285 @implementation EODelayedObserverQueue
286
287 static EODelayedObserverQueue *defaultQueue = nil;
288
289 + (EODelayedObserverQueue *)defaultObserverQueue {
290   if (defaultQueue == nil)
291     defaultQueue = [[EODelayedObserverQueue alloc] init];
292   return defaultQueue;
293 }
294
295 - (id)init {
296   [[NSNotificationCenter defaultCenter]
297                          addObserver:self selector:@selector(_notify:)
298                          name:@"EODelayedNotify" object:self];
299   return self;
300 }
301
302 - (void)dealloc {
303   [[NSNotificationCenter defaultCenter] removeObserver:self];
304   [self->runLoopModes release];
305   [super dealloc];
306 }
307
308 /* accessors */
309
310 - (void)setRunLoopModes:(NSArray *)_modes {
311   ASSIGN(self->runLoopModes, _modes);
312 }
313 - (NSArray *)runLoopModes {
314   return self->runLoopModes;
315 }
316
317 /* single queue */
318
319 static inline void _enqueue(EODelayedObserverQueue *self,
320                             EODelayedObserver **list,
321                             EODelayedObserver *newEntry)
322 {
323   if (*list == nil) {
324     /* first entry in this list */
325     *list = [newEntry retain];
326   }
327   else {
328     EODelayedObserver *e, *le;
329
330     for (e = *list, le = NULL; e != NULL; e = e->next) {
331       if (e == newEntry) {
332         /* already in queue */
333         return;
334       }
335       le = e;
336     }
337     le->next = [e retain];
338     e->next  = NULL;
339   }
340 }
341 static inline void _dequeue(EODelayedObserverQueue *self,
342                             EODelayedObserver **list,
343                             EODelayedObserver *entry)
344 {
345   EODelayedObserver *e, *le;
346
347   for (e = *list, le = NULL; e != NULL; e = e->next) {
348     if (e == entry) {
349       /* found entry */
350       le->next = e->next;
351       [e release];
352       return;
353     }
354     le = e;
355   }
356 }
357
358 static inline void _notify(EODelayedObserverQueue *self, EODelayedObserver *list)
359 {
360   while (list) {
361     [list subjectChanged];
362     list = list->next;
363   }
364 }
365
366 /* managing queue */
367
368 - (void)enqueueObserver:(EODelayedObserver *)_observer {
369   if (_observer == nil) return;
370   
371   _enqueue(self, &(self->queues[[_observer priority]]), _observer);
372
373   if (!self->hasObservers) {
374     /* register for ASAP notification */
375     NSNotification *notification;
376     
377     notification = [NSNotification notificationWithName:@"EODelayedNotify"
378                                    object:self];
379     
380     [[NSNotificationQueue defaultQueue]
381                           enqueueNotification:notification
382                           postingStyle:NSPostASAP
383                           coalesceMask:NSNotificationCoalescingOnSender
384                           forModes:[self runLoopModes]];
385     
386     self->hasObservers = YES;
387   }
388 }
389 - (void)dequeueObserver:(EODelayedObserver *)_observer {
390   if (_observer == nil) return;
391   
392   _dequeue(self, &(self->queues[[_observer priority]]), _observer);
393 }
394
395 /* notification */
396
397 - (void)notifyObserversUpToPriority:(EOObserverPriority)_lastPriority {
398   unsigned i;
399   
400   for (i = 0; i < _lastPriority; i++)
401     _notify(self, self->queues[i]);
402 }
403
404 - (void)_notify:(NSNotification *)_notification {
405   [self notifyObserversUpToPriority:EOObserverPrioritySixth];
406 }
407
408 @end /* EODelayedObserverQueue */
409
410 @implementation EODelayedObserver
411
412 /* accessors */
413
414 - (EOObserverPriority)priority {
415   return EOObserverPriorityThird;
416 }
417
418 - (EODelayedObserverQueue *)observerQueue {
419   return [EODelayedObserverQueue defaultObserverQueue];
420 }
421
422 /* notifications */
423
424 - (void)subjectChanged {
425   [self doesNotRecognizeSelector:_cmd];
426 }
427
428 - (void)objectWillChange:(id)_object {
429   [[self observerQueue] enqueueObserver:self];
430 }
431
432 - (void)discardPendingNotification {
433   [[self observerQueue] dequeueObserver:self];
434 }
435
436 @end /* EODelayedObserver */
437
438 @implementation EOObserverProxy
439
440 - (id)initWithTarget:(id)_target action:(SEL)_action
441   priority:(EOObserverPriority)_priority
442 {
443   if ((self = [super init])) {
444     self->target   = [_target retain];
445     self->action   = _action;
446     self->priority = _priority;
447   }
448   return self;
449 }
450 - (id)init {
451   return [self initWithTarget:nil action:NULL priority:EOObserverPriorityThird];
452 }
453
454 - (void)dealloc {
455   [self->target release];
456   [super dealloc];
457 }
458
459 /* accessors */
460
461 - (EOObserverPriority)priority {
462   return self->priority;
463 }
464
465 /* notifications */
466
467 - (void)subjectChanged {
468   [self->target performSelector:self->action withObject:self];
469 }
470
471 @end /* EOObserverProxy */
472
473 /* value functions for mapping table */
474
475 static void mapValRetain(NSMapTable *self, const void *_value) {
476   /* do nothing */
477 }
478 static void mapValRelease(NSMapTable *self, void *_value) {
479   /* do nothing */
480 }
481
482 static NSString *mapDescribe(NSMapTable *self, const void *_value) {
483   return @"";
484 }