2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
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
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.
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
23 #include "EOGenericRecord.h"
25 #include "EOClassDescription.h"
26 #include "EOObserver.h"
27 #include "EOKeyValueCoding.h"
31 @interface NSObject(MappedArray)
32 - (NSArray *)mappedArrayUsingSelector:(SEL)_selector;
35 @interface _EOConcreteEOGenericRecordKeyEnumerator : NSEnumerator
37 EOGenericRecord *dict;
38 struct _NSMapNode *node;
41 + (id)enumWithEO:(EOGenericRecord *)_eo;
44 #if !LIB_FOUNDATION_LIBRARY
48 struct _NSMapNode *next;
52 @implementation EOGenericRecord
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);
66 static EONull *null = nil;
73 if (null == nil) null = [[EONull null] retain];
76 - (id)initWithEditingContext:(id)_ec
77 classDescription:(EOClassDescription *)_classDesc
78 globalID:(EOGlobalID *)_oid
83 NSAssert(_classDesc, @"did not find class description for EOGenericRecord !");
86 capacity = 16 * 4 / 3;
87 capacity = capacity ? capacity : 13;
88 if (!is_prime(capacity))
89 capacity = nextPrime(capacity);
91 self->hashSize = capacity;
92 self->nodes = NSZoneCalloc([self zone], capacity, sizeof(void *));
95 self->classDescription = [_classDesc retain];
96 self->willChange = [self methodForSelector:@selector(willChange)];
101 EOClassDescription *c;
103 c = (EOClassDescription *)
104 [EOClassDescription classDescriptionForClass:[self class]];
106 return [self initWithEditingContext:nil classDescription:c globalID:nil];
110 if ([self respondsToSelector:@selector(_letDatabasesForget)])
111 [self performSelector:@selector(_letDatabasesForget)];
113 if (self->itemsCount > 0) {
114 NSZone *z = [self zone];
117 for (i = 0; i < self->hashSize; i++) {
118 struct _NSMapNode *next, *node;
120 node = self->nodes[i];
121 self->nodes[i] = NULL;
123 [(id)node->key release];
124 [(id)node->value release];
130 self->itemsCount = 0;
134 NSZoneFree([self zone], self->nodes);
136 [self->classDescription release];
140 /* class description */
142 - (NSClassDescription *)classDescription {
143 return self->classDescription;
146 static inline void _willChange(EOGenericRecord *self) {
147 if (self->willChange)
148 self->willChange(self, @selector(willChange:));
153 - (void)takeValue:(id)_value forKey:(id)_key {
156 if (_value == nil) _value = null;
159 NSAssert1(_key, @"called -takeValue:0x%08X forKey:nil !", _value);
162 value = eoGet(self, _key);
164 if (value != _value) {
168 eoRemove(self, _key);
170 eoInsert(self, _key, _value);
173 - (id)valueForKey:(id)_key {
176 if ((v = eoGet(self, _key)) == nil) {
178 if ([_key isEqualToString:@"description"]) {
179 NSLog(@"WARNING(%s): -valueForKey:%@ is nil, calling super",
180 __PRETTY_FUNCTION__, _key);
184 return [super valueForKey:_key];
188 NSAssert(null != nil, @"missing null ..");
191 return v == null ? nil : v;
194 - (void)takeValuesFromDictionary:(NSDictionary *)dictionary {
197 NSEnumerator *e = [dictionary keyEnumerator];
200 while ((key = [e nextObject])) {
201 id value = [dictionary objectForKey:key];
202 NSAssert(value, @"tried to set <nil> value ..");
203 eoInsert(self, key, value);
208 - (NSDictionary *)valuesForKeys:(NSArray *)keys {
209 // OPT - cache IMP for objectAtIndex, objectForKey, setObject:forKey:
210 NSMutableDictionary *dict;
211 IMP objAtIdx, setObjForKey;
214 if (keys == nil) return nil;
217 dict = [NSMutableDictionary dictionaryWithCapacity:n];
219 objAtIdx = [keys methodForSelector:@selector(objectAtIndex:)];
220 setObjForKey = [dict methodForSelector:@selector(setObject:forKey:)];
222 for (i = 0; i < n; i++) {
226 key = objAtIdx(keys, @selector(objectAtIndex:), i);
227 NSAssert(key, @"invalid key <nil>");
229 value = [self valueForKey:key];
230 if (value == nil) value = null;
233 NSAssert2(value, @"eo of type %@, missing value for attribute %@",
234 self->classDescription, key);
237 setObjForKey(dict, @selector(setObject:forKey:), value, key);
242 - (void)takeStoredValue:(id)_value forKey:(NSString *)_key {
243 if (_value == nil) _value = null;
244 [self takeValue:_value forKey:_key];
246 - (id)storedValueForKey:(NSString *)_key {
249 v = [self valueForKey:_key];
251 NSAssert(v != null, @"valueForKey: return NSNull !");
253 //if (v == nil) return [super storedValueForKey:_key];
257 - (void)setObject:(id)object forKey:(id)key {
258 if (object == nil) object = null;
261 eoInsert(self, key, object);
264 - (id)objectForKey:(id)key {
265 return eoGet(self, key);
268 - (void)removeObjectForKey:(id)key {
273 - (BOOL)kvcIsPreferredInKeyPath {
277 - (NSEnumerator *)keyEnumerator {
278 return [_EOConcreteEOGenericRecordKeyEnumerator enumWithEO:self];
283 - (id)copyWithZone:(NSZone *)_zone {
284 return [self retain];
289 - (NSString *)description {
290 return [NSString stringWithFormat:
291 @"<EOGenericRecord: description %@ attributes=%@>",
293 [self valuesForKeys:[self attributeKeys]]];
298 static __inline__ unsigned _getHashSize(EOGenericRecord *self)
300 return self->hashSize;
303 static __inline__ struct _NSMapNode *
304 _getNodeAt(EOGenericRecord *self, int idx)
306 return self->nodes[idx];
309 static BOOL is_prime(unsigned n) {
312 for (i = 2; i <= n2; i++) {
318 static unsigned nextPrime(unsigned old_value) {
319 unsigned i, new_value = old_value | 1;
321 for (i = new_value; i >= new_value; i += 2) {
328 static void eoMapGrow(EOGenericRecord *table, unsigned newSize) {
330 struct _NSMapNode **newNodeTable;
333 NSZoneCalloc([table zone], newSize, sizeof(struct _NSMapNode*));
335 for (i = 0; i < table->hashSize; i++) {
336 struct _NSMapNode *next, *node;
339 node = table->nodes[i];
342 h = [(id)node->key hash] % newSize;
343 node->next = newNodeTable[h];
344 newNodeTable[h] = node;
348 NSZoneFree([table zone], table->nodes);
349 table->nodes = newNodeTable;
350 table->hashSize = newSize;
353 static void eoCheckMapTableFull(EOGenericRecord *table) {
354 if( ++(table->itemsCount) >= ((table->hashSize * 3) / 4)) {
357 newSize = nextPrime((table->hashSize * 4) / 3);
358 if(newSize != table->hashSize)
359 eoMapGrow(table, newSize);
363 static __inline__ void eoInsert(EOGenericRecord *table, id key, id value) {
365 struct _NSMapNode *node;
367 h = [key hash] % table->hashSize;
369 for (node = table->nodes[h]; node; node = node->next) {
370 /* might cache the selector .. */
371 if ([key isEqual:node->key])
375 /* Check if an entry for key exist in nodeTable. */
377 /* key exist. Set for it new value and return the old value of it. */
378 if (key != node->key) {
380 [(id)node->key release];
382 if (value != node->value) {
383 value = [value retain];
384 [(id)node->value release];
391 /* key not found. Allocate a new bucket and initialize it. */
392 node = NSZoneMalloc([table zone], sizeof(struct _NSMapNode));
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;
400 eoCheckMapTableFull(table);
403 static __inline__ id eoGet(EOGenericRecord *table, id key) {
404 struct _NSMapNode *node;
406 node = table->nodes[[key hash] % table->hashSize];
407 for (; node; node = node->next) {
408 /* could cache method .. */
409 if ([key isEqual:node->key])
415 static __inline__ void eoRemove(EOGenericRecord *table, id key) {
417 struct _NSMapNode *node, *node1 = NULL;
422 h = [key hash] % table->hashSize;
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
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];
434 table->nodes[h] = node->next;
436 node1->next = node->next;
437 NSZoneFree([table zone], node);
438 (table->itemsCount)--;
444 @end /* EOGenericRecord */
446 @implementation _EOConcreteEOGenericRecordKeyEnumerator
448 - (id)initWithEO:(EOGenericRecord *)_eo {
449 self->dict = [_eo retain];
456 [self->dict release];
460 + (id)enumWithEO:(EOGenericRecord *)_eo {
461 return [[[self alloc] initWithEO:_eo] autorelease];
467 self->node = self->node->next;
469 if (self->node == NULL) {
471 ((unsigned)self->bucket) < _getHashSize(self->dict);
474 if (_getNodeAt(self->dict, self->bucket)) {
475 self->node = _getNodeAt(self->dict, self->bucket);
479 if (((unsigned)self->bucket) >= _getHashSize(self->dict)) {
481 self->bucket = (_getHashSize(self->dict) - 1);
485 return self->node->key;
488 @end /* _EOConcreteEOGenericRecordKeyEnumerator */