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