]> err.no Git - sope/blob - sope-core/NGExtensions/EOExt.subproj/EOKeyMapDataSource.m
Drop apache 1 build-dependency
[sope] / sope-core / NGExtensions / EOExt.subproj / EOKeyMapDataSource.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "EOKeyMapDataSource.h"
23 #include "NSArray+enumerator.h"
24 #include "EODataSource+NGExtensions.h"
25 #include "NSNull+misc.h"
26 #import <EOControl/EOControl.h>
27 #include "common.h"
28 #include <sys/time.h>
29
30 @interface EOKeyMapDataSource(Private)
31 - (void)_registerForSource:(id)_source;
32 - (void)_removeObserverForSource:(id)_source;
33 - (void)_sourceChanged;
34 @end
35
36 @implementation EOKeyMapDataSource
37
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];
43   }
44   return self;
45 }
46 - (id)initWithDataSource:(EODataSource *)_ds {
47   return [self initWithDataSource:_ds map:nil];
48 }
49 - (id)init {
50   return [self initWithDataSource:nil map:nil];
51 }
52
53 - (void)dealloc {
54   [self _removeObserverForSource:self->source];
55   [self->classDescription release];
56   [self->entityKeys release];
57   [self->mappedKeys release];
58   [self->map    release];
59   [self->fspec  release];
60   [self->source release];
61   [super dealloc];
62 }
63
64 /* mapping */
65
66 - (EOFetchSpecification *)mapFetchSpecification:(EOFetchSpecification *)_fs {
67   return [_fs fetchSpecificationByApplyingKeyMap:self->map];
68 }
69
70 - (id)mapFromSourceObject:(id)_object {
71   id values;
72   if (_object == nil) return nil;
73   
74   if (self->mappedKeys == nil) {
75     /* no need to rewrite keys, only taking a subset */
76     values = [_object valuesForKeys:self->entityKeys];
77   }
78   else {
79     unsigned i, count;
80     
81     count  = [self->entityKeys count];
82     values = [NSMutableDictionary dictionaryWithCapacity:count];
83     
84     for (i = 0; i < count; i++) {
85       NSString *key, *newKey;
86       id value;
87       
88       key    = [self->mappedKeys objectAtIndex:i];
89       newKey = [self->entityKeys objectAtIndex:i];
90       
91       value = [_object valueForKey:key];
92       if (value) [(NSMutableDictionary *)values setObject:value forKey:newKey];
93     }
94   }
95   
96   return [[[EOMappedObject alloc] 
97             initWithObject:_object values:values] autorelease];
98 }
99
100 - (id)mapToSourceObject:(id)_object {
101   // TODO
102   if (_object == nil) return nil;
103
104   if ([_object isKindOfClass:[EOMappedObject class]]) {
105     id obj;
106     
107     if ((obj = [_object mappedObject]) == nil) {
108       NSLog(@"don't know how to back-map objects: %@", _object);
109       return nil;
110     }
111     
112     if ([obj isModified]) {
113       if (self->map) {
114         NSLog(@"%s: don't know how to back-map modified object: %@", 
115               _object);
116 #if NeXT_Foundation_LIBRARY
117     [self doesNotRecognizeSelector:_cmd];
118     return nil; // keep compiler happy
119 #else
120     return [self notImplemented:_cmd];
121 #endif
122       }
123       
124       [obj applyChangesOnObject];
125     }    
126     return obj;
127   }
128   else {
129     NSLog(@"don't know how to back-map objects of class %@", [_object class]);
130 #if NeXT_Foundation_LIBRARY
131     [self doesNotRecognizeSelector:_cmd];
132 #else
133     return [self notImplemented:_cmd];
134 #endif
135   }
136   return nil; // keep compiler happy
137 }
138
139 - (id)mapCreatedObject:(id)_object {
140   return [self mapFromSourceObject:_object];
141 }
142
143 - (id)mapObjectForUpdate:(id)_object {
144   return [self mapToSourceObject:_object];
145 }
146 - (id)mapObjectForInsert:(id)_object {
147   return [self mapToSourceObject:_object];
148 }
149 - (id)mapObjectForDelete:(id)_object {
150   return [self mapToSourceObject:_object];
151 }
152
153 - (id)mapFetchedObject:(id)_object {
154   return [self mapFromSourceObject:_object];
155 }
156
157 - (void)setClassDescriptionForObjects:(NSClassDescription *)_cd {
158   ASSIGN(self->classDescription, _cd);
159   
160   /* setup array of keys to map */
161   
162   [self->entityKeys release]; self->entityKeys = nil;
163   [self->mappedKeys release]; self->mappedKeys = nil;
164   
165   if (_cd != nil) {
166     NSMutableArray *ma;
167     NSArray  *tmp;
168     unsigned i, count;
169     
170     ma = [[NSMutableArray alloc] initWithCapacity:16];
171     
172     /* first, collect keys we need */
173     
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];
180     
181     self->entityKeys = [ma copy];
182
183     /* next, map those keys to the source-schema */
184     
185     if (self->map != nil) {
186       [ma removeAllObjects];
187       for (i = 0, count = [entityKeys count]; i < count; i++) {
188         NSString *mappedKey, *key;
189         
190         key       = [entityKeys objectAtIndex:i];
191         mappedKey = [self->map valueForKey:key];
192         [ma addObject:mappedKey ? mappedKey : key];
193       }
194       
195       self->mappedKeys = [ma copy];
196     }
197     
198     [ma release];
199   }
200 }
201 - (NSClassDescription *)classDescriptionForObjects {
202   return self->classDescription;
203 }
204
205 /* accessors */
206
207 - (void)setSource:(EODataSource *)_source {
208   NSAssert(self->fspec == nil, @"only allowed as long as no spec is set !");
209   
210   [self _removeObserverForSource:self->source];
211   ASSIGN(self->source, _source);
212   [self _registerForSource:self->source];
213   
214   [self postDataSourceChangedNotification];
215 }
216 - (EODataSource *)source {
217   return self->source;
218 }
219
220 - (void)setFetchSpecification:(EOFetchSpecification *)_fetchSpec {
221   EOFetchSpecification *mappedSpec;
222   
223   if ([_fetchSpec isEqual:self->fspec])
224     return;
225
226   /*
227       This saves the spec in the datasource and saves a mapped spec
228       in the source datasource.
229   */
230     
231   ASSIGN(self->fspec, _fetchSpec);
232   mappedSpec = [self mapFetchSpecification:self->fspec];
233   [self->source setFetchSpecification:mappedSpec];
234     
235   [self postDataSourceChangedNotification];
236 }
237 - (EOFetchSpecification *)fetchSpecification {
238   return self->fspec;
239 }
240
241 - (NSException *)lastException {
242   if ([self->source respondsToSelector:@selector(lastException)])
243     return [(id)self->source lastException];
244   return nil;
245 }
246
247 /* fetch operations */
248
249 - (Class)fetchEnumeratorClass {
250   return [EOKeyMapDataSourceEnumerator class];
251 }
252
253 - (NSEnumerator *)fetchEnumerator {
254   NSEnumerator *e;
255   
256   if ((e = [self->source fetchEnumerator]) == nil)
257     return nil;
258   
259   e = [[[self fetchEnumeratorClass] alloc] initWithKeyMapDataSource:self
260                                            fetchEnumerator:e];
261   return [e autorelease];
262 }
263
264 - (NSArray *)fetchObjects {
265   NSAutoreleasePool *pool;
266   NSArray *a;
267   
268   pool = [[NSArray alloc] init];
269   a = [[NSArray alloc] initWithObjectsFromEnumerator:[self fetchEnumerator]];
270   [pool release];
271   return [a autorelease];
272 }
273
274 /* modifications */
275
276 - (void)insertObject:(id)_obj {
277   [self->source insertObject:[self mapObjectForInsert:_obj]];
278 }
279
280 - (void)deleteObject:(id)_obj {
281   [self->source deleteObject:[self mapObjectForDelete:_obj]];
282 }
283
284 - (id)createObject {
285   return [self mapCreatedObject:[self->source createObject]];
286 }
287 - (void)updateObject:(id)_obj {
288   [self->source updateObject:[self mapObjectForUpdate:_obj]];
289 }
290
291 - (void)clear {
292   if ([self->source respondsToSelector:@selector(clear)])
293     [(id)self->source clear];
294 }
295
296 /* description */
297
298 - (NSString *)description {
299   NSString *fmt;
300
301   fmt = [NSString stringWithFormat:@"<%@[0x%p]: source=%@ map=%@>",
302                     NSStringFromClass([self class]), self,
303                     self->source, self->map];
304   return fmt;
305 }
306
307 /* private */
308
309 - (void)_registerForSource:(id)_source {
310   static NSNotificationCenter *nc = nil;
311
312   if (_source != nil) {
313     if (nc == nil)
314       nc = [[NSNotificationCenter defaultCenter] retain];
315     
316     [nc addObserver:self selector:@selector(_sourceChanged)
317         name:EODataSourceDidChangeNotification object:_source];
318   }
319 }
320
321 - (void)_removeObserverForSource:(id)_source {
322   static NSNotificationCenter *nc = nil;
323
324   if (_source != nil) {
325     if (nc == nil)
326       nc = [NSNotificationCenter defaultCenter];
327     [nc removeObserver:self name:EODataSourceDidChangeNotification
328         object:_source];
329   }
330 }
331
332  - (void)_sourceChanged {
333   [self postDataSourceChangedNotification];
334 }
335
336 @end /* EOKeyMapDataSource */
337
338
339 @implementation EOKeyMapDataSourceEnumerator
340
341 - (id)initWithKeyMapDataSource:(EOKeyMapDataSource *)_ds
342   fetchEnumerator:(NSEnumerator *)_enum
343 {
344   if ((self = [super init])) {
345     self->ds     = [_ds retain];
346     self->source = [_enum retain];
347   }
348   return self;
349 }
350 - (void)dealloc {
351   [self->ds     release];
352   [self->source release];
353   [super dealloc];
354 }
355
356 /* fetching */
357
358 - (void)fetchDone {
359 }
360
361 - (id)nextObject {
362   id object;
363   
364   if ((object = [self->source nextObject]) == nil) {
365     [self fetchDone];
366     return nil;
367   }
368   
369   return [self->ds mapFetchedObject:object];
370 }
371
372 @end /* EOKeyMapDataSourceEnumerator */
373
374
375 @implementation EOMappedObject
376
377 - (id)initWithObject:(id)_object values:(NSDictionary *)_values {
378   if ((self = [super init])) {
379     self->original = [_object retain];
380     self->values   = [_values mutableCopy];
381   }
382   return self;
383 }
384
385 - (void)dealloc {
386   [self->original release];
387   [self->globalID release];
388   [self->values   release];
389   [super dealloc];
390 }
391
392 /* accessors */
393
394 - (id)mappedObject {
395   return self->original;
396 }
397 - (EOGlobalID *)globalID {
398   if (self->globalID == nil) {
399     if ([self->original respondsToSelector:@selector(globalID)])
400       self->globalID = [[self->original globalID] retain];
401   }
402   return self->globalID;
403 }
404
405 - (BOOL)isModified {
406   return self->flags.didChange ? YES : NO;
407 }
408 - (void)willChange {
409   self->flags.didChange = 1;
410 }
411
412 - (void)applyChangesOnObject {
413   if (!self->flags.didChange)
414     [self->original takeValuesFromDictionary:self->values];
415 }
416
417 /* mimic dictionary */
418
419 - (void)setObject:(id)_obj forKey:(id)_key {
420   [self willChange];
421   [self->values setObject:_obj forKey:_key];
422 }
423 - (id)objectForKey:(id)_key {
424   return [self->values objectForKey:_key];
425 }
426
427 - (void)removeObjectForKey:(id)_key {
428   [self willChange];
429   [self->values removeObjectForKey:_key];
430 }
431
432 - (NSEnumerator *)keyEnumerator {
433   return [self->values keyEnumerator];
434 }
435 - (NSEnumerator *)objectEnumerator {
436   return [self->values objectEnumerator];
437 }
438
439 - (NSDictionary *)asDictionary {
440   return self->values;
441 }
442
443 /* KVC */
444
445 - (void)takeValue:(id)_value forKey:(NSString *)_key {
446   [self willChange];
447   if ([_value isNotNull])
448     [self->values setObject:_value forKey:_key];
449   else
450     [self->values removeObjectForKey:_key];
451 }
452
453 - (id)valueForKey:(NSString *)_key {
454   return [self->values objectForKey:_key];
455 }
456
457 @end /* EOMappedObject */