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