2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "EOGenericRecord.h"
24 #include "EOClassDescription.h"
25 #include "EOObserver.h"
26 #include "EOKeyValueCoding.h"
30 @interface NSObject(MappedArray)
31 - (NSArray *)mappedArrayUsingSelector:(SEL)_selector;
34 @interface _EOConcreteEOGenericRecordKeyEnumerator : NSEnumerator
36 EOGenericRecord *dict;
37 struct _NSMapNode *node;
40 + (id)enumWithEO:(EOGenericRecord *)_eo;
43 #if !LIB_FOUNDATION_LIBRARY
47 struct _NSMapNode *next;
51 @implementation EOGenericRecord
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);
65 static EONull *null = nil;
72 if (null == nil) null = [[EONull null] retain];
75 - (id)initWithEditingContext:(id)_ec
76 classDescription:(EOClassDescription *)_classDesc
77 globalID:(EOGlobalID *)_oid
82 NSAssert(_classDesc, @"did not find class description for EOGenericRecord !");
85 capacity = 16 * 4 / 3;
86 capacity = capacity ? capacity : 13;
87 if (!is_prime(capacity))
88 capacity = nextPrime(capacity);
90 self->hashSize = capacity;
91 self->nodes = NSZoneCalloc([self zone], capacity, sizeof(void *));
94 self->classDescription = [_classDesc retain];
95 self->willChange = [self methodForSelector:@selector(willChange)];
100 EOClassDescription *c;
102 c = (EOClassDescription *)
103 [EOClassDescription classDescriptionForClass:[self class]];
105 return [self initWithEditingContext:nil classDescription:c globalID:nil];
109 if ([self respondsToSelector:@selector(_letDatabasesForget)])
110 [self performSelector:@selector(_letDatabasesForget)];
112 if (self->itemsCount > 0) {
113 NSZone *z = [self zone];
116 for (i = 0; i < self->hashSize; i++) {
117 struct _NSMapNode *next, *node;
119 node = self->nodes[i];
120 self->nodes[i] = NULL;
122 [(id)node->key release];
123 [(id)node->value release];
129 self->itemsCount = 0;
133 NSZoneFree([self zone], self->nodes);
135 [self->classDescription release];
139 /* class description */
141 - (NSClassDescription *)classDescription {
142 return self->classDescription;
145 static inline void _willChange(EOGenericRecord *self) {
146 if (self->willChange)
147 self->willChange(self, @selector(willChange:));
152 - (void)takeValue:(id)_value forKey:(NSString *)_key {
155 if (_value == nil) _value = null;
158 NSAssert1(_key, @"called -takeValue:0x%p forKey:nil !", _value);
161 value = eoGet(self, _key);
163 if (value != _value) {
167 eoRemove(self, _key);
169 eoInsert(self, _key, _value);
172 - (id)valueForKey:(NSString *)_key {
175 if ((v = eoGet(self, _key)) == nil) {
177 if ([_key isEqualToString:@"description"]) {
178 NSLog(@"WARNING(%s): -valueForKey:%@ is nil, calling super",
179 __PRETTY_FUNCTION__, _key);
183 return [super valueForKey:_key];
187 NSAssert(null != nil, @"missing null ..");
190 return v == null ? nil : v;
193 - (void)takeValuesFromDictionary:(NSDictionary *)dictionary {
196 NSEnumerator *e = [dictionary keyEnumerator];
199 while ((key = [e nextObject])) {
200 id value = [dictionary objectForKey:key];
201 NSAssert(value, @"tried to set <nil> value ..");
202 eoInsert(self, key, value);
207 - (NSDictionary *)valuesForKeys:(NSArray *)keys {
208 // OPT - cache IMP for objectAtIndex, objectForKey, setObject:forKey:
209 NSMutableDictionary *dict;
210 IMP objAtIdx, setObjForKey;
213 if (keys == nil) return nil;
216 dict = [NSMutableDictionary dictionaryWithCapacity:n];
218 objAtIdx = [keys methodForSelector:@selector(objectAtIndex:)];
219 setObjForKey = [dict methodForSelector:@selector(setObject:forKey:)];
221 for (i = 0; i < n; i++) {
225 key = objAtIdx(keys, @selector(objectAtIndex:), i);
226 NSAssert(key, @"invalid key <nil>");
228 value = [self valueForKey:key];
229 if (value == nil) value = null;
232 NSAssert2(value, @"eo of type %@, missing value for attribute %@",
233 self->classDescription, key);
236 setObjForKey(dict, @selector(setObject:forKey:), value, key);
241 - (void)takeStoredValue:(id)_value forKey:(NSString *)_key {
242 if (_value == nil) _value = null;
243 [self takeValue:_value forKey:_key];
245 - (id)storedValueForKey:(NSString *)_key {
248 v = [self valueForKey:_key];
250 NSAssert(v != null, @"valueForKey: return NSNull !");
252 //if (v == nil) return [super storedValueForKey:_key];
256 - (void)setObject:(id)object forKey:(id)key {
257 if (object == nil) object = null;
260 eoInsert(self, key, object);
263 - (id)objectForKey:(id)key {
264 return eoGet(self, key);
267 - (void)removeObjectForKey:(id)key {
272 - (BOOL)kvcIsPreferredInKeyPath {
276 - (NSEnumerator *)keyEnumerator {
277 return [_EOConcreteEOGenericRecordKeyEnumerator enumWithEO:self];
282 - (id)copyWithZone:(NSZone *)_zone {
283 return [self retain];
288 - (NSString *)description {
289 return [NSString stringWithFormat:
290 @"<EOGenericRecord: description %@ attributes=%@>",
292 [self valuesForKeys:[self attributeKeys]]];
297 static __inline__ unsigned _getHashSize(EOGenericRecord *self)
299 return self->hashSize;
302 static __inline__ struct _NSMapNode *
303 _getNodeAt(EOGenericRecord *self, int idx)
305 return self->nodes[idx];
308 static BOOL is_prime(unsigned n) {
311 for (i = 2; i <= n2; i++) {
317 static unsigned nextPrime(unsigned old_value) {
318 unsigned i, new_value = old_value | 1;
320 for (i = new_value; i >= new_value; i += 2) {
327 static void eoMapGrow(EOGenericRecord *table, unsigned newSize) {
329 struct _NSMapNode **newNodeTable;
332 NSZoneCalloc([table zone], newSize, sizeof(struct _NSMapNode*));
334 for (i = 0; i < table->hashSize; i++) {
335 struct _NSMapNode *next, *node;
338 node = table->nodes[i];
341 h = [(id)node->key hash] % newSize;
342 node->next = newNodeTable[h];
343 newNodeTable[h] = node;
347 NSZoneFree([table zone], table->nodes);
348 table->nodes = newNodeTable;
349 table->hashSize = newSize;
352 static void eoCheckMapTableFull(EOGenericRecord *table) {
353 if( ++(table->itemsCount) >= ((table->hashSize * 3) / 4)) {
356 newSize = nextPrime((table->hashSize * 4) / 3);
357 if(newSize != table->hashSize)
358 eoMapGrow(table, newSize);
362 static __inline__ void eoInsert(EOGenericRecord *table, id key, id value) {
364 struct _NSMapNode *node;
366 h = [key hash] % table->hashSize;
368 for (node = table->nodes[h]; node; node = node->next) {
369 /* might cache the selector .. */
370 if ([key isEqual:node->key])
374 /* Check if an entry for key exist in nodeTable. */
376 /* key exist. Set for it new value and return the old value of it. */
377 if (key != node->key) {
379 [(id)node->key release];
381 if (value != node->value) {
382 value = [value retain];
383 [(id)node->value release];
390 /* key not found. Allocate a new bucket and initialize it. */
391 node = NSZoneMalloc([table zone], sizeof(struct _NSMapNode));
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;
399 eoCheckMapTableFull(table);
402 static __inline__ id eoGet(EOGenericRecord *table, id key) {
403 struct _NSMapNode *node;
405 node = table->nodes[[key hash] % table->hashSize];
406 for (; node; node = node->next) {
407 /* could cache method .. */
408 if ([key isEqual:node->key])
414 static __inline__ void eoRemove(EOGenericRecord *table, id key) {
416 struct _NSMapNode *node, *node1 = NULL;
421 h = [key hash] % table->hashSize;
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
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];
433 table->nodes[h] = node->next;
435 node1->next = node->next;
436 NSZoneFree([table zone], node);
437 (table->itemsCount)--;
443 @end /* EOGenericRecord */
445 @implementation _EOConcreteEOGenericRecordKeyEnumerator
447 - (id)initWithEO:(EOGenericRecord *)_eo {
448 self->dict = [_eo retain];
455 [self->dict release];
459 + (id)enumWithEO:(EOGenericRecord *)_eo {
460 return [[[self alloc] initWithEO:_eo] autorelease];
466 self->node = self->node->next;
468 if (self->node == NULL) {
470 ((unsigned)self->bucket) < _getHashSize(self->dict);
473 if (_getNodeAt(self->dict, self->bucket)) {
474 self->node = _getNodeAt(self->dict, self->bucket);
478 if (((unsigned)self->bucket) >= _getHashSize(self->dict)) {
480 self->bucket = (_getHashSize(self->dict) - 1);
484 return self->node->key;
487 @end /* _EOConcreteEOGenericRecordKeyEnumerator */