2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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 "EOKeyMapDataSource.h"
24 #include "NSArray+enumerator.h"
25 #include "EODataSource+NGExtensions.h"
26 #include "NSNull+misc.h"
27 #import <EOControl/EOControl.h>
31 @interface EOKeyMapDataSource(Private)
32 - (void)_registerForSource:(id)_source;
33 - (void)_removeObserverForSource:(id)_source;
34 - (void)_sourceChanged;
37 @implementation EOKeyMapDataSource
39 - (id)initWithDataSource:(EODataSource *)_ds map:(id)_map {
40 if ((self = [super init])) {
41 self->source = [_ds retain];
42 self->map = [_map retain];
43 [self _registerForSource:self->source];
47 - (id)initWithDataSource:(EODataSource *)_ds {
48 return [self initWithDataSource:_ds map:nil];
51 return [self initWithDataSource:nil map:nil];
55 [self _removeObserverForSource:self->source];
56 [self->classDescription release];
57 [self->entityKeys release];
58 [self->mappedKeys release];
60 [self->fspec release];
61 [self->source release];
67 - (EOFetchSpecification *)mapFetchSpecification:(EOFetchSpecification *)_fs {
68 return [_fs fetchSpecificationByApplyingKeyMap:self->map];
71 - (id)mapFromSourceObject:(id)_object {
73 if (_object == nil) return nil;
75 if (self->mappedKeys == nil) {
76 /* no need to rewrite keys, only taking a subset */
77 values = [_object valuesForKeys:self->entityKeys];
82 count = [self->entityKeys count];
83 values = [NSMutableDictionary dictionaryWithCapacity:count];
85 for (i = 0; i < count; i++) {
86 NSString *key, *newKey;
89 key = [self->mappedKeys objectAtIndex:i];
90 newKey = [self->entityKeys objectAtIndex:i];
92 value = [_object valueForKey:key];
93 if (value) [(NSMutableDictionary *)values setObject:value forKey:newKey];
97 return [[[EOMappedObject alloc]
98 initWithObject:_object values:values] autorelease];
101 - (id)mapToSourceObject:(id)_object {
103 if (_object == nil) return nil;
105 if ([_object isKindOfClass:[EOMappedObject class]]) {
108 if ((obj = [_object mappedObject]) == nil) {
109 NSLog(@"don't know how to back-map objects: %@", _object);
113 if ([obj isModified]) {
115 NSLog(@"%s: don't know how to back-map modified object: %@",
117 #if NeXT_Foundation_LIBRARY
118 [self doesNotRecognizeSelector:_cmd];
119 return nil; // keep compiler happy
121 return [self notImplemented:_cmd];
125 [obj applyChangesOnObject];
130 NSLog(@"don't know how to back-map objects of class %@", [_object class]);
131 #if NeXT_Foundation_LIBRARY
132 [self doesNotRecognizeSelector:_cmd];
134 return [self notImplemented:_cmd];
137 return nil; // keep compiler happy
140 - (id)mapCreatedObject:(id)_object {
141 return [self mapFromSourceObject:_object];
144 - (id)mapObjectForUpdate:(id)_object {
145 return [self mapToSourceObject:_object];
147 - (id)mapObjectForInsert:(id)_object {
148 return [self mapToSourceObject:_object];
150 - (id)mapObjectForDelete:(id)_object {
151 return [self mapToSourceObject:_object];
154 - (id)mapFetchedObject:(id)_object {
155 return [self mapFromSourceObject:_object];
158 - (void)setClassDescriptionForObjects:(NSClassDescription *)_cd {
159 ASSIGN(self->classDescription, _cd);
161 /* setup array of keys to map */
163 [self->entityKeys release]; self->entityKeys = nil;
164 [self->mappedKeys release]; self->mappedKeys = nil;
171 ma = [[NSMutableArray alloc] initWithCapacity:16];
173 /* first, collect keys we need */
175 if ((tmp = [_cd attributeKeys]))
176 [ma addObjectsFromArray:tmp];
177 if ((tmp = [_cd toOneRelationshipKeys]))
178 [ma addObjectsFromArray:tmp];
179 if ((tmp = [_cd toManyRelationshipKeys]))
180 [ma addObjectsFromArray:tmp];
182 self->entityKeys = [ma copy];
184 /* next, map those keys to the source-schema */
187 [ma removeAllObjects];
188 for (i = 0, count = [entityKeys count]; i < count; i++) {
189 NSString *mappedKey, *key;
191 key = [entityKeys objectAtIndex:i];
192 mappedKey = [self->map valueForKey:key];
193 [ma addObject:mappedKey ? mappedKey : key];
196 self->mappedKeys = [ma copy];
202 - (NSClassDescription *)classDescriptionForObjects {
203 return self->classDescription;
208 - (void)setSource:(EODataSource *)_source {
209 NSAssert(self->fspec == nil, @"only allowed as long as no spec is set !");
211 [self _removeObserverForSource:self->source];
212 ASSIGN(self->source, _source);
213 [self _registerForSource:self->source];
215 [self postDataSourceChangedNotification];
217 - (EODataSource *)source {
221 - (void)setFetchSpecification:(EOFetchSpecification *)_fetchSpec {
222 if (![_fetchSpec isEqual:self->fspec]) {
224 This saves the spec in the datasource and saves a mapped spec
225 in the source datasource.
227 EOFetchSpecification *mappedSpec;
229 ASSIGN(self->fspec, _fetchSpec);
230 mappedSpec = [self mapFetchSpecification:self->fspec];
231 [self->source setFetchSpecification:mappedSpec];
233 [self postDataSourceChangedNotification];
236 - (EOFetchSpecification *)fetchSpecification {
240 - (NSException *)lastException {
241 if ([self->source respondsToSelector:@selector(lastException)])
242 return [(id)self->source lastException];
246 /* fetch operations */
248 - (Class)fetchEnumeratorClass {
249 return [EOKeyMapDataSourceEnumerator class];
252 - (NSEnumerator *)fetchEnumerator {
255 if ((e = [self->source fetchEnumerator]) == nil)
258 e = [[[self fetchEnumeratorClass] alloc] initWithKeyMapDataSource:self
260 return [e autorelease];
263 - (NSArray *)fetchObjects {
264 NSAutoreleasePool *pool;
267 pool = [[NSArray alloc] init];
268 a = [[NSArray alloc] initWithObjectsFromEnumerator:[self fetchEnumerator]];
270 return [a autorelease];
275 - (void)insertObject:(id)_obj {
276 [self->source insertObject:[self mapObjectForInsert:_obj]];
279 - (void)deleteObject:(id)_obj {
280 [self->source deleteObject:[self mapObjectForDelete:_obj]];
284 return [self mapCreatedObject:[self->source createObject]];
286 - (void)updateObject:(id)_obj {
287 [self->source updateObject:[self mapObjectForUpdate:_obj]];
291 if ([self->source respondsToSelector:@selector(clear)])
292 [(id)self->source clear];
297 - (NSString *)description {
300 fmt = [NSString stringWithFormat:@"<%@[0x%08X]: source=%@ map=%@>",
301 NSStringFromClass([self class]), self,
302 self->source, self->map];
306 @end /* EOKeyMapDataSource */
308 @implementation EOKeyMapDataSource(Private)
310 - (void)_registerForSource:(id)_source {
311 static NSNotificationCenter *nc = nil;
313 if (_source != nil) {
315 nc = [[NSNotificationCenter defaultCenter] retain];
317 [nc addObserver:self selector:@selector(_sourceChanged)
318 name:EODataSourceDidChangeNotification object:_source];
322 - (void)_removeObserverForSource:(id)_source {
323 static NSNotificationCenter *nc = nil;
325 if (_source != nil) {
327 nc = [NSNotificationCenter defaultCenter];
328 [nc removeObserver:self name:EODataSourceDidChangeNotification
333 - (void)_sourceChanged {
334 [self postDataSourceChangedNotification];
337 @end /* EOKeyMapDataSource(Private) */
339 @implementation EOKeyMapDataSourceEnumerator
341 - (id)initWithKeyMapDataSource:(EOKeyMapDataSource *)_ds
342 fetchEnumerator:(NSEnumerator *)_enum
344 if ((self = [super init])) {
345 self->ds = [_ds retain];
346 self->source = [_enum retain];
352 [self->source release];
364 if ((object = [self->source nextObject]) == nil) {
369 return [self->ds mapFetchedObject:object];
372 @end /* EOKeyMapDataSourceEnumerator */
374 @implementation EOMappedObject
376 - (id)initWithObject:(id)_object values:(NSDictionary *)_values {
377 if ((self = [super init])) {
378 self->original = [_object retain];
379 self->values = [_values mutableCopy];
385 [self->original release];
386 [self->globalID release];
387 [self->values release];
394 return self->original;
396 - (EOGlobalID *)globalID {
397 if (self->globalID == nil) {
398 if ([self->original respondsToSelector:@selector(globalID)])
399 self->globalID = [[self->original globalID] retain];
401 return self->globalID;
405 return self->flags.didChange ? YES : NO;
408 self->flags.didChange = 1;
411 - (void)applyChangesOnObject {
412 if (!self->flags.didChange)
413 [self->original takeValuesFromDictionary:self->values];
416 /* mimic dictionary */
418 - (void)setObject:(id)_obj forKey:(id)_key {
420 [self->values setObject:_obj forKey:_key];
422 - (id)objectForKey:(id)_key {
423 return [self->values objectForKey:_key];
426 - (void)removeObjectForKey:(id)_key {
428 [self->values removeObjectForKey:_key];
431 - (NSEnumerator *)keyEnumerator {
432 return [self->values keyEnumerator];
434 - (NSEnumerator *)objectEnumerator {
435 return [self->values objectEnumerator];
438 - (NSDictionary *)asDictionary {
444 - (void)takeValue:(id)_value forKey:(NSString *)_key {
446 if ([_value isNotNull])
447 [self->values setObject:_value forKey:_key];
449 [self->values removeObjectForKey:_key];
452 - (id)valueForKey:(NSString *)_key {
453 return [self->values objectForKey:_key];
456 @end /* EOMappedObject */