2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #import <Foundation/NSObject.h>
24 #include <SaxObjC/SaxXMLReader.h>
25 #include <SaxObjC/SaxContentHandler.h>
26 #include <SaxObjC/SaxDTDHandler.h>
27 #include <SaxObjC/SaxErrorHandler.h>
28 #include <SaxObjC/SaxEntityResolver.h>
29 #include <SaxObjC/SaxLexicalHandler.h>
30 #include <SaxObjC/SaxLocator.h>
31 #include <SaxObjC/SaxDeclHandler.h>
34 @class NSMutableArray, NSMutableDictionary;
37 @interface ExpatSaxDriver : NSObject < SaxXMLReader >
40 id<NSObject,SaxContentHandler> contentHandler;
41 id<NSObject,SaxDTDHandler> dtdHandler;
42 id<NSObject,SaxErrorHandler> errorHandler;
43 id<NSObject,SaxEntityResolver> entityResolver;
45 id<NSObject,SaxLexicalHandler> lexicalHandler;
46 id<NSObject,SaxDeclHandler> declHandler;
53 BOOL fNamespacePrefixes;
54 NSMutableDictionary *declNS;
64 #include <SaxObjC/SaxException.h>
65 #include <SaxObjC/SaxDocumentHandlerAdaptor.h>
68 static NSString *SaxDeclHandlerProperty =
69 @"http://xml.org/sax/properties/declaration-handler";
70 static NSString *SaxLexicalHandlerProperty =
71 @"http://xml.org/sax/properties/lexical-handler";
73 static NSString *SaxDOMNodeProperty =
74 @"http://xml.org/sax/properties/dom-node";
75 static NSString *SaxXMLStringProperty =
76 @"http://xml.org/sax/properties/xml-string";
79 @interface ExpatSaxDriver(Privates)
81 - (void)_tearDownParser;
84 static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd,
85 unichar **targetStart, const unichar *targetEnd);
93 @implementation ExpatSaxDriver
95 static NSMapTable *uniqueStrings = NULL; // THREAD
96 static Class NSStringClass = Nil;
98 static inline NSString *uniqueStringUTF8(const char *utf8) {
102 if (utf8 == NULL) return nil;
104 if (uniqueStrings == NULL) {
105 uniqueStrings = NSCreateMapTable(NSNonOwnedCStringMapKeyCallBacks,
106 NSObjectMapValueCallBacks,
109 else if ((s = NSMapGet(uniqueStrings, utf8))) {
110 /* found a string in cache ... */
114 newkey = malloc(strlen(utf8) + 1);
115 strcpy(newkey, utf8);
117 if (NSStringClass == Nil)
118 NSStringClass = [NSString class];
120 s = [[NSStringClass alloc] initWithUTF8String:newkey];
121 NSMapInsert(uniqueStrings, newkey, s);
127 if ((self = [super init])) {
128 /* feature defaults */
129 self->fNamespaces = YES;
130 self->fNamespacePrefixes = NO;
136 [self _tearDownParser];
137 RELEASE(self->attrs);
138 RELEASE(self->declNS);
139 RELEASE(self->declHandler);
140 RELEASE(self->lexicalHandler);
141 RELEASE(self->contentHandler);
142 RELEASE(self->dtdHandler);
143 RELEASE(self->errorHandler);
144 RELEASE(self->entityResolver);
150 - (void)setProperty:(NSString *)_name to:(id)_value {
151 if ([_name isEqualToString:SaxLexicalHandlerProperty]) {
152 ASSIGN(self->lexicalHandler, _value);
155 if ([_name isEqualToString:SaxDeclHandlerProperty]) {
156 ASSIGN(self->declHandler, _value);
160 [SaxNotRecognizedException raise:@"PropertyException"
161 format:@"don't know property %@", _name];
163 - (id)property:(NSString *)_name {
164 if ([_name isEqualToString:SaxLexicalHandlerProperty])
165 return self->lexicalHandler;
166 if ([_name isEqualToString:SaxDeclHandlerProperty])
167 return self->declHandler;
169 [SaxNotRecognizedException raise:@"PropertyException"
170 format:@"don't know property %@", _name];
176 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
177 if ([_name isEqualToString:@"http://xml.org/sax/features/namespaces"]) {
178 self->fNamespaces = _value;
182 if ([_name isEqualToString:
183 @"http://xml.org/sax/features/namespace-prefixes"]) {
184 self->fNamespacePrefixes = _value;
188 [SaxNotRecognizedException raise:@"FeatureException"
189 format:@"don't know feature %@", _name];
191 - (BOOL)feature:(NSString *)_name {
192 if ([_name isEqualToString:@"http://xml.org/sax/features/namespaces"])
193 return self->fNamespaces;
195 if ([_name isEqualToString:
196 @"http://xml.org/sax/features/namespace-prefixes"])
197 return self->fNamespacePrefixes;
199 if ([_name isEqualToString:
200 @"http://www.skyrix.com/sax/features/predefined-namespaces"])
203 [SaxNotRecognizedException raise:@"FeatureException"
204 format:@"don't know feature %@", _name];
208 /* pre-defining namespaces */
210 - (void)declarePrefix:(NSString *)_prefix namespaceURI:(NSString *)_uri {
211 NSAssert(_prefix, @"invalid prefix ...");
212 NSAssert(_uri, @"invalid uri ...");
214 if (self->declNS == nil) {
215 self->declNS = [[NSMutableDictionary alloc] initWithCapacity:8];
218 setObject:@"http://www.w3.org/XML/1998/namespace"
220 [self->declNS setObject:@"" forKey:@":"];
223 [self->declNS setObject:_uri forKey:_prefix];
228 - (void)setDocumentHandler:(id<NSObject,SaxDocumentHandler>)_handler {
229 SaxDocumentHandlerAdaptor *a;
231 a = [[SaxDocumentHandlerAdaptor alloc] initWithDocumentHandler:_handler];
232 [self setContentHandler:a];
236 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
237 ASSIGN(self->dtdHandler, _handler);
239 - (id<NSObject,SaxDTDHandler>)dtdHandler {
240 return self->dtdHandler;
243 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
244 ASSIGN(self->errorHandler, _handler);
246 - (id<NSObject,SaxErrorHandler>)errorHandler {
247 return self->errorHandler;
250 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
251 ASSIGN(self->entityResolver, _handler);
253 - (id<NSObject,SaxEntityResolver>)entityResolver {
254 return self->entityResolver;
257 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
258 ASSIGN(self->contentHandler, _handler);
260 - (id<NSObject,SaxContentHandler>)contentHandler {
261 return self->contentHandler;
266 - (void)_reportParseError:(enum XML_Error)_error systemId:(NSString *)_sysId {
267 NSMutableDictionary *ui;
273 ename = @"SaxException";
274 ereason = @"XML parse error";
275 sel = @selector(fatalError:);
278 case XML_ERROR_NONE: /* no error ... */
280 case XML_ERROR_SYNTAX:
281 sel = @selector(error:);
282 ereason = @"XML syntax error";
284 case XML_ERROR_NO_MEMORY:
285 sel = @selector(fatalError:);
286 ereason = @"out of memory";
288 case XML_ERROR_NO_ELEMENTS:
289 sel = @selector(error:);
290 ereason = @"no elements";
292 case XML_ERROR_INVALID_TOKEN:
293 sel = @selector(error:);
294 ereason = @"invalid token";
296 case XML_ERROR_UNCLOSED_TOKEN:
297 sel = @selector(error:);
298 ereason = @"unclosed token";
300 case XML_ERROR_PARTIAL_CHAR:
301 sel = @selector(error:);
302 ereason = @"partial character";
304 case XML_ERROR_TAG_MISMATCH:
305 sel = @selector(error:);
306 ereason = @"tag mismatch";
308 case XML_ERROR_DUPLICATE_ATTRIBUTE:
309 sel = @selector(error:);
310 ereason = @"duplicate attribute";
312 case XML_ERROR_JUNK_AFTER_DOC_ELEMENT:
313 sel = @selector(warning:);
314 ereason = @"junk after document element";
316 case XML_ERROR_PARAM_ENTITY_REF:
317 sel = @selector(error:);
318 ereason = @"parameter entity reference";
320 case XML_ERROR_UNDEFINED_ENTITY:
321 sel = @selector(error:);
322 ereason = @"undefined entity";
324 case XML_ERROR_RECURSIVE_ENTITY_REF:
325 sel = @selector(error:);
326 ereason = @"recursive entity reference";
328 case XML_ERROR_ASYNC_ENTITY:
329 sel = @selector(error:);
330 ereason = @"async entity";
332 case XML_ERROR_BAD_CHAR_REF:
333 sel = @selector(error:);
334 ereason = @"bad character reference";
336 case XML_ERROR_BINARY_ENTITY_REF:
337 sel = @selector(error:);
338 ereason = @"binary entity reference";
340 case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF:
341 sel = @selector(error:);
342 ereason = @"attibute external entity reference";
344 case XML_ERROR_MISPLACED_XML_PI:
345 sel = @selector(error:);
346 ereason = @"misplaced processing instruction";
348 case XML_ERROR_UNKNOWN_ENCODING:
349 sel = @selector(error:);
350 ereason = @"unknown encoding";
352 case XML_ERROR_INCORRECT_ENCODING:
353 sel = @selector(error:);
354 ereason = @"incorrect encoding";
356 case XML_ERROR_UNCLOSED_CDATA_SECTION:
357 sel = @selector(error:);
358 ereason = @"unclosed CDATA section";
360 case XML_ERROR_EXTERNAL_ENTITY_HANDLING:
361 sel = @selector(error:);
362 ereason = @"external entity handling";
364 case XML_ERROR_NOT_STANDALONE:
365 sel = @selector(error:);
366 ereason = @"XML is not standalone";
368 case XML_ERROR_UNEXPECTED_STATE:
369 sel = @selector(fatalError:);
370 ereason = @"unexpected status";
374 ui = [NSMutableDictionary dictionaryWithCapacity:4];
376 if (_sysId) [ui setObject:_sysId forKey:@"systemId"];
377 [ui setObject:self forKey:@"parser"];
381 if ((line = XML_GetCurrentLineNumber(self->expat)) > 0)
382 [ui setObject:[NSNumber numberWithInt:line] forKey:@"line"];
384 [ui setObject:[NSNumber numberWithUnsignedInt:_error]
385 forKey:@"expatErrorCode"];
387 e = (id)[SaxParseException exceptionWithName:ename
391 [self->errorHandler performSelector:sel withObject:e];
394 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
395 if (_source == nil) {
400 if ([_source isKindOfClass:[NSData class]]) {
401 if ([self _setupParser]) {
402 NSAutoreleasePool *pool;
404 pool = [[NSAutoreleasePool alloc] init];
408 [self->contentHandler startDocument];
409 res = XML_Parse(self->expat, [_source bytes], [_source length], 1);
412 [self _reportParseError:XML_GetErrorCode(self->expat)
415 [self->contentHandler endDocument];
418 [self _tearDownParser];
421 else if ([_source isKindOfClass:[NSString class]]) {
422 [self parseFromSource:
423 [_source dataUsingEncoding:NSUTF8StringEncoding]
426 else if ([_source isKindOfClass:[NSURL class]]) {
428 _sysId = [_source absoluteString];
430 [self parseFromSource:[_source resourceDataUsingCache:NO]
434 [self parseFromSource:[_source stringValue] systemId:_sysId];
437 - (void)parseFromSource:(id)_source {
438 [self parseFromSource:_source systemId:nil];
441 - (void)parseFromSystemId:(NSString *)_sysId {
442 if (![_sysId hasPrefix:@"file:"]) {
443 SaxParseException *e;
447 if ((url = [NSURL URLWithString:_sysId]))
448 return [self parseFromSource:url systemId:_sysId];
450 ui = [NSDictionary dictionaryWithObjectsAndKeys:
451 _sysId ? _sysId : @"<nil>", @"systemID",
455 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
456 reason:@"can't handle system-id"
459 [self->errorHandler fatalError:e];
465 _sysId = [_sysId substringFromIndex:7];
467 if ((data = [NSData dataWithContentsOfMappedFile:_sysId]) == nil) {
468 NSLog(@"couldn't load file '%@'", _sysId);
472 return [self parseFromSource:data systemId:_sysId];
478 static TagTriple splitName(ExpatSaxDriver *self, const char *el) {
484 if ((len = strlen(el)) == 0) {
491 if (len >= self->nameBufLen) {
492 if (self->nameBuf) free(self->nameBuf);
493 self->nameBuf = malloc(len + 8);
494 self->nameBufLen = len + 6;
501 t.raw = uniqueStringUTF8(el);
503 if ((el_tag = index(buf, '\t'))) {
506 t.tag = uniqueStringUTF8(el_tag + 1);
508 idx = [t.raw rangeOfString:@"\t"].location;
509 t.uri = [[t.raw substringToIndex:idx] copy];
511 else if ((self->declNS != nil) && ((el_tag = index(buf, ':')) != NULL)) {
512 /* check predefined namespaces ... */
516 prefix = uniqueStringUTF8(buf);
518 if ((t.uri = [self->declNS objectForKey:prefix])) {
519 t.tag = uniqueStringUTF8(el_tag + 1);
520 t.uri = [t.uri copy];
524 t.tag = [t.raw copy];
531 t.tag = [t.raw copy];
536 static void releaseTag(TagTriple t) {
542 static void _startElem(void *data, const char *el, const char **attr) {
543 ExpatSaxDriver *self = (ExpatSaxDriver *)data;
547 t = splitName(self, el);
549 /* process attributes */
551 if (self->attrs == nil)
552 self->attrs = [[SaxAttributes alloc] init];
555 if (NSStringClass == Nil) NSStringClass = [NSString class];
557 for (i = 0; attr[i] != NULL; i += 2) {
561 at = splitName(self, attr[i]);
562 value = [[NSStringClass alloc] initWithUTF8String:attr[i + 1]];
566 uri:at.uri ? at.uri : t.uri
568 type:@"CDATA" value:value];
574 /* notify handler ... */
576 [self->contentHandler
577 startElement:t.tag namespace:t.uri rawName:t.raw
578 attributes:self->attrs];
583 static void _endElem(void *data, const char *el) {
584 ExpatSaxDriver *self = (ExpatSaxDriver *)data;
587 t = splitName(self, el);
589 [self->contentHandler endElement:t.tag namespace:t.uri rawName:t.raw];
594 static void _startNS(void *data, const char *_prefix, const char *_uri) {
595 ExpatSaxDriver *self = (ExpatSaxDriver *)data;
596 NSString *spre, *suri;
598 spre = _prefix ? uniqueStringUTF8(_prefix) : @"";
599 suri = _uri ? uniqueStringUTF8(_uri) : @"";
600 [self->contentHandler startPrefixMapping:spre uri:suri];
604 static void _endNS(void *data, const char *_prefix) {
605 ExpatSaxDriver *self = (ExpatSaxDriver *)data;
608 spre = _prefix ? uniqueStringUTF8(_prefix) : @"";
609 [self->contentHandler endPrefixMapping:spre];
613 static void _characters(void *_data, const char *chars, int len) {
614 ExpatSaxDriver *self = (ExpatSaxDriver *)_data;
620 [self->contentHandler characters:data length:len];
624 [self->contentHandler characters:NULL length:0];
628 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
630 if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
631 (void *)&ts, ts + (len * sizeof(unichar)))) {
633 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
634 __PRETTY_FUNCTION__, __LINE__);
637 [self->contentHandler characters:data length:(unsigned)(ts - data)];
642 static void _pi(void *_udata, const char *_target, const char *_data) {
643 ExpatSaxDriver *self = (ExpatSaxDriver *)_udata;
644 NSString *target, *data;
646 target = uniqueStringUTF8(_target);
647 data = uniqueStringUTF8(_data);
648 [self->contentHandler processingInstruction:target data:data];
653 static void _comment(void *_data, const char *chars) {
654 ExpatSaxDriver *self = (ExpatSaxDriver *)_data;
663 [self->lexicalHandler comment:data length:0];
667 [self->lexicalHandler comment:NULL length:0];
671 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
673 if (_UTF8ToUTF16((void *)&chars, (void *)(chars + len),
674 (void *)&ts, ts + (len * sizeof(unichar)))) {
676 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
677 __PRETTY_FUNCTION__, __LINE__);
680 [self->lexicalHandler comment:data length:(ts - data)];
685 static void _startCDATA(void *data) {
687 static void _endCDATA(void *data) {
690 - (BOOL)_setupParser {
691 [self _tearDownParser];
693 if ((self->expat = XML_ParserCreateNS(NULL, '\t')) == NULL) {
695 NSLog(@"%s: couldn't create expat parser ..", __PRETTY_FUNCTION__);
700 XML_SetUserData(self->expat, self);
701 XML_SetReturnNSTriplet(self->expat, 1); /* also return NS prefix */
703 if (self->contentHandler) {
704 XML_SetElementHandler(self->expat, _startElem, _endElem);
705 XML_SetNamespaceDeclHandler(self->expat, _startNS, _endNS);
707 XML_SetCharacterDataHandler(self->expat, _characters);
708 XML_SetProcessingInstructionHandler(self->expat, _pi);
710 XML_SetCdataSectionHandler(self->expat, _startCDATA, _endCDATA);
713 if (self->lexicalHandler) {
714 XML_SetCommentHandler(self->expat, _comment);
720 - (void)_tearDownParser {
722 XML_ParserFree(self->expat);
727 self->nameBuf = NULL;
728 self->nameBufLen = 0;
732 @end /* ExpatSaxDriver */