]> err.no Git - sope/blob - sope-xml/libxmlSAXDriver/libxmlDocSAXDriver.m
removed gcc 4.0 warnings
[sope] / sope-xml / libxmlSAXDriver / libxmlDocSAXDriver.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 #import "libxmlDocSAXDriver.h"
23 #import "libxmlSAXLocator.h"
24 #include <SaxObjC/SaxObjC.h>
25 #include <SaxObjC/SaxException.h>
26 #include "common.h"
27
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30
31 @interface libxmlDocSAXDriver(PrivateMethods)
32
33 - (void)tearDownParser;
34
35 - (BOOL)walkDocumentTree:(xmlDocPtr)_doc;
36 - (BOOL)processNode:(xmlNodePtr)_node;
37 - (BOOL)processTextNode:(xmlNodePtr)_node;
38 - (BOOL)processChildren:(xmlNodePtr)children;
39
40 @end
41
42 static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd, 
43                         unichar **targetStart, const unichar *targetEnd);
44
45 static inline NSString *xmlCharsToString(const xmlChar *_s) {
46   static Class NSStringClass = Nil;
47   if (NSStringClass == Nil)
48     NSStringClass = [NSString class];
49   return _s ? [[NSStringClass alloc] initWithUTF8String:(const char*)_s] : nil;
50 }
51
52 static NSString *SaxDeclHandlerProperty =
53   @"http://xml.org/sax/properties/declaration-handler";
54 static NSString *SaxLexicalHandlerProperty =
55   @"http://xml.org/sax/properties/lexical-handler";
56
57 @implementation libxmlDocSAXDriver
58
59 static libxmlDocSAXDriver *activeDriver = nil;
60 static void warning(void *udata, const char *msg, ...);
61 static void error(void *udata, const char *msg, ...);
62 static void fatalError(void *udata, const char *msg, ...);
63 static void setLocator(void *udata, xmlSAXLocatorPtr _locator);
64
65 - (id)init {
66   if ((self = [super init])) {
67     self->encodeEntities = NO;
68   }
69   return self;
70 }
71
72 - (void)dealloc {
73   [self tearDownParser];
74   [self->lexicalHandler release];
75   [self->declHandler    release];
76   [self->contentHandler release];
77   [self->dtdHandler     release];
78   [self->errorHandler   release];
79   [self->entityResolver release];
80   [super dealloc];
81 }
82
83 /* features & properties */
84
85 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
86   [SaxNotRecognizedException raise:@"FeatureException"
87                              format:@"don't know feature %@", _name];
88 }
89 - (BOOL)feature:(NSString *)_name {
90   [SaxNotRecognizedException raise:@"FeatureException"
91                              format:@"don't know feature %@", _name];
92   return NO;
93 }
94
95 - (void)setProperty:(NSString *)_name to:(id)_value {
96   if ([_name isEqualToString:SaxLexicalHandlerProperty]) {
97     ASSIGN(self->lexicalHandler, _value);
98     return;
99   }
100   if ([_name isEqualToString:SaxDeclHandlerProperty]) {
101     ASSIGN(self->declHandler, _value);
102     return;
103   }
104   
105   [SaxNotRecognizedException raise:@"PropertyException"
106                              format:@"don't know property %@", _name];
107 }
108 - (id)property:(NSString *)_name {
109   if ([_name isEqualToString:SaxLexicalHandlerProperty])
110     return self->lexicalHandler;
111   if ([_name isEqualToString:SaxDeclHandlerProperty])
112     return self->declHandler;
113   
114   [SaxNotRecognizedException raise:@"PropertyException"
115                              format:@"don't know property %@", _name];
116   return nil;
117 }
118
119 /* handlers */
120
121 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
122   ASSIGN(self->dtdHandler, _handler);
123 }
124 - (id<NSObject,SaxDTDHandler>)dtdHandler {
125   return self->dtdHandler;
126 }
127
128 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
129   ASSIGN(self->errorHandler, _handler);
130 }
131 - (id<NSObject,SaxErrorHandler>)errorHandler {
132   return self->errorHandler;
133 }
134
135 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
136   ASSIGN(self->entityResolver, _handler);
137 }
138 - (id<NSObject,SaxEntityResolver>)entityResolver {
139   return self->entityResolver;
140 }
141
142 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
143   ASSIGN(self->contentHandler, _handler);
144 }
145 - (id<NSObject,SaxContentHandler>)contentHandler {
146   return self->contentHandler;
147 }
148
149 /* libxml */
150
151 - (void)setupParserWithDocumentPath:(NSString *)_path {
152   static xmlSAXHandler sax;
153   
154   NSAssert(self->ctxt == NULL, @"DocSAX parser context already setup !");
155   
156   memcpy(&sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler));
157   sax.error              = error;
158   sax.warning            = warning;
159   sax.fatalError         = fatalError;
160   sax.setDocumentLocator = setLocator;
161   
162   NSAssert(activeDriver == nil, @"a parser is already running !");
163   activeDriver = self;
164   
165   self->ctxt = xmlCreatePushParserCtxt(&sax             /* sax      */,
166                                        NULL /*self*/    /* userdata */,
167                                        NULL             /* chunk    */,
168                                        0                /* chunklen */,
169                                        [_path cString]  /* filename */);
170   self->doc = NULL;
171 }
172 - (void)tearDownParser {
173   if (activeDriver == self)
174     activeDriver = nil;
175   
176   if (self->doc) {
177     xmlFreeDoc(self->doc);
178     self->doc = NULL;
179   }
180   if (self->ctxt) {
181     xmlFreeParserCtxt(self->ctxt);
182     self->ctxt = NULL;
183   }
184 }
185
186 /* IO */
187
188 - (void)pushBytes:(const char *)_bytes count:(unsigned)_len {
189   if (_len == 0) return;
190   NSAssert(self->ctxt, @"missing DocSAX parser context");
191   xmlParseChunk(self->ctxt, _bytes, _len, 0);
192 }
193 - (void)pushEOF {
194   char dummyByte;
195   xmlParseChunk(self->ctxt, &dummyByte, 0, 1 /* terminate */);
196   self->doc = ((xmlParserCtxtPtr)ctxt)->myDoc;
197 }
198
199 /* parsing */
200
201 - (void)_parseFromData:(NSData *)_data systemId:(NSString *)_sysId {
202   NSAutoreleasePool *pool;
203   
204   pool = [[NSAutoreleasePool alloc] init];
205
206   /* parse into structure */
207   [self setupParserWithDocumentPath:_sysId];
208   [self pushBytes:[_data bytes] count:[_data length]];
209   [self pushEOF];
210   
211   if (self->doc == NULL) {
212     NSLog(@"Couldn't parse file: %@", _sysId);
213     [self tearDownParser];
214   }
215   else {
216     //NSLog(@"parsed file: %@", _sysId);
217     
218     [self walkDocumentTree:self->doc];
219     [self tearDownParser];
220   }
221   
222   [pool release];
223 }
224
225 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
226   if ([_source isKindOfClass:[NSData class]]) {
227     [self _parseFromData:_source systemId:nil];
228     return;
229   }
230   if ([_source isKindOfClass:[NSString class]]) {
231     [self _parseFromData:[_source dataUsingEncoding:NSISOLatin1StringEncoding]
232           systemId:nil];
233     return;
234   }
235   if ([_source isKindOfClass:[NSURL class]]) {
236     NSData *data;
237
238     data = [_source isFileURL]
239       ? [NSData dataWithContentsOfMappedFile:[_source path]]
240       : [_source resourceDataUsingCache:YES];
241     
242     [self _parseFromData:data systemId:[_source absoluteString]];
243     return;
244   }
245   
246   {
247     SaxParseException *e;
248     NSDictionary      *ui;
249     
250     ui = [NSDictionary dictionaryWithObjectsAndKeys:
251                          _source ? _source : @"<nil>", @"source",
252                          self,                         @"parser",
253                          nil];
254     
255     e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
256                                reason:@"cannot handle data-source"
257                                userInfo:ui];
258     
259     [self->errorHandler fatalError:e];
260   }
261 }
262 - (void)parseFromSource:(id)_source {
263   if ([_source isKindOfClass:[NSString class]])
264     [self parseFromSource:_source systemId:@"<string>"];
265   else if ([_source isKindOfClass:[NSData class]])
266     [self parseFromSource:_source systemId:@"<data>"];
267   else if ([_source isKindOfClass:[NSURL class]])
268     [self parseFromSource:_source systemId:[_source absoluteString]];
269   else
270     [self parseFromSource:_source systemId:@"<memory>"];
271 }
272
273 - (void)parseFromSystemId:(NSString *)_sysId {
274   NSAutoreleasePool *pool;
275   NSData *data;
276   
277   if (![_sysId hasPrefix:@"file://"]) {
278     /* exception */
279     return;
280   }
281   
282   pool = [[NSAutoreleasePool alloc] init];
283   
284   /* cut off file:// */
285   _sysId = [_sysId substringFromIndex:7];
286   
287   /* load data */
288   data = [NSData dataWithContentsOfFile:_sysId];
289
290   [self _parseFromData:data systemId:_sysId];
291   
292   [pool release];
293 }
294
295 /* process attribute nodes */
296
297 - (SaxAttributes *)processAttributes:(xmlAttrPtr)_attributes {
298   xmlAttrPtr    attribute;
299   SaxAttributes *attributes;
300   
301   if (_attributes == NULL)
302     /* nothing to process */
303     return nil;
304
305   /* setup or clear attribute cache */
306   
307   attributes = [[SaxAttributes alloc] init];
308   
309   /* add attributes */
310   
311   for (attribute = _attributes; attribute; attribute = attribute->next) {
312     NSString *name;
313     NSString *value;
314     NSString *nsuri;
315 #if 0
316     printf("attr name '%s' has NS '%s'\n",
317            attribute->name, attribute->ns ? "yes" : "no");
318 #endif
319     
320     name      = xmlCharsToString(attribute->name);
321     value     = @"";
322     
323     if (attribute->children) {
324       xmlChar  *t;
325       
326       if ((t = xmlNodeListGetString(doc, attribute->children, 0))) {
327         value = xmlCharsToString(t);
328         free(t); /* should be xmlFree ?? */
329       }
330     }
331
332     nsuri = (attribute->ns != NULL)
333       ? xmlCharsToString(attribute->ns->href)
334       : nil;
335     
336     [attributes addAttribute:name
337                 uri:nsuri
338                 rawName:name
339                 type:@"CDATA" value:value];
340     
341     [nsuri release]; nsuri = nil;
342     [name  release]; name  = nil;
343     [value release]; value = nil;
344   }
345   
346   return attributes;
347 }
348
349 /* walking the tree, generating SAX events */
350
351 - (BOOL)_resolveEntityReferences {
352   return YES;
353 }
354
355 - (BOOL)processEntityRefNode:(xmlNodePtr)_node {
356   if ([self _resolveEntityReferences])
357     return [self processTextNode:_node];
358   else {
359     NSString *refName;
360     NSString *entityValue;
361
362     refName     = xmlCharsToString(_node->name);
363     entityValue = xmlCharsToString(_node->content);
364
365 #if 0
366     NSLog(@"%s:%i: Ignoring entity ref: '%@' %s\n",
367           __PRETTY_FUNCTION__, __LINE__, refName, _node->content);
368 #endif
369   
370     [entityValue release];
371     [refName     release];
372     return YES;
373   }
374 }
375
376 - (BOOL)processDocumentNode:(xmlNodePtr)node {
377   BOOL result;
378   
379   [self->contentHandler startDocument];
380   [self->contentHandler
381        startPrefixMapping:@""
382        uri:@"http://www.w3.org/XML/1998/namespace"];
383   result = [self processChildren:node->children];
384   [self->contentHandler endPrefixMapping:@""];
385   [self->contentHandler endDocument];
386   
387   return result;
388 }
389
390 - (BOOL)processTextNode:(xmlNodePtr)_node {
391   static unichar c = '\0';
392   
393   if (self->contentHandler == nil)
394     return YES;
395   
396   if (_node->content) {
397     xmlChar *chars;
398     
399     if (self->encodeEntities) {
400       /* should use the DocSAX encoding routine (htmlEncodeEntities) ??? */
401       
402       chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
403     }
404     else
405       chars = _node->content;
406     
407     if (chars == NULL) {
408       [self->contentHandler characters:&c length:0];
409     }
410     else {
411       void     *data, *ts;
412       unsigned len;
413       
414       len = strlen((char *)chars);
415       data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
416       
417       if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
418                        (void *)&ts, ts + (len * sizeof(unichar)))) {
419         free(data);
420         NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
421               __PRETTY_FUNCTION__, __LINE__);
422         return NO;
423       }
424       
425       [self->contentHandler characters:data length:(unsigned)(ts - data)];
426       
427       free(data);
428     }
429   }
430   else
431     [self->contentHandler characters:&c length:0];
432   
433   return YES;
434 }
435
436 - (BOOL)processCommentNode:(xmlNodePtr)_node {
437   unichar c = '\0';
438   
439   if (self->lexicalHandler == nil)
440     return YES;
441   
442   if (_node->content) {
443     xmlChar  *chars;
444     
445     /* uses the DocSAX encoding routine !!!!!!!!!! */
446     chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
447     
448     if (chars == NULL) {
449       [self->lexicalHandler comment:&c length:0];
450     }
451     else {
452       void     *data, *ts;
453       unsigned len;
454     
455       len = strlen((const char *)chars);
456       data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
457   
458       if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
459                        (void *)&ts, ts + (len * sizeof(unichar)))) {
460         free(data);
461         NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
462               __PRETTY_FUNCTION__, __LINE__);
463         return NO;
464       }
465       
466       [self->lexicalHandler comment:data length:(ts - data)];
467       
468       free(data);
469     }
470   }
471   else
472     [self->lexicalHandler comment:&c length:0];
473   
474   return YES;
475 }
476
477 - (BOOL)processDTDNode:(xmlNodePtr)node {
478   /* do nothing with DTD nodes .. */
479   return YES;
480 }
481
482 - (BOOL)processEntityNode:(xmlNodePtr)node {
483   /* do nothing with entity nodes .. */
484   NSLog(@"%s:%i: ignoring entity node (name='%s') ...",
485         __PRETTY_FUNCTION__, __LINE__, node->name);
486   return YES;
487 }
488
489 - (BOOL)processPINode:(xmlNodePtr)node {
490   NSString *piName;
491   NSString *piValue;
492   
493   piName  = xmlCharsToString(node->name);
494   piValue = xmlCharsToString(node->content);
495   
496   [self->contentHandler processingInstruction:piName data:piValue];
497
498   [piName  release];
499   [piValue release];
500   return YES;
501 }
502
503 - (BOOL)processElementNode:(xmlNodePtr)node {
504   id<NSObject,SaxAttributes> attrs;
505   NSString *tagName;
506   NSString *nsuri;
507   BOOL     result;
508   
509   self->depth++;
510   
511   tagName = xmlCharsToString(node->name);
512   nsuri = (node->ns != NULL)
513     ? xmlCharsToString(node->ns->href)
514     : nil;
515   
516   attrs = [self processAttributes:node->properties];
517   
518   [self->contentHandler
519        startElement:tagName
520        namespace:nsuri
521        rawName:tagName
522        attributes:attrs];
523   
524   [attrs release]; attrs = nil;
525   
526   result = [self processChildren:node->children];
527   
528   [self->contentHandler
529        endElement:tagName
530        namespace:nsuri
531        rawName:tagName];
532   
533   self->depth--;
534
535   [nsuri   release]; nsuri   = nil;
536   [tagName release]; tagName = nil;
537   [attrs   release]; attrs   = nil;
538   return result;
539 }
540
541 - (BOOL)processChildren:(xmlNodePtr)children {
542   xmlNodePtr node;
543   
544   if (children == NULL)
545     return YES;
546   
547   for (node = children; node; node = node->next) {
548     [self processNode:node];
549   }
550   
551   return YES;
552 }
553
554 - (BOOL)processNode:(xmlNodePtr)_node {
555   switch(_node->type) {
556     case XML_ELEMENT_NODE:
557       return [self processElementNode:_node];
558
559     case XML_ATTRIBUTE_NODE:
560       NSLog(@"invalid place for attribute-node !");
561       return NO;
562       
563     case XML_TEXT_NODE:
564       return [self processTextNode:_node];
565
566     case XML_CDATA_SECTION_NODE:
567       return [self processTextNode:_node];
568       
569     case XML_ENTITY_REF_NODE:
570       return [self processEntityRefNode:_node];
571
572     case XML_ENTITY_NODE:
573       return [self processEntityNode:_node];
574       
575     case XML_PI_NODE:
576       return [self processPINode:_node];
577       
578     case XML_COMMENT_NODE:
579       return [self processCommentNode:_node];
580       
581     case XML_DOCUMENT_NODE:
582       return [self processDocumentNode:_node];
583       
584     case XML_DTD_NODE:
585       return [self processDTDNode:_node];
586     
587     default:
588       NSLog(@"WARNING: UNKNOWN node type %i\n", _node->type);
589       break;
590   }
591   return NO;
592 }
593
594 - (BOOL)walkDocumentTree:(xmlDocPtr)_doc {
595   int  type;
596   BOOL result;
597   
598   type = ((xmlDocPtr)self->doc)->type;
599   ((xmlDocPtr)self->doc)->type = XML_DOCUMENT_NODE;
600   
601   result = [self processNode:(xmlNodePtr)self->doc];
602   
603   ((xmlDocPtr)self->doc)->type = type;
604   
605   return result;
606 }
607
608 /* callbacks */
609
610 static SaxParseException *
611 mkException(libxmlDocSAXDriver *self, NSString *key,
612             const char *msg, va_list va)
613 {
614   NSString          *s, *reason;
615   NSDictionary      *ui;
616   SaxParseException *e;
617   int count = 0, i;
618   id  keys[7], values[7];
619   id  tmp;
620   NSRange r;
621   
622   s = [NSString stringWithCString:msg];
623   s = [[[NSString alloc]
624                   initWithFormat:s arguments:va]
625                   autorelease];
626   
627   r = [s rangeOfString:@"\n"];
628   reason = (r.length > 0)
629     ? [s substringToIndex:r.location]
630     : s;
631   
632   if ([reason length] == 0)
633     reason = @"unknown reason";
634   
635   keys[0] = @"parser"; values[0] = self; count++;
636   keys[1] = @"depth";  values[1] = [NSNumber numberWithInt:self->depth]; count++;
637   
638   if ([s length] > 0) {
639     keys[count]   = @"errorMessage";
640     values[count] = s;
641     count++;
642   }
643
644   // NSLog(@"locator: %@", self->locator);
645   
646   if ((i = [self->locator lineNumber]) >= 0) {
647     keys[count] = @"line";
648     values[count] = [NSNumber numberWithInt:i];
649     count++;
650   }
651   if ((i = [self->locator columnNumber]) >= 0) {
652     keys[count] = @"column";
653     values[count] = [NSNumber numberWithInt:i];
654     count++;
655   }
656   if ((tmp = [self->locator publicId])) {
657     keys[count]   = @"publicId";
658     values[count] = tmp;
659     count++;
660   }
661   if ((tmp = [self->locator systemId])) {
662     keys[count]   = @"systemId";
663     values[count] = tmp;
664     count++;
665   }
666   
667   ui = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];
668   
669   e = (id)[SaxParseException exceptionWithName:key
670                              reason:reason
671                              userInfo:ui];
672   return e;
673 }
674
675 static void warning(void *udata, const char *msg, ...) {
676   va_list           args;
677   SaxParseException *e;
678   
679   NSCAssert(activeDriver, @"no driver is active !");
680   
681   va_start(args, msg);
682   e = mkException(activeDriver, @"SAXWarning", msg, args);
683   va_end(args);
684   
685   [activeDriver->errorHandler warning:e];
686 }
687
688 static void error(void *udata, const char *msg, ...) {
689   va_list           args;
690   SaxParseException *e;
691
692   NSCAssert(activeDriver, @"no driver is active !");
693   
694   va_start(args, msg);
695   e = mkException(activeDriver, @"SAXError", msg, args);
696   va_end(args);
697   
698   [activeDriver->errorHandler error:e];
699 }
700
701 static void fatalError(void *udata, const char *msg, ...) {
702   va_list           args;
703   SaxParseException *e;
704
705   NSCAssert(activeDriver, @"no driver is active !");
706   
707   va_start(args, msg);
708   e = mkException(activeDriver, @"SAXFatalError", msg, args);
709   va_end(args);
710   
711   [activeDriver->errorHandler fatalError:e];
712 }
713
714 static void setLocator(void *udata, xmlSAXLocatorPtr _locator) {
715   NSCAssert(activeDriver, @"no driver is active !");
716   
717   [activeDriver->locator release];
718   
719   activeDriver->locator = [[libxmlSAXLocator alloc]
720                                              initWithSaxLocator:_locator
721                                              parser:activeDriver];
722   activeDriver->locator->ctx = activeDriver->ctxt;
723   
724   [activeDriver->contentHandler setDocumentLocator:activeDriver->locator];
725 }
726
727 @end /* libxmlDocSAXDriver */
728
729 #include "unicode.h"