2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #import "libxmlDocSAXDriver.h"
23 #import "libxmlSAXLocator.h"
24 #include <SaxObjC/SaxObjC.h>
25 #include <SaxObjC/SaxException.h>
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
31 @interface libxmlDocSAXDriver(PrivateMethods)
33 - (void)tearDownParser;
35 - (BOOL)walkDocumentTree:(xmlDocPtr)_doc;
36 - (BOOL)processNode:(xmlNodePtr)_node;
37 - (BOOL)processTextNode:(xmlNodePtr)_node;
38 - (BOOL)processChildren:(xmlNodePtr)children;
42 static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd,
43 unichar **targetStart, const unichar *targetEnd);
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;
52 static NSString *SaxDeclHandlerProperty =
53 @"http://xml.org/sax/properties/declaration-handler";
54 static NSString *SaxLexicalHandlerProperty =
55 @"http://xml.org/sax/properties/lexical-handler";
57 @implementation libxmlDocSAXDriver
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);
66 if ((self = [super init])) {
67 self->encodeEntities = NO;
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];
83 /* features & properties */
85 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
86 [SaxNotRecognizedException raise:@"FeatureException"
87 format:@"don't know feature %@", _name];
89 - (BOOL)feature:(NSString *)_name {
90 [SaxNotRecognizedException raise:@"FeatureException"
91 format:@"don't know feature %@", _name];
95 - (void)setProperty:(NSString *)_name to:(id)_value {
96 if ([_name isEqualToString:SaxLexicalHandlerProperty]) {
97 ASSIGN(self->lexicalHandler, _value);
100 if ([_name isEqualToString:SaxDeclHandlerProperty]) {
101 ASSIGN(self->declHandler, _value);
105 [SaxNotRecognizedException raise:@"PropertyException"
106 format:@"don't know property %@", _name];
108 - (id)property:(NSString *)_name {
109 if ([_name isEqualToString:SaxLexicalHandlerProperty])
110 return self->lexicalHandler;
111 if ([_name isEqualToString:SaxDeclHandlerProperty])
112 return self->declHandler;
114 [SaxNotRecognizedException raise:@"PropertyException"
115 format:@"don't know property %@", _name];
121 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
122 ASSIGN(self->dtdHandler, _handler);
124 - (id<NSObject,SaxDTDHandler>)dtdHandler {
125 return self->dtdHandler;
128 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
129 ASSIGN(self->errorHandler, _handler);
131 - (id<NSObject,SaxErrorHandler>)errorHandler {
132 return self->errorHandler;
135 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
136 ASSIGN(self->entityResolver, _handler);
138 - (id<NSObject,SaxEntityResolver>)entityResolver {
139 return self->entityResolver;
142 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
143 ASSIGN(self->contentHandler, _handler);
145 - (id<NSObject,SaxContentHandler>)contentHandler {
146 return self->contentHandler;
151 - (void)setupParserWithDocumentPath:(NSString *)_path {
152 static xmlSAXHandler sax;
154 NSAssert(self->ctxt == NULL, @"DocSAX parser context already setup !");
156 memcpy(&sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler));
158 sax.warning = warning;
159 sax.fatalError = fatalError;
160 sax.setDocumentLocator = setLocator;
162 NSAssert(activeDriver == nil, @"a parser is already running !");
165 self->ctxt = xmlCreatePushParserCtxt(&sax /* sax */,
166 NULL /*self*/ /* userdata */,
169 [_path cString] /* filename */);
172 - (void)tearDownParser {
173 if (activeDriver == self)
177 xmlFreeDoc(self->doc);
181 xmlFreeParserCtxt(self->ctxt);
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);
195 xmlParseChunk(self->ctxt, &dummyByte, 0, 1 /* terminate */);
196 self->doc = ((xmlParserCtxtPtr)ctxt)->myDoc;
201 - (void)_parseFromData:(NSData *)_data systemId:(NSString *)_sysId {
202 NSAutoreleasePool *pool;
204 pool = [[NSAutoreleasePool alloc] init];
206 /* parse into structure */
207 [self setupParserWithDocumentPath:_sysId];
208 [self pushBytes:[_data bytes] count:[_data length]];
211 if (self->doc == NULL) {
212 NSLog(@"Couldn't parse file: %@", _sysId);
213 [self tearDownParser];
216 //NSLog(@"parsed file: %@", _sysId);
218 [self walkDocumentTree:self->doc];
219 [self tearDownParser];
225 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
226 if ([_source isKindOfClass:[NSData class]]) {
227 [self _parseFromData:_source systemId:nil];
230 if ([_source isKindOfClass:[NSString class]]) {
231 [self _parseFromData:[_source dataUsingEncoding:NSISOLatin1StringEncoding]
235 if ([_source isKindOfClass:[NSURL class]]) {
238 data = [_source isFileURL]
239 ? [NSData dataWithContentsOfMappedFile:[_source path]]
240 : [_source resourceDataUsingCache:YES];
242 [self _parseFromData:data systemId:[_source absoluteString]];
247 SaxParseException *e;
250 ui = [NSDictionary dictionaryWithObjectsAndKeys:
251 _source ? _source : @"<nil>", @"source",
255 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
256 reason:@"cannot handle data-source"
259 [self->errorHandler fatalError:e];
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]];
270 [self parseFromSource:_source systemId:@"<memory>"];
273 - (void)parseFromSystemId:(NSString *)_sysId {
274 NSAutoreleasePool *pool;
277 if (![_sysId hasPrefix:@"file://"]) {
282 pool = [[NSAutoreleasePool alloc] init];
284 /* cut off file:// */
285 _sysId = [_sysId substringFromIndex:7];
288 data = [NSData dataWithContentsOfFile:_sysId];
290 [self _parseFromData:data systemId:_sysId];
295 /* process attribute nodes */
297 - (SaxAttributes *)processAttributes:(xmlAttrPtr)_attributes {
298 xmlAttrPtr attribute;
299 SaxAttributes *attributes;
301 if (_attributes == NULL)
302 /* nothing to process */
305 /* setup or clear attribute cache */
307 attributes = [[SaxAttributes alloc] init];
311 for (attribute = _attributes; attribute; attribute = attribute->next) {
316 printf("attr name '%s' has NS '%s'\n",
317 attribute->name, attribute->ns ? "yes" : "no");
320 name = xmlCharsToString(attribute->name);
323 if (attribute->children) {
326 if ((t = xmlNodeListGetString(doc, attribute->children, 0))) {
327 value = xmlCharsToString(t);
328 free(t); /* should be xmlFree ?? */
332 nsuri = (attribute->ns != NULL)
333 ? xmlCharsToString(attribute->ns->href)
336 [attributes addAttribute:name
339 type:@"CDATA" value:value];
341 [nsuri release]; nsuri = nil;
342 [name release]; name = nil;
343 [value release]; value = nil;
349 /* walking the tree, generating SAX events */
351 - (BOOL)_resolveEntityReferences {
355 - (BOOL)processEntityRefNode:(xmlNodePtr)_node {
356 if ([self _resolveEntityReferences])
357 return [self processTextNode:_node];
360 NSString *entityValue;
362 refName = xmlCharsToString(_node->name);
363 entityValue = xmlCharsToString(_node->content);
366 NSLog(@"%s:%i: Ignoring entity ref: '%@' %s\n",
367 __PRETTY_FUNCTION__, __LINE__, refName, _node->content);
370 [entityValue release];
376 - (BOOL)processDocumentNode:(xmlNodePtr)node {
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];
390 - (BOOL)processTextNode:(xmlNodePtr)_node {
391 static unichar c = '\0';
393 if (self->contentHandler == nil)
396 if (_node->content) {
399 if (self->encodeEntities) {
400 /* should use the DocSAX encoding routine (htmlEncodeEntities) ??? */
402 chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
405 chars = _node->content;
408 [self->contentHandler characters:&c length:0];
414 len = strlen((char *)chars);
415 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
417 if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
418 (void *)&ts, ts + (len * sizeof(unichar)))) {
420 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
421 __PRETTY_FUNCTION__, __LINE__);
425 [self->contentHandler characters:data length:(unsigned)(ts - data)];
431 [self->contentHandler characters:&c length:0];
436 - (BOOL)processCommentNode:(xmlNodePtr)_node {
439 if (self->lexicalHandler == nil)
442 if (_node->content) {
445 /* uses the DocSAX encoding routine !!!!!!!!!! */
446 chars = xmlEncodeEntitiesReentrant(self->doc, _node->content);
449 [self->lexicalHandler comment:&c length:0];
455 len = strlen((const char *)chars);
456 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
458 if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
459 (void *)&ts, ts + (len * sizeof(unichar)))) {
461 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
462 __PRETTY_FUNCTION__, __LINE__);
466 [self->lexicalHandler comment:data length:(ts - data)];
472 [self->lexicalHandler comment:&c length:0];
477 - (BOOL)processDTDNode:(xmlNodePtr)node {
478 /* do nothing with DTD nodes .. */
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);
489 - (BOOL)processPINode:(xmlNodePtr)node {
493 piName = xmlCharsToString(node->name);
494 piValue = xmlCharsToString(node->content);
496 [self->contentHandler processingInstruction:piName data:piValue];
503 - (BOOL)processElementNode:(xmlNodePtr)node {
504 id<NSObject,SaxAttributes> attrs;
511 tagName = xmlCharsToString(node->name);
512 nsuri = (node->ns != NULL)
513 ? xmlCharsToString(node->ns->href)
516 attrs = [self processAttributes:node->properties];
518 [self->contentHandler
524 [attrs release]; attrs = nil;
526 result = [self processChildren:node->children];
528 [self->contentHandler
535 [nsuri release]; nsuri = nil;
536 [tagName release]; tagName = nil;
537 [attrs release]; attrs = nil;
541 - (BOOL)processChildren:(xmlNodePtr)children {
544 if (children == NULL)
547 for (node = children; node; node = node->next) {
548 [self processNode:node];
554 - (BOOL)processNode:(xmlNodePtr)_node {
555 switch(_node->type) {
556 case XML_ELEMENT_NODE:
557 return [self processElementNode:_node];
559 case XML_ATTRIBUTE_NODE:
560 NSLog(@"invalid place for attribute-node !");
564 return [self processTextNode:_node];
566 case XML_CDATA_SECTION_NODE:
567 return [self processTextNode:_node];
569 case XML_ENTITY_REF_NODE:
570 return [self processEntityRefNode:_node];
572 case XML_ENTITY_NODE:
573 return [self processEntityNode:_node];
576 return [self processPINode:_node];
578 case XML_COMMENT_NODE:
579 return [self processCommentNode:_node];
581 case XML_DOCUMENT_NODE:
582 return [self processDocumentNode:_node];
585 return [self processDTDNode:_node];
588 NSLog(@"WARNING: UNKNOWN node type %i\n", _node->type);
594 - (BOOL)walkDocumentTree:(xmlDocPtr)_doc {
598 type = ((xmlDocPtr)self->doc)->type;
599 ((xmlDocPtr)self->doc)->type = XML_DOCUMENT_NODE;
601 result = [self processNode:(xmlNodePtr)self->doc];
603 ((xmlDocPtr)self->doc)->type = type;
610 static SaxParseException *
611 mkException(libxmlDocSAXDriver *self, NSString *key,
612 const char *msg, va_list va)
614 NSString *s, *reason;
616 SaxParseException *e;
618 id keys[7], values[7];
622 s = [NSString stringWithCString:msg];
623 s = [[[NSString alloc]
624 initWithFormat:s arguments:va]
627 r = [s rangeOfString:@"\n"];
628 reason = (r.length > 0)
629 ? [s substringToIndex:r.location]
632 if ([reason length] == 0)
633 reason = @"unknown reason";
635 keys[0] = @"parser"; values[0] = self; count++;
636 keys[1] = @"depth"; values[1] = [NSNumber numberWithInt:self->depth]; count++;
638 if ([s length] > 0) {
639 keys[count] = @"errorMessage";
644 // NSLog(@"locator: %@", self->locator);
646 if ((i = [self->locator lineNumber]) >= 0) {
647 keys[count] = @"line";
648 values[count] = [NSNumber numberWithInt:i];
651 if ((i = [self->locator columnNumber]) >= 0) {
652 keys[count] = @"column";
653 values[count] = [NSNumber numberWithInt:i];
656 if ((tmp = [self->locator publicId])) {
657 keys[count] = @"publicId";
661 if ((tmp = [self->locator systemId])) {
662 keys[count] = @"systemId";
667 ui = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];
669 e = (id)[SaxParseException exceptionWithName:key
675 static void warning(void *udata, const char *msg, ...) {
677 SaxParseException *e;
679 NSCAssert(activeDriver, @"no driver is active !");
682 e = mkException(activeDriver, @"SAXWarning", msg, args);
685 [activeDriver->errorHandler warning:e];
688 static void error(void *udata, const char *msg, ...) {
690 SaxParseException *e;
692 NSCAssert(activeDriver, @"no driver is active !");
695 e = mkException(activeDriver, @"SAXError", msg, args);
698 [activeDriver->errorHandler error:e];
701 static void fatalError(void *udata, const char *msg, ...) {
703 SaxParseException *e;
705 NSCAssert(activeDriver, @"no driver is active !");
708 e = mkException(activeDriver, @"SAXFatalError", msg, args);
711 [activeDriver->errorHandler fatalError:e];
714 static void setLocator(void *udata, xmlSAXLocatorPtr _locator) {
715 NSCAssert(activeDriver, @"no driver is active !");
717 [activeDriver->locator release];
719 activeDriver->locator = [[libxmlSAXLocator alloc]
720 initWithSaxLocator:_locator
721 parser:activeDriver];
722 activeDriver->locator->ctx = activeDriver->ctxt;
724 [activeDriver->contentHandler setDocumentLocator:activeDriver->locator];
727 @end /* libxmlDocSAXDriver */