]> err.no Git - sope/blob - sope-xml/SaxObjC/SaxObjectModel.m
use Version file for install locations
[sope] / sope-xml / SaxObjC / SaxObjectModel.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
22 #include "SaxObjectModel.h"
23 #include "common.h"
24
25 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
26     APPLE_FOUNDATION_LIBRARY
27 bool _CFArrayIsMutable(CFArrayRef dict);
28 #endif
29
30 static NSDictionary *mapDictsToObjects(NSDictionary *_dict, Class clazz) {
31   NSMutableDictionary *md;
32   NSEnumerator *e;
33   NSString     *key;
34   
35   md = [NSMutableDictionary dictionaryWithCapacity:16];
36   
37   e = [_dict keyEnumerator];
38   while ((key = [e nextObject])) {
39     id obj;
40
41     obj = [[clazz alloc] initWithDictionary:[_dict objectForKey:key]];
42     [md setObject:obj forKey:key];
43     [obj release];
44   }
45   return md;
46 }
47
48 @implementation SaxObjectModel
49
50 static BOOL    doDebug = NO;
51 static NSArray *searchPathes = nil;
52
53 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
54     APPLE_FOUNDATION_LIBRARY
55 static Class NSCFArrayClass = Nil;
56
57 + (void)initialize {
58   static BOOL isInitialized = NO;
59   
60   if (isInitialized) return;
61   isInitialized = YES;
62     NSCFArrayClass = NSClassFromString(@"NSCFArray");
63 }
64 #endif
65
66 + (NSArray *)saxMappingSearchPathes {
67   if (searchPathes == nil) {
68     NSMutableArray *ma;
69     NSDictionary   *env;
70     id tmp;
71     
72     env = [[NSProcessInfo processInfo] environment];
73     ma  = [NSMutableArray arrayWithCapacity:6];
74
75 #if COCOA_Foundation_LIBRARY
76     tmp = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
77                                               NSAllDomainsMask,
78                                               YES);
79     if ([tmp count] > 0) {
80       NSEnumerator *e;
81       
82       e = [tmp objectEnumerator];
83       while ((tmp = [e nextObject])) {
84         tmp = [tmp stringByAppendingPathComponent:@"SaxMappings"];
85         if (![ma containsObject:tmp])
86           [ma addObject:tmp];
87       }
88     }
89 #else
90     if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
91       tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
92     tmp = [tmp componentsSeparatedByString:@":"];
93     if ([tmp count] > 0) {
94       NSEnumerator *e;
95       
96       e = [tmp objectEnumerator];
97       while ((tmp = [e nextObject])) {
98         tmp = [tmp stringByAppendingPathComponent:@"Library/SaxMappings"];
99         if (![ma containsObject:tmp])
100           [ma addObject:tmp];
101       }
102     }
103 #endif
104     
105     /* FHS fallback */
106     [ma addObject:@"/usr/local/share/sope-4.3/saxmappings/"];
107     [ma addObject:@"/usr/share/sope-4.3/saxmappings/"];
108     searchPathes = [ma copy];
109     
110     if ([searchPathes count] == 0)
111       NSLog(@"%s: no search pathes were found!", __PRETTY_FUNCTION__);
112   }
113   return searchPathes;
114 }
115
116 + (id)modelWithName:(NSString *)_name {
117   NSFileManager *fileManager;
118   NSEnumerator  *pathes;
119   NSString      *path;
120
121   /* first look in main bundle */
122   
123   if ((path = [[NSBundle mainBundle] pathForResource:_name ofType:@"xmap"]))
124     return [self modelWithContentsOfFile:path];
125
126   /* then in Library */
127   
128   fileManager = [NSFileManager defaultManager];
129   pathes      = [[[self class] saxMappingSearchPathes] objectEnumerator];
130   _name       = [_name stringByAppendingPathExtension:@"xmap"];
131   
132   while ((path = [pathes nextObject])) {
133     BOOL isDir;
134     
135     path = [path stringByAppendingPathComponent:_name];
136     
137     if (![fileManager fileExistsAtPath:path isDirectory:&isDir])
138       continue;
139     if (isDir)
140       continue;
141     
142     break;
143   }
144   
145   return [self modelWithContentsOfFile:path];
146 }
147
148 + (id)modelWithContentsOfFile:(NSString *)_path {
149   NSDictionary *dict;
150   
151   if ((dict = [NSDictionary dictionaryWithContentsOfFile:_path]) == nil)
152     return nil;
153   
154   return [[[self alloc] initWithDictionary:dict] autorelease];
155 }
156
157 - (id)initWithDictionary:(NSDictionary *)_dict {
158   self->nsToModel =
159     [mapDictsToObjects(_dict, [SaxNamespaceModel class]) retain];
160   return self;
161 }
162
163 - (void)dealloc {
164   [self->nsToModel release];
165   [super dealloc];
166 }
167
168 /* queries */
169
170 - (SaxTagModel *)modelForTag:(NSString *)_localName namespace:(NSString *)_ns {
171   SaxNamespaceModel *nsmap;
172   
173   if ((nsmap = [self->nsToModel objectForKey:_ns]) == nil) {
174     if ((nsmap = [self->nsToModel objectForKey:@"*"]) == nil)
175       return nil;
176   }
177   return [nsmap modelForTag:_localName];
178 }
179
180 /* faking dictionary */
181
182 - (id)objectForKey:(id)_key {
183   return [self->nsToModel objectForKey:_key];
184 }
185
186 @end /* SaxMappingModel */
187
188 @implementation SaxNamespaceModel
189
190 - (id)initWithDictionary:(NSDictionary *)_dict {
191   self->tagToModel = [mapDictsToObjects(_dict, [SaxTagModel class]) retain];
192   return self;
193 }
194
195 - (void)dealloc {
196   [self->tagToModel release];
197   [super dealloc];
198 }
199
200 /* queries */
201
202 - (SaxTagModel *)modelForTag:(NSString *)_localName {
203   SaxTagModel *map;
204   
205   if ((map = [self->tagToModel objectForKey:_localName]))
206     return map;
207   if ((map = [self->tagToModel objectForKey:@"*"]))
208     return map;
209   return nil;
210 }
211
212 /* faking dictionary */
213
214 - (id)objectForKey:(id)_key {
215   return [self->tagToModel objectForKey:_key];
216 }
217
218 @end /* SaxNamespaceModel */
219
220 @implementation SaxTagModel
221
222 - (NSDictionary *)_extractAttributeMapping:(NSDictionary *)as {
223   NSMutableDictionary *md;
224   NSEnumerator *keys;
225   NSString     *k;
226   NSDictionary *result;
227       
228   md = [[NSMutableDictionary alloc] initWithCapacity:16];
229       
230   keys = [as keyEnumerator];
231   while ((k = [keys nextObject])) {
232     id val;
233         
234     val = [as objectForKey:k];
235         
236     if ([val isKindOfClass:[NSString class]])
237       [md setObject:val forKey:k];
238     else if ([val count] == 0)
239       [md setObject:k forKey:k];
240     else 
241       [md setObject:[(NSDictionary *)val objectForKey:@"key"] forKey:k];
242   }
243   
244   result = [md copy];
245   [md release];
246   return result;
247 }
248
249 - (id)initWithDictionary:(NSDictionary *)_dict {
250   if ((self = [super init])) {
251     NSDictionary *rels;
252     NSDictionary *as;
253     
254     self->className     = [[_dict objectForKey:@"class"]  copy];
255     self->key           = [[_dict objectForKey:@"key"]    copy];
256     self->tagKey        = [[_dict objectForKey:@"tagKey"] copy];
257     self->namespaceKey  = [[_dict objectForKey:@"namespaceKey"] copy];
258     self->parentKey     = [[_dict objectForKey:@"parentKey"] copy];
259     self->contentKey    = [[_dict objectForKey:@"contentKey"] copy];
260     self->defaultValues = [[_dict objectForKey:@"defaultValues"] copy];
261     
262     if ((as = [_dict objectForKey:@"attributes"]))
263       self->attrToKey = [self _extractAttributeMapping:as];
264     
265     if ((rels = [_dict objectForKey:@"ToManyRelationships"])) {
266       NSMutableDictionary *md;
267       NSEnumerator *keys;
268       NSString *k;
269       
270       self->toManyRelationshipKeys = [[rels allKeys] copy];
271     
272       md = [[NSMutableDictionary alloc] initWithCapacity:16];
273       
274       keys = [self->toManyRelationshipKeys objectEnumerator];
275       while ((k = [keys nextObject])) {
276         id       tags;
277         NSString *tag;
278         
279         tags = [rels objectForKey:k];
280         if ([tags isKindOfClass:[NSString class]])
281           tags = [NSArray arrayWithObject:tags];
282         tags = [tags objectEnumerator];
283         
284         while ((tag = [tags nextObject])) {
285           NSString *t;
286           
287           if ((t = [md objectForKey:tag])) {
288             NSLog(@"SaxObjectModel: cannot map tag '%@' to key '%@', "
289                   @"it is already mapped to key '%@'.",
290                   tag, k, t);
291           }
292           else {
293             [md setObject:k forKey:tag];
294           }
295         }
296       }
297       self->tagToKey = [md copy];
298       [md release];
299     }
300     
301   }
302   return self;
303 }
304
305 - (void)dealloc {
306   [self->defaultValues          release];
307   [self->toManyRelationshipKeys release];
308   [self->tagToKey     release];
309   [self->className    release];
310   [self->tagKey       release];
311   [self->namespaceKey release];
312   [self->parentKey  release];
313   [self->contentKey release];
314   [self->key        release];
315   [self->attrToKey  release];
316   [super dealloc];
317 }
318
319 /* accessors */
320
321 - (NSString *)className {
322   return self->className;
323 }
324 - (NSString *)key {
325   return self->key;
326 }
327 - (NSString *)tagKey {
328   return self->tagKey;
329 }
330 - (NSString *)namespaceKey {
331   return self->namespaceKey;
332 }
333 - (NSString *)parentKey {
334   return self->parentKey;
335 }
336 - (NSString *)contentKey {
337   return self->contentKey;
338 }
339
340 - (NSDictionary *)defaultValues {
341   return self->defaultValues;
342 }
343
344 - (BOOL)isToManyKey:(NSString *)_key {
345   return [self->toManyRelationshipKeys containsObject:_key];
346 }
347 - (NSArray *)toManyRelationshipKeys {
348   return self->toManyRelationshipKeys;
349 }
350
351 - (BOOL)isToManyTag:(NSString *)_tag {
352   return ([self->tagToKey objectForKey:_tag] != nil) ? YES : NO;
353 }
354
355 - (NSString *)propertyKeyForChildTag:(NSString *)_tag {
356   return [self->tagToKey objectForKey:_tag];
357 }
358
359 - (NSArray *)attributeKeys {
360   return [self->attrToKey allKeys];
361 }
362 - (NSString *)propertyKeyForAttribute:(NSString *)_attr {
363   return [self->attrToKey objectForKey:_attr];
364 }
365
366 /* object operations */
367
368 - (void)addValue:(id)_value toPropertyWithKey:(NSString *)_key 
369   ofObject:(id)_object
370 {
371   NSString *selname;
372   SEL      sel;
373   
374   selname = [[NSString alloc] initWithFormat:@"addTo%@:", 
375                               [_key capitalizedString]];
376   if ((sel = NSSelectorFromString(selname)) == NULL) {
377     if (doDebug) {
378       NSLog(@"got no selector for key '%@', selname '%@' !",
379             _key, selname);
380     }
381   }
382   [selname release]; selname = nil;
383   
384   if (doDebug) {
385     NSLog(@"%s: adding value %@ to %@ of %@", __PRETTY_FUNCTION__,
386           _value, _key, _object);
387     NSLog(@"  selector: %@", NSStringFromSelector(sel));
388   }
389   
390   if ((sel != NULL) && [_object respondsToSelector:sel]) {
391     [_object performSelector:sel withObject:_value];
392   }
393   else {
394     id v;
395     
396     v = [_object valueForKey:_key];
397     
398     if ([self isToManyKey:_key]) {
399       /* to-many relationship */
400
401       if (v == nil) {
402         [_object takeValue:[NSArray arrayWithObject:_value] forKey:_key];
403       }
404       else {
405 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
406     APPLE_FOUNDATION_LIBRARY
407         if ([v isKindOfClass:NSCFArrayClass] &&
408             _CFArrayIsMutable((CFArrayRef)v))
409 #else
410         if ([v respondsToSelector:@selector(addObject:)])
411 #endif
412           /* the value is mutable */
413           [v addObject:_value];
414         else {
415           /* the value is immutable */
416           v = [v arrayByAddingObject:_value];
417           [_object takeValue:v forKey:_key];
418         }
419       }
420     }
421     else {
422       NSLog(@" APPLIED ON TO-ONE (%@) !", _key);
423       /* to-one relationship */
424       if (v != _value)
425         [_object takeValue:v forKey:_key];
426     }
427   }
428 }
429
430 @end /* SaxTagModel */