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