]> err.no Git - sope/blob - sope-core/EOControl/EOClassDescription.m
Drop apache 1 build-dependency
[sope] / sope-core / EOControl / EOClassDescription.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 "EOClassDescription.h"
23 #include "EOKeyValueCoding.h"
24 #include "EONull.h"
25 #include "common.h"
26
27 @implementation NSClassDescription(EOClassDescription)
28
29 /* model */
30
31 - (NSString *)entityName {
32   return nil;
33 }
34 - (NSString *)inverseForRelationshipKey:(NSString *)_key {
35   return nil;
36 }
37
38 - (NSClassDescription *)classDescriptionForDestinationKey:(NSString *)_key {
39   return nil;
40 }
41
42 /* object initialization */
43
44 - (id)createInstanceWithEditingContext:(id)_ec
45   globalID:(EOGlobalID *)_oid
46   zone:(NSZone *)_zone
47 {
48   return nil;
49 }
50
51 - (void)awakeObject:(id)_object
52   fromFetchInEditingContext:(id)_ec
53 {
54 }
55 - (void)awakeObject:(id)_object
56   fromInsertionInEditingContext:(id)_ec
57 {
58 }
59
60 /* formatting */
61
62 - (NSFormatter *)defaultFormatterForKey:(NSString *)_key {
63   return nil;
64 }
65 - (NSFormatter *)defaultFormatterForKeyPath:(NSString *)_keyPath {
66   return nil;
67 }
68
69 /* delete */
70
71 - (void)propagateDeleteForObject:(id)_object editingContext:(id)_ec {
72 }
73
74 @end /* NSClassDescription(EOClassDescription) */
75
76 @implementation EOClassDescription
77
78 // THREAD
79 static NSMapTable *entityToDesc = NULL;
80 static NSMapTable *classToDesc  = NULL;
81
82 + (void)initialize {
83   if (entityToDesc == NULL) {
84     entityToDesc = NSCreateMapTable(NSObjectMapKeyCallBacks,
85                                     NSObjectMapValueCallBacks,
86                                     32);
87   }
88   if (classToDesc == NULL) {
89     classToDesc = NSCreateMapTable(NSObjectMapKeyCallBacks,
90                                    NSObjectMapValueCallBacks,
91                                    32);
92   }
93 }
94
95 + (NSClassDescription *)classDescriptionForClass:(Class)_class
96 {
97   EOClassDescription *d;
98
99 #if DEBUG
100   NSAssert(_class != [EOGlobalID class],
101            @"classDescriptionForClass:EOGlobalID ???");
102 #endif
103   
104   if ((d = NSMapGet(classToDesc, _class)))
105     return d;
106   
107   [[NSNotificationCenter defaultCenter]
108                          postNotificationName:
109                            @"EOClassDescriptionNeededForClass"
110                          object:_class];
111   
112   return NSMapGet(classToDesc, _class);
113 }
114
115 + (NSClassDescription *)classDescriptionForEntityName:(NSString *)_entityName {
116   NSClassDescription *d;
117   
118   if ((d = NSMapGet(entityToDesc, _entityName)))
119     return d;
120
121   [[NSNotificationCenter defaultCenter]
122                          postNotificationName:
123                            @"EOClassDescriptionNeededForEntityName"
124                          object:_entityName];
125   
126   return NSMapGet(entityToDesc, _entityName);
127 }
128
129 + (void)invalidateClassDescriptionCache {
130   NSResetMapTable(entityToDesc);
131   NSResetMapTable(classToDesc);
132 }
133
134 + (void)registerClassDescription:(NSClassDescription *)_clazzDesc
135   forClass:(Class)_class
136 {
137   NSString *entityName;
138   
139   if (_clazzDesc == nil)
140     return;
141   
142   if (_class)
143     NSMapInsert(classToDesc,  _class, _clazzDesc);
144   
145   if ((entityName = [_clazzDesc entityName]))
146     NSMapInsert(entityToDesc, entityName, _clazzDesc);
147 }
148
149 /* description */
150
151 - (NSString *)description {
152   return [NSString stringWithFormat:@"<%@[0x%p]: entity=%@>",
153                      NSStringFromClass([self class]), self,
154                      [self entityName]];
155 }
156
157 @end /* EOClassDescription */
158
159 @implementation NSObject(EOClassDescription)
160
161 - (NSClassDescription *)classDescriptionForDestinationKey:(NSString *)_key {
162   return [[self classDescription] classDescriptionForDestinationKey:_key];
163 }
164
165 /* object initialization */
166
167 - (id)initWithEditingContext:(id)_ec
168   classDescription:(NSClassDescription *)_classDesc
169   globalID:(EOGlobalID *)_oid
170 {
171   return [self init];
172 }
173
174 - (void)awakeFromFetchInEditingContext:(id)_ec {
175   [[self classDescription]
176          awakeObject:self fromFetchInEditingContext:_ec];
177 }
178 - (void)awakeFromInsertionInEditingContext:(id)_ec {
179   [[self classDescription]
180          awakeObject:self fromInsertionInEditingContext:_ec];
181 }
182
183 /* model */
184
185 - (NSString *)entityName {
186   return [[self classDescription] entityName];
187 }
188 - (NSString *)inverseForRelationshipKey:(NSString *)_key {
189   return [[self classDescription] inverseForRelationshipKey:_key];
190 }
191 - (NSArray *)attributeKeys {
192   return [[self classDescription] attributeKeys];
193 }
194
195 - (BOOL)isToManyKey:(NSString *)_key {
196   return [[self toManyRelationshipKeys] containsObject:_key];
197 }
198 - (NSArray *)allPropertyKeys {
199   NSArray *attrs;
200
201   attrs = [self attributeKeys];
202   attrs = attrs
203     ? [attrs arrayByAddingObjectsFromArray:[self toOneRelationshipKeys]]
204     : [self toOneRelationshipKeys];
205   attrs = attrs
206     ? [attrs arrayByAddingObjectsFromArray:[self toManyRelationshipKeys]]
207     : [self toManyRelationshipKeys];
208
209   return attrs;
210 }
211
212 /* delete */
213
214 - (void)propagateDeleteWithEditingContext:(id)_ec {
215   [[self classDescription] propagateDeleteForObject:self editingContext:_ec];
216 }
217
218 @end /* NSObject(EOClassDescription) */
219
220 @implementation NSException(EOValidation)
221
222 + (NSException *)aggregateExceptionWithExceptions:(NSArray *)_exceptions {
223   NSException *e;
224
225   e = [[self alloc] initWithName:@"EOAggregateException"
226                     reason:@"several exceptions occured"
227                     userInfo:
228                       [NSDictionary dictionaryWithObject:_exceptions
229                                     forKey:@"exceptions"]];
230   return [e autorelease];
231 }
232
233 @end /* NSException(EOValidation) */
234
235 /* snapshots */
236
237 @implementation NSObject(EOSnapshots)
238
239 - (NSDictionary *)snapshot {
240   static NSNull *null = nil;
241   NSMutableDictionary *d;
242   NSDictionary *r;
243   NSEnumerator *e;
244   NSString *key;
245
246   if (null == nil) null = [NSNull null];
247   
248   d = [[NSMutableDictionary alloc] initWithCapacity:64];
249   
250   e = [[self attributeKeys] objectEnumerator];
251   while ((key = [e nextObject])) {
252     id value;
253
254     value = [self valueForKey:key];
255     value = (value == nil) ? (id)[null retain] : (id)[value copy];
256     [d setObject:value forKey:key];
257     [value release]; value = nil;
258   }
259   
260   e = [[self toOneRelationshipKeys] objectEnumerator];
261   while ((key = [e nextObject])) {
262     id value;
263
264     value = [self valueForKey:key];
265     if (value == nil) value = [NSNull null];
266
267     [d setObject:value forKey:key];
268   }
269   
270   e = [[self toManyRelationshipKeys] objectEnumerator];
271   while ((key = [e nextObject])) {
272     id value;
273
274     value = [self valueForKey:key];
275     if (value == nil) {
276       value = [[NSNull null] retain];
277     }
278     else {
279       value = [value shallowCopy];
280     }
281     [d setObject:value forKey:key];
282     [value release]; value = nil;
283   }
284   
285   r = [d copy];
286   [d release]; d = nil;
287   return [r autorelease];
288 }
289
290 - (void)updateFromSnapshot:(NSDictionary *)_snapshot {
291   [self takeValuesFromDictionary:_snapshot];
292 }
293
294 - (NSDictionary *)changesFromSnapshot:(NSDictionary *)_snapshot {
295   /* not really correct, need to work on relationships */
296   static NSNull *null = nil;
297   NSMutableDictionary *diff;
298   NSEnumerator *props;
299   NSString     *key;
300   
301   if (null == nil) null = [NSNull null];
302   
303   diff = [NSMutableDictionary dictionaryWithCapacity:32];
304   
305   props = [[self allPropertyKeys] objectEnumerator];
306   while ((key = [props nextObject])) {
307     id value;
308     id svalue;
309     
310     value  = [self valueForKey:key];
311     svalue = [_snapshot objectForKey:key];
312     if (value  == nil) value  = null;
313     if (svalue == nil) svalue = null;
314
315     if (svalue != value) {
316       /* difference */
317       if ([self isToManyKey:key]) {
318         id adiff[2];
319
320         adiff[0] = [NSArray array];
321         adiff[1] = [NSArray array];
322
323         /* to be completed: calc real diff */
324         
325         [diff setObject:[NSArray arrayWithObjects:adiff count:2] forKey:key];
326       }
327       else
328         [diff setObject:value forKey:key];
329     }
330   }
331   
332   return diff;
333 }
334
335 - (void)reapplyChangesFromDictionary:(NSDictionary *)_changes {
336   /* not really correct, need to work on relationships */
337   NSEnumerator *keys;
338   NSString *key;
339
340   keys = [_changes keyEnumerator];
341   while ((key = [keys nextObject])) {
342     id value;
343     
344     value = [_changes objectForKey:key];
345
346     if ([self isToManyKey:key]) {
347       NSArray        *added;
348       NSArray        *deleted;
349       NSMutableArray *current;
350
351       added   = [value objectAtIndex:0];
352       deleted = [value objectAtIndex:1];
353       current = [[self valueForKey:key] mutableCopy];
354       
355       if (added)   [current addObjectsFromArray:added];
356       if (deleted) [current removeObjectsInArray:deleted];
357       
358       [current release]; current = nil;
359     }
360     else
361       [self takeValue:value forKey:key];
362     
363     [self takeValuesFromDictionary:_changes];
364   }
365 }
366
367 @end /* NSObject(EOSnapshots) */
368
369 /* relationships */
370
371 @implementation NSObject(EORelationshipManipulation)
372
373 - (void)addObject:(id)_o toBothSidesOfRelationshipWithKey:(NSString *)_key {
374   NSString *revKey;
375   BOOL     isToMany;
376   
377   revKey = [self inverseForRelationshipKey:_key];
378   isToMany = [self isToManyKey:_key];
379   
380   self = [[self retain] autorelease];
381   _o   = [[_o retain] autorelease];
382   
383   if (isToMany) {
384     /* watch out, likely to be buggy ! */
385     [self addObject:_o toPropertyWithKey:_key];
386   }
387   else
388     [self takeValue:_o forKey:_key];
389
390   if (revKey) {
391     /* add to the reverse object */
392     BOOL revIsToMany;
393
394     revIsToMany = [_o isToManyKey:revKey];
395     
396     if (revIsToMany)
397       [_o addObject:self toPropertyWithKey:revKey];
398     else
399       [_o takeValue:self forKey:revKey];
400   }
401 }
402 - (void)removeObject:(id)_o fromBothSidesOfRelationshipWithKey:(NSString *)_key {
403   NSString *revKey;
404   BOOL isToMany;
405
406   revKey   = [self inverseForRelationshipKey:_key];
407   isToMany = [self isToManyKey:_key];
408   
409   self = [[self retain] autorelease];
410   _o   = [[_o   retain] autorelease];
411
412   /* remove from this object */
413   
414   if (isToMany)
415     [self removeObject:_o fromPropertyWithKey:_key];
416   else
417     [self takeValue:nil forKey:_key];
418   
419   if (revKey) {
420     /* remove from reverse object */
421     BOOL revIsToMany;
422
423     revIsToMany = [_o isToManyKey:revKey];
424     
425     if (revIsToMany)
426       [_o removeObject:self fromPropertyWithKey:revKey];
427     else
428       [_o takeValue:nil forKey:revKey];
429   }
430 }
431
432 - (void)addObject:(id)_object toPropertyWithKey:(NSString *)_key {
433   NSString *selname;
434   SEL      sel;
435
436   selname = [@"addTo" stringByAppendingString:[_key capitalizedString]];
437   sel = NSSelectorFromString(selname);
438
439   if ([self respondsToSelector:sel])
440     [self performSelector:sel withObject:_object];
441   else {
442     id v;
443
444     v = [self valueForKey:_key];
445
446     if ([self isToManyKey:_key]) {
447       /* to-many relationship */
448       if (v == nil) {
449         [self takeValue:[NSArray arrayWithObject:_object] forKey:_key];
450       }
451       else if (![v containsObject:_object]) {
452         if ([v respondsToSelector:@selector(addObject:)])
453           [v addObject:_object];
454         else {
455           v = [v arrayByAddingObject:_object];
456           [self takeValue:v forKey:_key];
457         }
458       }
459     }
460     else {
461       /* to-one relationship */
462       if (v != _object)
463         [self takeValue:v forKey:_key];
464     }
465   }
466 }
467 - (void)removeObject:(id)_object fromPropertyWithKey:(NSString *)_key {
468   NSString *selname;
469   SEL      sel;
470
471   selname = [@"removeFrom" stringByAppendingString:[_key capitalizedString]];
472   sel = NSSelectorFromString(selname);
473   
474   if ([self respondsToSelector:sel])
475     [self performSelector:sel withObject:_object];
476   else {
477     id v;
478
479     v = [self valueForKey:_key];
480     
481     if ([self isToManyKey:_key]) {
482       /* to-many relationship */
483       if (v == nil) {
484         /* do nothing */
485       }
486       else if (![v containsObject:_object]) {
487         if ([v respondsToSelector:@selector(addObject:)])
488           [v removeObject:_object];
489         else {
490           v = [v mutableCopy];
491           [v removeObject:_object];
492           [self takeValue:v forKey:_key];
493           [v release]; v = nil;
494         }
495       }
496     }
497     else {
498       /* to-one relationship */
499       [self takeValue:nil forKey:_key];
500     }
501   }
502 }
503
504 @end /* NSObject(EORelationshipManipulation) */
505
506 /* shallow array copying */
507
508 @implementation NSArray(ShallowCopy)
509
510 - (id)shallowCopy {
511   NSArray *a;
512   unsigned i, cc;
513   id *objects;
514
515   cc = [self count];
516   objects = calloc(cc + 1, sizeof(id));
517   
518   for (i = 0; i < cc; i++)
519     objects[i] = [self objectAtIndex:i];
520
521   a = [[NSArray alloc] initWithObjects:objects count:cc];
522   
523   if (objects) free(objects);
524   
525   return a;
526 }
527
528 @end /* NSArray(ShallowCopy) */