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