]> err.no Git - sope/blob - sope-core/EOControl/EOGenericRecord.m
gnu runtime bugfix
[sope] / sope-core / EOControl / EOGenericRecord.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 "EOGenericRecord.h"
23 #include "EONull.h"
24 #include "EOClassDescription.h"
25 #include "EOObserver.h"
26 #include "EOKeyValueCoding.h"
27 #include "common.h"
28 #include <math.h>
29
30 @interface NSObject(MappedArray)
31 - (NSArray *)mappedArrayUsingSelector:(SEL)_selector;
32 @end
33
34 @interface _EOConcreteEOGenericRecordKeyEnumerator : NSEnumerator
35 {
36     EOGenericRecord   *dict;
37     struct _NSMapNode *node;
38     int               bucket;
39 }
40 + (id)enumWithEO:(EOGenericRecord *)_eo;
41 @end
42
43 #if !LIB_FOUNDATION_LIBRARY
44 struct _NSMapNode {
45     void *key;
46     void *value;
47     struct _NSMapNode *next;
48 };
49 #endif
50
51 @implementation EOGenericRecord
52
53 /* final methods */
54
55 static __inline__ unsigned _getHashSize(EOGenericRecord *self);
56 static __inline__ struct _NSMapNode *_getNodeAt(EOGenericRecord *self, int idx);
57 static BOOL is_prime(unsigned n);
58 static unsigned nextPrime(unsigned old_value);
59 static void eoMapGrow(EOGenericRecord *table, unsigned newSize);
60 static void eoCheckMapTableFull(EOGenericRecord *table);
61 static __inline__ void eoInsert(EOGenericRecord *table, id key, id value);
62 static __inline__ id eoGet(EOGenericRecord *table, id key);
63 static __inline__ void eoRemove(EOGenericRecord *table, id key);
64
65 static EONull *null = nil;
66
67 + (int)version {
68   return 2;
69 }
70
71 + (void)initialize {
72   if (null == nil) null = [[EONull null] retain];
73 }
74
75 - (id)initWithEditingContext:(id)_ec
76   classDescription:(EOClassDescription *)_classDesc
77   globalID:(EOGlobalID *)_oid
78 {
79   unsigned capacity;
80   
81 #if DEBUG
82   NSAssert(_classDesc, @"did not find class description for EOGenericRecord !");
83 #endif
84
85   capacity = 16 * 4 / 3;
86   capacity = capacity ? capacity : 13;
87   if (!is_prime(capacity))
88     capacity = nextPrime(capacity);
89
90   self->hashSize   = capacity;
91   self->nodes      = NSZoneCalloc([self zone], capacity, sizeof(void *));
92   self->itemsCount = 0;
93   
94   self->classDescription = [_classDesc retain];
95   self->willChange = [self methodForSelector:@selector(willChange)];
96   return self;
97 }
98
99 - (id)init {
100   EOClassDescription *c;
101   
102   c = (EOClassDescription *)
103     [EOClassDescription classDescriptionForClass:[self class]];
104   
105   return [self initWithEditingContext:nil classDescription:c globalID:nil];
106 }
107
108 - (void)dealloc {
109   if ([self respondsToSelector:@selector(_letDatabasesForget)])
110     [self performSelector:@selector(_letDatabasesForget)];
111   
112   if (self->itemsCount > 0) {
113     NSZone *z = [self zone];
114     unsigned i;
115         
116     for (i = 0; i < self->hashSize; i++) {
117       struct _NSMapNode *next, *node;
118             
119       node = self->nodes[i];
120       self->nodes[i] = NULL;            
121       while (node) {
122         [(id)node->key   release];
123         [(id)node->value release];
124         next = node->next;
125         NSZoneFree(z, node);
126         node = next;
127       }
128     }
129     self->itemsCount = 0;
130   }
131     
132   if (self->nodes)
133     NSZoneFree([self zone], self->nodes);
134
135   [self->classDescription release];
136   [super dealloc];
137 }
138
139 /* class description */
140
141 - (NSClassDescription *)classDescription {
142   return self->classDescription;
143 }
144
145 static inline void _willChange(EOGenericRecord *self) {
146   if (self->willChange)
147     self->willChange(self, @selector(willChange:));
148   else
149     [self willChange];
150 }
151
152 - (void)takeValue:(id)_value forKey:(NSString *)_key {
153   id value;
154   
155   if (_value == nil) _value = null;
156   
157 #if DEBUG
158   NSAssert1(_key, @"called -takeValue:0x%p forKey:nil !", _value);
159 #endif
160   
161   value = eoGet(self, _key);
162   
163   if (value != _value) {
164     _willChange(self);
165     
166     if (_value == nil)
167       eoRemove(self, _key);
168     else
169       eoInsert(self, _key, _value);
170   }
171 }
172 - (id)valueForKey:(NSString *)_key {
173   id v;
174   
175   if ((v = eoGet(self, _key)) == nil) {
176 #if DEBUG && 0
177     if ([_key isEqualToString:@"description"]) {
178       NSLog(@"WARNING(%s): -valueForKey:%@ is nil, calling super",
179             __PRETTY_FUNCTION__, _key);
180     }
181 #endif
182     
183     return [super valueForKey:_key];
184   }
185
186 #if DEBUG
187   NSAssert(null != nil, @"missing null ..");
188 #endif
189   
190   return v == null ? nil : v;
191 }
192
193 - (void)takeValuesFromDictionary:(NSDictionary *)dictionary {
194   _willChange(self);
195   {
196     NSEnumerator *e = [dictionary keyEnumerator];
197     NSString     *key;
198
199     while ((key = [e nextObject])) {
200       id value = [dictionary objectForKey:key];
201       NSAssert(value, @"tried to set <nil> value ..");
202       eoInsert(self, key, value);
203     }
204   }
205 }
206
207 - (NSDictionary *)valuesForKeys:(NSArray *)keys {
208   // OPT - cache IMP for objectAtIndex, objectForKey, setObject:forKey:
209   NSMutableDictionary *dict;
210   IMP objAtIdx, setObjForKey;
211   unsigned int i, n;
212
213   if (keys == nil) return nil;
214
215   n = [keys count];
216   dict = [NSMutableDictionary dictionaryWithCapacity:n];
217
218   objAtIdx     = [keys methodForSelector:@selector(objectAtIndex:)];
219   setObjForKey = [dict methodForSelector:@selector(setObject:forKey:)];
220     
221   for (i = 0; i < n; i++) {
222     NSString *key;
223     id       value;
224
225     key = objAtIdx(keys, @selector(objectAtIndex:), i);
226     NSAssert(key, @"invalid key <nil>");
227     
228     value = [self valueForKey:key];
229     if (value == nil) value = null;
230     
231 #if DEBUG
232     NSAssert2(value, @"eo of type %@, missing value for attribute %@",
233               self->classDescription, key);
234 #endif
235     
236     setObjForKey(dict, @selector(setObject:forKey:), value, key);
237   }
238   return dict;
239 }
240
241 - (void)takeStoredValue:(id)_value forKey:(NSString *)_key {
242   if (_value == nil) _value = null;
243   [self takeValue:_value forKey:_key];
244 }
245 - (id)storedValueForKey:(NSString *)_key {
246   id v;
247
248   v = [self valueForKey:_key];
249 #if DEBUG && 0
250   NSAssert(v != null, @"valueForKey: return NSNull !");
251 #endif
252   //if (v == nil) return [super storedValueForKey:_key];
253   return v;
254 }
255
256 - (void)setObject:(id)object forKey:(id)key {
257   if (object == nil) object = null;
258   
259   _willChange(self);
260   eoInsert(self, key, object);
261 }
262
263 - (id)objectForKey:(id)key {
264   return eoGet(self, key);
265 }
266
267 - (void)removeObjectForKey:(id)key {
268   _willChange(self);
269   eoRemove(self, key);
270 }
271
272 - (BOOL)kvcIsPreferredInKeyPath {
273   return YES;
274 }
275
276 - (NSEnumerator *)keyEnumerator {
277   return [_EOConcreteEOGenericRecordKeyEnumerator enumWithEO:self];
278 }
279
280 /* copying */
281
282 - (id)copyWithZone:(NSZone *)_zone {
283   return [self retain];
284 }
285
286 /* description */
287
288 - (NSString *)description {
289   return [NSString stringWithFormat:
290                      @"<EOGenericRecord: description %@ attributes=%@>",
291                      [self entityName],
292                      [self valuesForKeys:[self attributeKeys]]];
293 }
294
295 /* final methods */
296
297 static __inline__ unsigned _getHashSize(EOGenericRecord *self)
298 {
299     return self->hashSize;
300 }
301
302 static __inline__ struct _NSMapNode *
303 _getNodeAt(EOGenericRecord *self, int idx)
304 {
305     return self->nodes[idx];
306 }
307
308 static BOOL is_prime(unsigned n) {
309   int i, n2 = sqrt(n);
310
311   for (i = 2; i <= n2; i++) {
312     if (n % i == 0)
313       return NO;
314   }
315   return YES;
316 }
317 static unsigned nextPrime(unsigned old_value) {
318   unsigned i, new_value = old_value | 1;
319
320   for (i = new_value; i >= new_value; i += 2) {
321     if (is_prime(i))
322       return i;
323   }
324   return old_value;
325 }
326
327 static void eoMapGrow(EOGenericRecord *table, unsigned newSize) {
328   unsigned i;
329   struct _NSMapNode **newNodeTable;
330     
331   newNodeTable =
332     NSZoneCalloc([table zone], newSize, sizeof(struct _NSMapNode*));
333     
334   for (i = 0; i < table->hashSize; i++) {
335     struct _NSMapNode *next, *node;
336     unsigned int h;
337     
338     node = table->nodes[i];
339     while (node) {
340       next = node->next;
341       h = [(id)node->key hash] % newSize;
342       node->next = newNodeTable[h];
343       newNodeTable[h] = node;
344       node = next;
345     }
346   }
347   NSZoneFree([table zone], table->nodes);
348   table->nodes    = newNodeTable;
349   table->hashSize = newSize;
350 }
351
352 static void eoCheckMapTableFull(EOGenericRecord *table) {
353   if( ++(table->itemsCount) >= ((table->hashSize * 3) / 4)) {
354     unsigned newSize;
355     
356     newSize = nextPrime((table->hashSize * 4) / 3);
357     if(newSize != table->hashSize)
358       eoMapGrow(table, newSize);
359   }
360 }
361
362 static __inline__ void eoInsert(EOGenericRecord *table, id key, id value) {
363   unsigned int h;
364   struct _NSMapNode *node;
365   
366   h = [key hash] % table->hashSize;
367   
368   for (node = table->nodes[h]; node; node = node->next) {
369     /* might cache the selector .. */
370     if ([key isEqual:node->key])
371       break;
372   }
373   
374   /* Check if an entry for key exist in nodeTable. */
375   if (node) {
376     /* key exist. Set for it new value and return the old value of it. */
377     if (key != node->key) {
378       key = [key retain];
379       [(id)node->key release];
380     }
381     if (value != node->value) {
382       value = [value retain];
383       [(id)node->value release];
384     }
385     node->key   = key;
386     node->value = value;
387     return;
388   }
389   
390   /* key not found. Allocate a new bucket and initialize it. */
391   node = NSZoneMalloc([table zone], sizeof(struct _NSMapNode));
392   key   = [key   retain];
393   value = [value retain];
394   node->key   = (void*)key;
395   node->value = (void*)value;
396   node->next  = table->nodes[h];
397   table->nodes[h] = node;
398   
399   eoCheckMapTableFull(table);
400 }
401
402 static __inline__ id eoGet(EOGenericRecord *table, id key) {
403     struct _NSMapNode *node;
404     
405     node = table->nodes[[key hash] % table->hashSize];
406     for (; node; node = node->next) {
407         /* could cache method .. */
408         if ([key isEqual:node->key])
409             return node->value;
410     }
411     return nil;
412 }
413
414 static __inline__ void eoRemove(EOGenericRecord *table, id key) {
415     unsigned int h;
416     struct _NSMapNode *node, *node1 = NULL;
417
418     if (key == nil)
419         return;
420
421     h = [key hash] % table->hashSize;
422     
423     // node point to current bucket, and node1 to previous bucket or to NULL
424     // if current node is the first node in the list 
425     
426     for (node = table->nodes[h]; node; node1 = node, node = node->next) {
427         /* could cache method .. */
428         if ([key isEqual:node->key]) {
429             [(id)node->key   release];
430             [(id)node->value release];
431             
432             if (!node1)
433                 table->nodes[h] = node->next;
434             else
435                 node1->next = node->next;
436             NSZoneFree([table zone], node);
437             (table->itemsCount)--;
438             return;
439         }
440     }
441 }
442
443 @end /* EOGenericRecord */
444
445 @implementation _EOConcreteEOGenericRecordKeyEnumerator
446
447 - (id)initWithEO:(EOGenericRecord *)_eo {
448   self->dict   = [_eo retain];
449   self->node   = NULL;
450   self->bucket = -1;
451   return self;
452 }
453
454 - (void)dealloc {
455   [self->dict release];
456   [super dealloc];
457 }
458
459 + (id)enumWithEO:(EOGenericRecord *)_eo {
460   return [[[self alloc] initWithEO:_eo] autorelease];
461 }
462
463 - (id)nextObject
464 {
465     if (self->node)
466         self->node = self->node->next;
467     
468     if (self->node == NULL) {
469         for(self->bucket++;
470             ((unsigned)self->bucket) < _getHashSize(self->dict);
471             self->bucket++) {
472
473             if (_getNodeAt(self->dict, self->bucket)) {
474                 self->node = _getNodeAt(self->dict, self->bucket);
475                 break;
476             }
477         }
478         if (((unsigned)self->bucket) >= _getHashSize(self->dict)) {
479             self->node = NULL;
480             self->bucket = (_getHashSize(self->dict) - 1);
481             return nil;
482         }
483     }
484     return self->node->key;
485 }
486
487 @end /* _EOConcreteEOGenericRecordKeyEnumerator */