]> err.no Git - sope/blob - sope-xml/SaxObjC/SaxObjectModel.m
improved OSX framework support
[sope] / sope-xml / SaxObjC / SaxObjectModel.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 "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     {
107       NSString *p;
108       
109       p = [NSString stringWithFormat:@"share/sope-%i.%i/saxmappings/",
110                       SOPE_MAJOR_VERSION, SOPE_MINOR_VERSION];
111       [ma addObject:[@"/usr/local/" stringByAppendingString:p]];
112       [ma addObject:[@"/usr/"       stringByAppendingString:p]];
113     }
114     searchPathes = [ma copy];
115     
116     if ([searchPathes count] == 0)
117       NSLog(@"%s: no search pathes were found!", __PRETTY_FUNCTION__);
118   }
119   return searchPathes;
120 }
121
122 + (id)modelWithName:(NSString *)_name {
123   NSFileManager *fileManager;
124   NSEnumerator  *pathes;
125   NSString      *path;
126
127   /* first look in main bundle */
128   
129   if ((path = [[NSBundle mainBundle] pathForResource:_name ofType:@"xmap"]))
130     return [self modelWithContentsOfFile:path];
131
132   /* then in Library */
133   
134   fileManager = [NSFileManager defaultManager];
135   pathes      = [[[self class] saxMappingSearchPathes] objectEnumerator];
136   _name       = [_name stringByAppendingPathExtension:@"xmap"];
137   
138   while ((path = [pathes nextObject])) {
139     BOOL isDir;
140     
141     path = [path stringByAppendingPathComponent:_name];
142     
143     if (![fileManager fileExistsAtPath:path isDirectory:&isDir])
144       continue;
145     if (isDir)
146       continue;
147     
148     break;
149   }
150   
151   return [self modelWithContentsOfFile:path];
152 }
153
154 + (id)modelWithContentsOfFile:(NSString *)_path {
155   NSDictionary *dict;
156   
157   if ((dict = [NSDictionary dictionaryWithContentsOfFile:_path]) == nil)
158     return nil;
159   
160   return [[[self alloc] initWithDictionary:dict] autorelease];
161 }
162
163 - (id)initWithDictionary:(NSDictionary *)_dict {
164   self->nsToModel =
165     [mapDictsToObjects(_dict, [SaxNamespaceModel class]) retain];
166   return self;
167 }
168
169 - (void)dealloc {
170   [self->nsToModel release];
171   [super dealloc];
172 }
173
174 /* queries */
175
176 - (SaxTagModel *)modelForTag:(NSString *)_localName namespace:(NSString *)_ns {
177   SaxNamespaceModel *nsmap;
178   
179   if ((nsmap = [self->nsToModel objectForKey:_ns]) == nil) {
180     if ((nsmap = [self->nsToModel objectForKey:@"*"]) == nil)
181       return nil;
182   }
183   return [nsmap modelForTag:_localName];
184 }
185
186 /* faking dictionary */
187
188 - (id)objectForKey:(id)_key {
189   return [self->nsToModel objectForKey:_key];
190 }
191
192 @end /* SaxMappingModel */
193
194 @implementation SaxNamespaceModel
195
196 - (id)initWithDictionary:(NSDictionary *)_dict {
197   self->tagToModel = [mapDictsToObjects(_dict, [SaxTagModel class]) retain];
198   return self;
199 }
200
201 - (void)dealloc {
202   [self->tagToModel release];
203   [super dealloc];
204 }
205
206 /* queries */
207
208 - (SaxTagModel *)modelForTag:(NSString *)_localName {
209   SaxTagModel *map;
210   
211   if ((map = [self->tagToModel objectForKey:_localName]))
212     return map;
213   if ((map = [self->tagToModel objectForKey:@"*"]))
214     return map;
215   return nil;
216 }
217
218 /* faking dictionary */
219
220 - (id)objectForKey:(id)_key {
221   return [self->tagToModel objectForKey:_key];
222 }
223
224 @end /* SaxNamespaceModel */
225
226 @implementation SaxTagModel
227
228 - (NSDictionary *)_extractAttributeMapping:(NSDictionary *)as {
229   NSMutableDictionary *md;
230   NSEnumerator *keys;
231   NSString     *k;
232   NSDictionary *result;
233       
234   md = [[NSMutableDictionary alloc] initWithCapacity:16];
235       
236   keys = [as keyEnumerator];
237   while ((k = [keys nextObject])) {
238     id val;
239         
240     val = [as objectForKey:k];
241         
242     if ([val isKindOfClass:[NSString class]])
243       [md setObject:val forKey:k];
244     else if ([val count] == 0)
245       [md setObject:k forKey:k];
246     else 
247       [md setObject:[(NSDictionary *)val objectForKey:@"key"] forKey:k];
248   }
249   
250   result = [md copy];
251   [md release];
252   return result;
253 }
254
255 - (id)initWithDictionary:(NSDictionary *)_dict {
256   if ((self = [super init])) {
257     NSDictionary *rels;
258     NSDictionary *as;
259     
260     self->className     = [[_dict objectForKey:@"class"]  copy];
261     self->key           = [[_dict objectForKey:@"key"]    copy];
262     self->tagKey        = [[_dict objectForKey:@"tagKey"] copy];
263     self->namespaceKey  = [[_dict objectForKey:@"namespaceKey"] copy];
264     self->parentKey     = [[_dict objectForKey:@"parentKey"] copy];
265     self->contentKey    = [[_dict objectForKey:@"contentKey"] copy];
266     self->defaultValues = [[_dict objectForKey:@"defaultValues"] copy];
267     
268     if ((as = [_dict objectForKey:@"attributes"]))
269       self->attrToKey = [self _extractAttributeMapping:as];
270     
271     if ((rels = [_dict objectForKey:@"ToManyRelationships"])) {
272       NSMutableDictionary *md;
273       NSEnumerator *keys;
274       NSString *k;
275       
276       self->toManyRelationshipKeys = [[rels allKeys] copy];
277     
278       md = [[NSMutableDictionary alloc] initWithCapacity:16];
279       
280       keys = [self->toManyRelationshipKeys objectEnumerator];
281       while ((k = [keys nextObject])) {
282         id       tags;
283         NSString *tag;
284         
285         tags = [rels objectForKey:k];
286         if ([tags isKindOfClass:[NSString class]])
287           tags = [NSArray arrayWithObject:tags];
288         tags = [tags objectEnumerator];
289         
290         while ((tag = [tags nextObject])) {
291           NSString *t;
292           
293           if ((t = [md objectForKey:tag])) {
294             NSLog(@"SaxObjectModel: cannot map tag '%@' to key '%@', "
295                   @"it is already mapped to key '%@'.",
296                   tag, k, t);
297           }
298           else {
299             [md setObject:k forKey:tag];
300           }
301         }
302       }
303       self->tagToKey = [md copy];
304       [md release];
305     }
306     
307   }
308   return self;
309 }
310
311 - (void)dealloc {
312   [self->defaultValues          release];
313   [self->toManyRelationshipKeys release];
314   [self->tagToKey     release];
315   [self->className    release];
316   [self->tagKey       release];
317   [self->namespaceKey release];
318   [self->parentKey  release];
319   [self->contentKey release];
320   [self->key        release];
321   [self->attrToKey  release];
322   [super dealloc];
323 }
324
325 /* accessors */
326
327 - (NSString *)className {
328   return self->className;
329 }
330 - (NSString *)key {
331   return self->key;
332 }
333 - (NSString *)tagKey {
334   return self->tagKey;
335 }
336 - (NSString *)namespaceKey {
337   return self->namespaceKey;
338 }
339 - (NSString *)parentKey {
340   return self->parentKey;
341 }
342 - (NSString *)contentKey {
343   return self->contentKey;
344 }
345
346 - (NSDictionary *)defaultValues {
347   return self->defaultValues;
348 }
349
350 - (BOOL)isToManyKey:(NSString *)_key {
351   return [self->toManyRelationshipKeys containsObject:_key];
352 }
353 - (NSArray *)toManyRelationshipKeys {
354   return self->toManyRelationshipKeys;
355 }
356
357 - (BOOL)isToManyTag:(NSString *)_tag {
358   return ([self->tagToKey objectForKey:_tag] != nil) ? YES : NO;
359 }
360
361 - (NSString *)propertyKeyForChildTag:(NSString *)_tag {
362   return [self->tagToKey objectForKey:_tag];
363 }
364
365 - (NSArray *)attributeKeys {
366   return [self->attrToKey allKeys];
367 }
368 - (NSString *)propertyKeyForAttribute:(NSString *)_attr {
369   return [self->attrToKey objectForKey:_attr];
370 }
371
372 /* object operations */
373
374 - (void)addValue:(id)_value toPropertyWithKey:(NSString *)_key 
375   ofObject:(id)_object
376 {
377   NSString *selname;
378   SEL      sel;
379   
380   selname = [[NSString alloc] initWithFormat:@"addTo%@:", 
381                               [_key capitalizedString]];
382   if ((sel = NSSelectorFromString(selname)) == NULL) {
383     if (doDebug) {
384       NSLog(@"got no selector for key '%@', selname '%@' !",
385             _key, selname);
386     }
387   }
388   [selname release]; selname = nil;
389   
390   if (doDebug) {
391     NSLog(@"%s: adding value %@ to %@ of %@", __PRETTY_FUNCTION__,
392           _value, _key, _object);
393     NSLog(@"  selector: %@", NSStringFromSelector(sel));
394   }
395   
396   if ((sel != NULL) && [_object respondsToSelector:sel]) {
397     [_object performSelector:sel withObject:_value];
398   }
399   else {
400     id v;
401     
402     v = [_object valueForKey:_key];
403     
404     if ([self isToManyKey:_key]) {
405       /* to-many relationship */
406
407       if (v == nil) {
408         [_object takeValue:[NSArray arrayWithObject:_value] forKey:_key];
409       }
410       else {
411 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
412     APPLE_FOUNDATION_LIBRARY
413         if ([v isKindOfClass:NSCFArrayClass] &&
414             _CFArrayIsMutable((CFArrayRef)v))
415 #else
416         if ([v respondsToSelector:@selector(addObject:)])
417 #endif
418           /* the value is mutable */
419           [v addObject:_value];
420         else {
421           /* the value is immutable */
422           v = [v arrayByAddingObject:_value];
423           [_object takeValue:v forKey:_key];
424         }
425       }
426     }
427     else {
428       NSLog(@" APPLIED ON TO-ONE (%@) !", _key);
429       /* to-one relationship */
430       if (v != _value)
431         [_object takeValue:v forKey:_key];
432     }
433   }
434 }
435
436 @end /* SaxTagModel */