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 "EOKeyMapDataSource.h"
23 #include "NSArray+enumerator.h"
24 #include "EODataSource+NGExtensions.h"
25 #include "NSNull+misc.h"
26 #import <EOControl/EOControl.h>
30 @interface EOKeyMapDataSource(Private)
31 - (void)_registerForSource:(id)_source;
32 - (void)_removeObserverForSource:(id)_source;
33 - (void)_sourceChanged;
36 @implementation EOKeyMapDataSource
38 - (id)initWithDataSource:(EODataSource *)_ds map:(id)_map {
39 if ((self = [super init])) {
40 self->source = [_ds retain];
41 self->map = [_map retain];
42 [self _registerForSource:self->source];
46 - (id)initWithDataSource:(EODataSource *)_ds {
47 return [self initWithDataSource:_ds map:nil];
50 return [self initWithDataSource:nil map:nil];
54 [self _removeObserverForSource:self->source];
55 [self->classDescription release];
56 [self->entityKeys release];
57 [self->mappedKeys release];
59 [self->fspec release];
60 [self->source release];
66 - (EOFetchSpecification *)mapFetchSpecification:(EOFetchSpecification *)_fs {
67 return [_fs fetchSpecificationByApplyingKeyMap:self->map];
70 - (id)mapFromSourceObject:(id)_object {
72 if (_object == nil) return nil;
74 if (self->mappedKeys == nil) {
75 /* no need to rewrite keys, only taking a subset */
76 values = [_object valuesForKeys:self->entityKeys];
81 count = [self->entityKeys count];
82 values = [NSMutableDictionary dictionaryWithCapacity:count];
84 for (i = 0; i < count; i++) {
85 NSString *key, *newKey;
88 key = [self->mappedKeys objectAtIndex:i];
89 newKey = [self->entityKeys objectAtIndex:i];
91 value = [_object valueForKey:key];
92 if (value) [(NSMutableDictionary *)values setObject:value forKey:newKey];
96 return [[[EOMappedObject alloc]
97 initWithObject:_object values:values] autorelease];
100 - (id)mapToSourceObject:(id)_object {
102 if (_object == nil) return nil;
104 if ([_object isKindOfClass:[EOMappedObject class]]) {
107 if ((obj = [_object mappedObject]) == nil) {
108 NSLog(@"don't know how to back-map objects: %@", _object);
112 if ([obj isModified]) {
114 NSLog(@"%s: don't know how to back-map modified object: %@",
116 #if NeXT_Foundation_LIBRARY
117 [self doesNotRecognizeSelector:_cmd];
118 return nil; // keep compiler happy
120 return [self notImplemented:_cmd];
124 [obj applyChangesOnObject];
129 NSLog(@"don't know how to back-map objects of class %@", [_object class]);
130 #if NeXT_Foundation_LIBRARY
131 [self doesNotRecognizeSelector:_cmd];
133 return [self notImplemented:_cmd];
136 return nil; // keep compiler happy
139 - (id)mapCreatedObject:(id)_object {
140 return [self mapFromSourceObject:_object];
143 - (id)mapObjectForUpdate:(id)_object {
144 return [self mapToSourceObject:_object];
146 - (id)mapObjectForInsert:(id)_object {
147 return [self mapToSourceObject:_object];
149 - (id)mapObjectForDelete:(id)_object {
150 return [self mapToSourceObject:_object];
153 - (id)mapFetchedObject:(id)_object {
154 return [self mapFromSourceObject:_object];
157 - (void)setClassDescriptionForObjects:(NSClassDescription *)_cd {
158 ASSIGN(self->classDescription, _cd);
160 /* setup array of keys to map */
162 [self->entityKeys release]; self->entityKeys = nil;
163 [self->mappedKeys release]; self->mappedKeys = nil;
170 ma = [[NSMutableArray alloc] initWithCapacity:16];
172 /* first, collect keys we need */
174 if ((tmp = [_cd attributeKeys]) != nil)
175 [ma addObjectsFromArray:tmp];
176 if ((tmp = [_cd toOneRelationshipKeys]) != nil)
177 [ma addObjectsFromArray:tmp];
178 if ((tmp = [_cd toManyRelationshipKeys]) != nil)
179 [ma addObjectsFromArray:tmp];
181 self->entityKeys = [ma copy];
183 /* next, map those keys to the source-schema */
185 if (self->map != nil) {
186 [ma removeAllObjects];
187 for (i = 0, count = [entityKeys count]; i < count; i++) {
188 NSString *mappedKey, *key;
190 key = [entityKeys objectAtIndex:i];
191 mappedKey = [self->map valueForKey:key];
192 [ma addObject:mappedKey ? mappedKey : key];
195 self->mappedKeys = [ma copy];
201 - (NSClassDescription *)classDescriptionForObjects {
202 return self->classDescription;
207 - (void)setSource:(EODataSource *)_source {
208 NSAssert(self->fspec == nil, @"only allowed as long as no spec is set !");
210 [self _removeObserverForSource:self->source];
211 ASSIGN(self->source, _source);
212 [self _registerForSource:self->source];
214 [self postDataSourceChangedNotification];
216 - (EODataSource *)source {
220 - (void)setFetchSpecification:(EOFetchSpecification *)_fetchSpec {
221 EOFetchSpecification *mappedSpec;
223 if ([_fetchSpec isEqual:self->fspec])
227 This saves the spec in the datasource and saves a mapped spec
228 in the source datasource.
231 ASSIGN(self->fspec, _fetchSpec);
232 mappedSpec = [self mapFetchSpecification:self->fspec];
233 [self->source setFetchSpecification:mappedSpec];
235 [self postDataSourceChangedNotification];
237 - (EOFetchSpecification *)fetchSpecification {
241 - (NSException *)lastException {
242 if ([self->source respondsToSelector:@selector(lastException)])
243 return [(id)self->source lastException];
247 /* fetch operations */
249 - (Class)fetchEnumeratorClass {
250 return [EOKeyMapDataSourceEnumerator class];
253 - (NSEnumerator *)fetchEnumerator {
256 if ((e = [self->source fetchEnumerator]) == nil)
259 e = [[[self fetchEnumeratorClass] alloc] initWithKeyMapDataSource:self
261 return [e autorelease];
264 - (NSArray *)fetchObjects {
265 NSAutoreleasePool *pool;
268 pool = [[NSArray alloc] init];
269 a = [[NSArray alloc] initWithObjectsFromEnumerator:[self fetchEnumerator]];
271 return [a autorelease];
276 - (void)insertObject:(id)_obj {
277 [self->source insertObject:[self mapObjectForInsert:_obj]];
280 - (void)deleteObject:(id)_obj {
281 [self->source deleteObject:[self mapObjectForDelete:_obj]];
285 return [self mapCreatedObject:[self->source createObject]];
287 - (void)updateObject:(id)_obj {
288 [self->source updateObject:[self mapObjectForUpdate:_obj]];
292 if ([self->source respondsToSelector:@selector(clear)])
293 [(id)self->source clear];
298 - (NSString *)description {
301 fmt = [NSString stringWithFormat:@"<%@[0x%p]: source=%@ map=%@>",
302 NSStringFromClass([self class]), self,
303 self->source, self->map];
309 - (void)_registerForSource:(id)_source {
310 static NSNotificationCenter *nc = nil;
312 if (_source != nil) {
314 nc = [[NSNotificationCenter defaultCenter] retain];
316 [nc addObserver:self selector:@selector(_sourceChanged)
317 name:EODataSourceDidChangeNotification object:_source];
321 - (void)_removeObserverForSource:(id)_source {
322 static NSNotificationCenter *nc = nil;
324 if (_source != nil) {
326 nc = [NSNotificationCenter defaultCenter];
327 [nc removeObserver:self name:EODataSourceDidChangeNotification
332 - (void)_sourceChanged {
333 [self postDataSourceChangedNotification];
336 @end /* EOKeyMapDataSource */
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 */
375 @implementation EOMappedObject
377 - (id)initWithObject:(id)_object values:(NSDictionary *)_values {
378 if ((self = [super init])) {
379 self->original = [_object retain];
380 self->values = [_values mutableCopy];
386 [self->original release];
387 [self->globalID release];
388 [self->values release];
395 return self->original;
397 - (EOGlobalID *)globalID {
398 if (self->globalID == nil) {
399 if ([self->original respondsToSelector:@selector(globalID)])
400 self->globalID = [[self->original globalID] retain];
402 return self->globalID;
406 return self->flags.didChange ? YES : NO;
409 self->flags.didChange = 1;
412 - (void)applyChangesOnObject {
413 if (!self->flags.didChange)
414 [self->original takeValuesFromDictionary:self->values];
417 /* mimic dictionary */
419 - (void)setObject:(id)_obj forKey:(id)_key {
421 [self->values setObject:_obj forKey:_key];
423 - (id)objectForKey:(id)_key {
424 return [self->values objectForKey:_key];
427 - (void)removeObjectForKey:(id)_key {
429 [self->values removeObjectForKey:_key];
432 - (NSEnumerator *)keyEnumerator {
433 return [self->values keyEnumerator];
435 - (NSEnumerator *)objectEnumerator {
436 return [self->values objectEnumerator];
439 - (NSDictionary *)asDictionary {
445 - (void)takeValue:(id)_value forKey:(NSString *)_key {
447 if ([_value isNotNull])
448 [self->values setObject:_value forKey:_key];
450 [self->values removeObjectForKey:_key];
453 - (id)valueForKey:(NSString *)_key {
454 return [self->values objectForKey:_key];
457 @end /* EOMappedObject */