]> err.no Git - sope/blob - sope-xml/SaxObjC/SaxObjectDecoder.m
added support for XML-RPC 'nil' type
[sope] / sope-xml / SaxObjC / SaxObjectDecoder.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 "SaxObjectDecoder.h"
23 #include "SaxObjectModel.h"
24 #include "common.h"
25
26 static BOOL debugOn = NO;
27
28 @interface _SaxObjTagInfo : NSObject
29 {
30 @public
31   SaxObjectDecoder *decoder;    /* non-retained */
32   _SaxObjTagInfo   *parentInfo; /* non-retained */
33   SaxTagModel *mapping;
34   NSString    *tagName;
35   NSString    *namespace;
36   NSException *error;
37   id          object;
38   struct {
39     int isRoot:1;
40     int isMutableDict:1;
41     int isString:1;
42     int isMutableString:1;
43     int hasContentKey:1;
44   } flags;
45   NSMutableString *collectedCharData;
46 }
47
48 /* accessors */
49
50 - (SaxTagModel *)mapping;
51 - (id)object;
52
53 /* tag handling */
54
55 - (void)start;
56 - (void)stop;
57
58 - (void)characters:(unichar *)_chars length:(int)_len;
59
60 @end
61
62 @implementation SaxObjectDecoder
63
64 static NSNull *null = nil;
65
66 + (void)initialize {
67   static BOOL    didInit = NO;
68   NSUserDefaults *ud;
69
70   if (didInit) return;
71   didInit = YES;
72
73   null    = [[NSNull null] retain];
74   ud      = [NSUserDefaults standardUserDefaults];
75   debugOn = [ud boolForKey:@"SaxObjectDecoderDebugEnabled"];
76 }
77
78 - (id)initWithMappingModel:(SaxObjectModel *)_model {
79   if ((self = [super init])) {
80     self->mapping = [_model retain];
81   }
82   return self;
83 }
84
85 - (id)initWithMappingAtPath:(NSString *)_path {
86   SaxObjectModel *model;
87
88   model = [SaxObjectModel modelWithContentsOfFile:_path];
89   return [self initWithMappingModel:model];
90 }
91 - (id)initWithMappingNamed:(NSString *)_name {
92   SaxObjectModel *model;
93   
94   model = [SaxObjectModel modelWithName:_name];
95   return [self initWithMappingModel:model];
96 }
97
98 - (id)init {
99   return [self initWithMappingModel:nil];
100 }
101
102 - (void)dealloc {
103   [self->locator      release];
104   [self->rootObject   release];
105   [self->mapping      release];
106
107   [self->infoStack    release];
108   [self->mappingStack release];
109   [self->objectStack  release];
110   [super dealloc];
111 }
112
113 /* parse results */
114
115 - (id)rootObject {
116   return self->rootObject;
117 }
118
119 /* cleanup */
120
121 - (void)parseReset {
122   NSAutoreleasePool *pool;
123   
124   pool = [[NSAutoreleasePool alloc] init];
125   [self->infoStack    removeAllObjects];
126   [self->mappingStack removeAllObjects];
127   [self->objectStack  removeAllObjects];
128   [pool release];
129 }
130 - (void)reset {
131   [self parseReset];
132   
133   [self->rootObject release]; 
134   self->rootObject = nil;
135 }
136
137 /* parsing */
138
139 - (void)startDocument {
140   [self reset];
141   
142   if (self->infoStack == nil)
143     self->infoStack = [[NSMutableArray alloc] initWithCapacity:16];
144 }
145
146 - (void)endDocument {
147   [self parseReset];
148 }
149
150 /* positioning info */
151
152 - (void)setDocumentLocator:(id<NSObject,SaxLocator>)_locator {
153   ASSIGN(self->locator, _locator);
154 }
155
156 /* stacks */
157
158 - (void)pushInfo:(_SaxObjTagInfo *)_info {
159   [self->infoStack addObject:_info];
160 }
161 - (void)popInfo {
162   [self->infoStack removeObjectAtIndex:([self->infoStack count] - 1)];
163 }
164 - (id)currentInfo {
165   return [self->infoStack lastObject];
166 }
167
168 /* elements */
169
170 - (NSException *)missingNamespaceMapping:(NSString *)_ns {
171   return [NSException exceptionWithName:@"MissingNamespaceMapping"
172                       reason:_ns
173                       userInfo:nil];
174 }
175 - (NSException *)missingElementMapping:(NSString *)_ns:(NSString *)_tag {
176   return [NSException exceptionWithName:@"MissingElementMapping"
177                       reason:_tag
178                       userInfo:nil];
179 }
180 - (NSException *)missingMappedClass:(NSString *)_className {
181   return [NSException exceptionWithName:@"MissingMappedClass"
182                       reason:_className
183                       userInfo:nil];
184 }
185
186 - (SaxTagModel *)mappingForTag:(NSString *)_tag namespace:(NSString *)_ns {
187   return [self->mapping modelForTag:_tag namespace:_ns];
188 }
189
190 - (void)couldNotApplyAttribute:(NSString *)_attr asKey:(NSString *)_key
191   onObject:(id)_object
192 {
193   NSLog(@"SaxObjectDecoder: could not apply attribute '%@' (key=%@) "
194         @"on object %@",
195         _attr, _key, _object);
196 }
197
198 - (void)startElement:(NSString *)_localName
199   namespace:(NSString *)_ns
200   rawName:(NSString *)_rawName
201   attributes:(id<SaxAttributes>)_attributes
202 {
203   _SaxObjTagInfo *info;
204   
205   info = [_SaxObjTagInfo alloc];
206   info->decoder      = self;
207   info->flags.isRoot = [self->infoStack count] == 0 ? 1 : 0;
208   info->parentInfo   = info->flags.isRoot ? nil : [self->infoStack lastObject];
209   info->tagName      = [_localName copy];
210   info->namespace    = [_ns        copy];
211   
212   [self->infoStack addObject:info];
213   [info release];
214
215   /* determine mapping dictionary */
216   
217   if ((info->mapping = [self mappingForTag:_localName namespace:_ns]) == nil) {
218     if (debugOn) {
219       NSLog(@"found no mapping for element '%@' (namespace %@)", 
220             _localName, _ns);
221     }
222     info->error = [[self missingElementMapping:_ns:_localName] retain];
223     return;
224   }
225   
226   /* start object */
227   [info start];
228   
229   /* add attribute values */
230   {
231     NSEnumerator *e;
232     NSString     *a;
233     
234     e = [[info->mapping attributeKeys] objectEnumerator];
235     while ((a = [e nextObject])) {
236       NSString *v, *key;
237       
238       if ((v = [_attributes valueForName:a uri:_ns])) {
239         key = [info->mapping propertyKeyForAttribute:a];
240         
241         NS_DURING
242           [info->object takeValue:v forKey:key];
243         NS_HANDLER
244           [self couldNotApplyAttribute:a asKey:key onObject:info->object];
245         NS_ENDHANDLER;
246       }
247     }
248   }
249 }
250 - (void)endElement:(NSString *)_localName
251   namespace:(NSString *)_ns
252   rawName:(NSString *)_rawName
253 {
254   _SaxObjTagInfo *info;
255   unsigned idx;
256
257   idx = [self->infoStack count] - 1;
258   info = [self->infoStack objectAtIndex:idx];
259   [info stop];
260   
261   if (idx == 0)
262     ASSIGN(self->rootObject, [info object]);
263   
264   [self->infoStack removeObjectAtIndex:idx];
265 }
266
267 /* CDATA */
268
269 - (void)characters:(unichar *)_chars length:(int)_len {
270   _SaxObjTagInfo *info;
271   
272   if (_len == 0) return;
273   info = [self->infoStack objectAtIndex:([self->infoStack count] - 1)];
274   [info characters:_chars length:_len];
275 }
276
277 - (BOOL)processIgnorableWhitespace {
278   return NO;
279 }
280
281 - (void)ignorableWhitespace:(unichar *)_chars length:(int)_len {
282   if ([self processIgnorableWhitespace])
283     [self characters:_chars length:_len];
284 }
285
286 @end /* SaxObjectDecoder */
287
288 @implementation NSObject(SaxObjectCoding)
289
290 - (id)initWithSaxDecoder:(SaxObjectDecoder *)_decoder {
291   return [self init];
292 }
293
294 - (id)awakeAfterUsingSaxDecoder:(SaxObjectDecoder *)_decoder {
295   return self;
296 }
297
298 @end /* SaxCoding */
299
300 @implementation _SaxObjTagInfo
301
302 static Class  MutableDictClass   = Nil;
303 static Class  MutableStringClass = Nil;
304 static Class  StringClass        = Nil;
305
306 + (void)initialize {
307   MutableDictClass   = [NSMutableDictionary class];
308   MutableStringClass = [NSMutableString     class];
309   StringClass        = [NSString            class];
310 }
311
312 - (void)dealloc {
313   [self->tagName   release];
314   [self->namespace release];
315   [self->error     release];
316   [self->object    release];
317   [self->collectedCharData release];
318   [super dealloc];
319 }
320
321 /* errors */
322
323 - (NSException *)missingMappedClass:(NSString *)_className {
324   return [NSException exceptionWithName:@"MissingMappedClass"
325                       reason:_className
326                       userInfo:nil];
327 }
328
329 /* accessors */
330
331 - (SaxTagModel *)mapping {
332   return self->mapping;
333 }
334 - (id)object {
335   return (self->object == null) ? nil : self->object;
336 }
337
338 - (SaxTagModel *)parentMapping {
339   return [self->parentInfo mapping];
340 }
341 - (id)parentObject {
342   return [self->parentInfo object];
343 }
344
345 /* run */
346
347 - (Class)defaultElementClass {
348   return [NSMutableDictionary class];
349 }
350
351 - (void)unableToSetValue:(id)_object forKey:(NSString *)_key
352   withTag:(NSString *)_tag toParent:(id)_parent
353   exception:(NSException *)_exc
354 {
355   NSLog(@"couldn't apply value %@ for key %@ with parent %@<%@>: %@",
356         _object, _key, _parent, NSStringFromClass([_parent class]), _exc);
357 }
358
359 - (void)addObject:(id)_object fromTag:(NSString *)_tag
360   withMapping:(SaxTagModel *)_elementMap
361   toParent:(id)_parent withMapping:(SaxTagModel *)_parentMap
362 {
363   NSString *key;
364
365   if (_object     == nil || _object     == null) return;
366   if (_parent     == nil || _parent     == null) return;
367   if (_elementMap == nil || _elementMap == (id)null) return;
368   if (_parentMap  == nil || _parentMap  == (id)null) return;
369   
370   if ((key = [_parentMap propertyKeyForChildTag:_tag]) == nil) {
371     if ((key = [_elementMap key]) == nil)
372       key = _tag;
373   }
374   
375   NS_DURING {
376     if ([_parentMap isToManyKey:key]) {
377       [_parentMap addValue:_object toPropertyWithKey:key ofObject:_parent];
378     }
379     else {
380       [_parent takeValue:_object forKey:key];
381     }
382   }
383   NS_HANDLER {
384     [self unableToSetValue:_object forKey:key withTag:_tag toParent:_parent
385           exception:localException];
386   }
387   NS_ENDHANDLER;
388 }
389
390 - (void)start {
391   NSString *s;
392   Class    mappedClazz;
393   
394   /* determine class */
395   
396   if ((s = [self->mapping className])) {
397     mappedClazz = NSClassFromString(s);
398     if (mappedClazz == Nil) {
399       self->error = [[self missingMappedClass:s] retain];
400       return;
401     }
402   }
403   else
404     mappedClazz = [self defaultElementClass];
405   
406   /* do we need to check for subclasses? I guess not. */
407   self->flags.isMutableDict   = (mappedClazz == MutableDictClass)   ? 1 : 0;
408   self->flags.isMutableString = (mappedClazz == MutableStringClass) ? 1 : 0;
409   self->flags.isString        = (mappedClazz == StringClass)        ? 1 : 0;
410   self->flags.hasContentKey   = [[self->mapping contentKey] length] > 0 ?1:0;
411   
412   /* create an object for the element .. */
413   
414   if ((self->object = [[mappedClazz alloc] initWithSaxDecoder:self->decoder])) {
415     NSDictionary *defaultValues;
416     id tmp;
417     
418     if ((defaultValues = [self->mapping defaultValues]))
419       [self->object takeValuesFromDictionary:defaultValues];
420     
421     if ((tmp = [self->mapping tagKey]))
422       [self->object takeValue:self->tagName forKey:tmp];
423     if ((tmp = [self->mapping namespaceKey]))
424       [self->object takeValue:self->namespace forKey:tmp];
425   }
426 }
427
428 - (void)stop {
429   id tmp;
430
431   /* awake from decoding (the decoded object can replace itself :-) */
432   
433   if (self->flags.isString) {
434   }
435   else {
436     if (self->flags.hasContentKey) {
437       NSString *s;
438
439       s = [self->collectedCharData copy];
440       ASSIGN(self->collectedCharData, (id)nil);
441       
442       [self->object takeValue:s forKey:[self->mapping contentKey]];
443       [s release];
444     }
445     
446     tmp = self->object;
447     self->object =
448       [[self->object awakeAfterUsingSaxDecoder:self->decoder] retain];
449     [tmp release];
450   }
451   if (!self->flags.isRoot) {
452     NSString *t;
453     id parent;
454
455     parent = [self parentObject];
456     
457     /* add to parent */
458
459     if ((t = [self->mapping parentKey]))
460       [self->object takeValue:parent forKey:t];
461     
462     [self addObject:self->object 
463           fromTag:self->tagName
464           withMapping:self->mapping
465           toParent:parent
466           withMapping:[self parentMapping]];
467   }
468 }
469
470 - (void)characters:(unichar *)_chars length:(int)_len {
471   if (self->flags.isMutableString) {
472     NSString *tmp;
473     
474     tmp = [[NSString alloc] initWithCharacters:_chars length:_len];
475     [self->object appendString:tmp];
476     [tmp release];
477   }
478   else if (self->flags.isString) {
479     NSString *tmp, *old;
480
481     old = self->object;
482     
483     tmp = [[NSString alloc] initWithCharacters:_chars length:_len];
484     self->object = [[self->object stringByAppendingString:tmp] retain];
485     [tmp release];
486     [old release];
487   }
488   else if (self->flags.hasContentKey) {
489     if (self->collectedCharData == nil) {
490       self->collectedCharData = 
491         [[NSMutableString alloc] initWithCharacters:_chars length:_len];
492     }
493     else {
494       NSString *tmp;
495       
496       tmp = [[NSString alloc] initWithCharacters:_chars length:_len];
497       [self->collectedCharData appendString:tmp];
498       [tmp release];
499     }
500   }
501 }
502
503 @end /* _SaxObjTagInfo */