2 Copyright (C) 2003-2004 Max Berger
3 Copyright (C) 2004-2005 OpenGroupware.org
5 This file is part of versitSaxDriver, written for the OpenGroupware.org
8 SOPE is free software; you can redistribute it and/or modify it under
9 the terms of the GNU Lesser General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
13 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with SOPE; see the file COPYING. If not, write to the
20 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
24 #include "VSSaxDriver.h"
25 #include "VSStringFormatter.h"
26 #include <SaxObjC/SaxException.h>
29 @interface VSSaxTag : NSObject
41 + (id)beginTag:(NSString *)_tag group:(NSString *)_group
42 attributes:(SaxAttributes *)_attrs;
43 - (id)initEndTag:(NSString *)_tag;
45 - (id)initWithContentString:(NSString *)_data;
47 - (NSString *)tagName;
54 @implementation VSSaxTag
56 + (id)beginTag:(NSString *)_tag group:(NSString *)_group
57 attributes:(SaxAttributes *)_attrs
61 tag = [[[self alloc] init] autorelease];
63 tag->tagName = [_tag copy];
64 tag->group = [_group copy];
65 tag->attrs = [_attrs retain];
68 - (id)initEndTag:(NSString *)_tag {
70 self->tagName = [_tag copy];
73 - (id)initWithContentString:(NSString *)_data {
79 self->datalen = [_data length];
80 self->data = calloc(self->datalen + 1, sizeof(unichar));
81 [_data getCharacters:self->data range:NSMakeRange(0, self->datalen)];
86 if (self->data) free(self->data);
87 [self->group release];
88 [self->tagName release];
89 [self->attrs release];
95 - (NSString *)tagName {
103 return self->type == 'B' ? YES : NO;
106 return self->type == 'E' ? YES : NO;
109 return (self->type == 'B' || self->type == 'E') ? YES : NO;
114 @implementation VSSaxDriver
116 static BOOL debugOn = NO;
118 static NSCharacterSet *dotCharSet = nil;
119 static NSCharacterSet *equalSignCharSet = nil;
120 static NSCharacterSet *commaCharSet = nil;
121 static NSCharacterSet *colonAndSemicolonCharSet = nil;
122 static NSCharacterSet *colonSemicolonAndDquoteCharSet = nil;
123 static NSCharacterSet *whitespaceCharSet = nil;
125 static VSStringFormatter *stringFormatter = nil;
128 static BOOL didInit = NO;
135 ud = [NSUserDefaults standardUserDefaults];
136 debugOn = [ud boolForKey:@"VSSaxDriverDebugEnabled"];
139 [[NSCharacterSet characterSetWithCharactersInString:@"."] retain];
141 [[NSCharacterSet characterSetWithCharactersInString:@"="] retain];
143 [[NSCharacterSet characterSetWithCharactersInString:@","] retain];
144 colonAndSemicolonCharSet =
145 [[NSCharacterSet characterSetWithCharactersInString:@":;"] retain];
146 colonSemicolonAndDquoteCharSet =
147 [[NSCharacterSet characterSetWithCharactersInString:@":;\""] retain];
149 [[NSCharacterSet whitespaceCharacterSet] retain];
151 stringFormatter = [VSStringFormatter sharedFormatter];
156 if ((self = [super init])) {
157 self->prefixURI = @"";
158 self->cardStack = [[NSMutableArray alloc] initWithCapacity:4];
159 self->elementList = [[NSMutableArray alloc] initWithCapacity:8];
160 self->attributeMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
161 self->subItemMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
167 [self->contentHandler release];
168 [self->errorHandler release];
169 [self->prefixURI release];
170 [self->cardStack release];
171 [self->elementList release];
172 [self->attributeElements release];
173 [self->elementMapping release];
174 [self->attributeMapping release];
175 [self->subItemMapping release];
181 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
183 - (BOOL)feature:(NSString *)_name {
187 - (void)setProperty:(NSString *)_name to:(id)_value {
189 - (id)property:(NSString *)_name {
195 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
196 ASSIGN(self->contentHandler, _handler);
199 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
203 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
204 ASSIGN(self->errorHandler, _handler);
206 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
210 - (id<NSObject,SaxContentHandler>)contentHandler {
211 return self->contentHandler;
214 - (id<NSObject,SaxDTDHandler>)dtdHandler {
219 - (id<NSObject,SaxErrorHandler>)errorHandler {
220 return self->errorHandler;
222 - (id<NSObject,SaxEntityResolver>)entityResolver {
227 - (void)setPrefixURI:(NSString *)_uri {
228 ASSIGNCOPY(self->prefixURI, _uri);
230 - (NSString *)prefixURI {
231 return self->prefixURI;
234 - (void)setAttributeElements:(NSSet *)_elements {
235 ASSIGNCOPY(self->attributeElements, _elements);
237 - (NSSet *)attributeElements {
238 return self->attributeElements;
241 - (void)setElementMapping:(NSDictionary *)_mapping {
242 ASSIGNCOPY(self->elementMapping, _mapping);
244 - (NSDictionary *)elementMapping {
245 return self->elementMapping;
248 - (void)setAttributeMapping:(NSDictionary *)_mapping {
249 [self setAttributeMapping:_mapping forElement:@""];
252 - (void)setAttributeMapping:(NSDictionary *)_mapping
253 forElement:(NSString *)_element
257 [attributeMapping setObject:_mapping forKey:_element];
260 - (void)setSubItemMapping:(NSArray *)_mapping forElement:(NSString *)_element {
261 [subItemMapping setObject:_mapping forKey:_element];
268 - (NSString *)_groupFromTagName:(NSString *)_tagName {
271 r = [_tagName rangeOfCharacterFromSet:dotCharSet];
275 return [_tagName substringToIndex:r.location];
278 - (NSString *)_mapTagName:(NSString *)_tagName {
282 if ((ret = [self->elementMapping objectForKey:_tagName]) != nil)
285 //NSLog(@"Unknown Key: %@ in %@",_tagName,self->elementMapping);
289 This is to allow parsing of vCards produced by Apple
291 The dot-notation is described as 'grouping' in RFC 2425, section 5.8.2.
293 r = [_tagName rangeOfCharacterFromSet:dotCharSet];
295 ret = [self _mapTagName:[_tagName substringFromIndex:(r.location + 1)]];
300 - (NSString *)_mapAttrName:(NSString *)_attrName forTag:(NSString *)_tagName {
301 NSDictionary *tagMap;
302 NSString *mappedName;
304 /* check whether we have a attr-map stored under the element-name */
305 tagMap = [self->attributeMapping objectForKey:_tagName];
306 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
309 /* check whether we have a attr-map stored under the mapped element-name */
310 tagMap = [self->attributeMapping objectForKey:[self _mapTagName:_tagName]];
311 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
314 /* check whether we have a global attr-map */
315 tagMap = [self->attributeMapping objectForKey:@""];
316 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
319 /* return the name as-is */
323 - (void)_parseAttr:(NSString *)_attr
324 forTag:(NSString *)_tagName
325 intoAttr:(NSString **)attr_
326 intoValue:(NSString **)value_
329 NSString *attrName, *attrValue, *mappedName;
331 r = [_attr rangeOfCharacterFromSet:equalSignCharSet];
333 unsigned left, right;
335 attrName = [[_attr substringToIndex:r.location] uppercaseString];
336 left = NSMaxRange(r);
337 right = [_attr length] - 1;
339 if (([_attr characterAtIndex:left] == '"') &&
340 ([_attr characterAtIndex:right] == '"'))
343 r = NSMakeRange(left, right - left);
344 attrValue = [_attr substringWithRange:r];
347 attrValue = [_attr substringFromIndex:left];
350 else if (left == right) {
351 attrValue = [_attr substringFromIndex:left];
363 // ZNeK: what's this for?
364 r = [attrValue rangeOfCharacterFromSet:commaCharSet];
365 while (r.length > 0) {
366 [attrValue replaceCharactersInRange:r withString:@" "];
367 r = [attrValue rangeOfCharacterFromSet:commaCharSet];
371 mappedName = [self _mapAttrName:attrName forTag:_tagName];
373 *value_ = [stringFormatter stringByUnescapingRFC2445Text:attrValue];
376 - (SaxAttributes *)_mapAttrs:(NSArray *)_attrs forTag:(NSString *)_tagName {
377 SaxAttributes *retAttrs;
378 NSEnumerator *attrEnum;
379 NSString *curAttr, *mappedAttr, *mappedValue, *oldValue;
380 NSMutableDictionary *attributes;
382 if (_attrs == nil || [_attrs count] == 0)
385 attributes = [[NSMutableDictionary alloc] initWithCapacity:4];
386 retAttrs = [[[SaxAttributes alloc] init] autorelease];
388 attrEnum = [_attrs objectEnumerator];
389 while ((curAttr = [attrEnum nextObject]) != nil) {
390 [self _parseAttr:curAttr
393 intoValue:&mappedValue];
394 if ((oldValue = [attributes objectForKey:mappedAttr]) != nil) {
398 // TODO: hh asks: what does 'duh' is supposed to mean?
399 val = [[NSString alloc] initWithFormat:@"%@ %@",oldValue, mappedValue];
400 [attributes setObject:val forKey:mappedAttr];
404 [attributes setObject:mappedValue forKey:mappedAttr];
407 attrEnum = [attributes keyEnumerator];
408 while ((curAttr = [attrEnum nextObject]) != nil) {
410 TODO: values are not always mapped to CDATA! Eg in the dawson draft:
411 | TYPE for TEL | tel.type | NMTOKENS | 'VOICE' |
412 | TYPE for EMAIL | email.type | NMTOKENS | 'INTERNET' |
413 | TYPE for PHOTO,| img.type | CDATA | REQUIRED |
415 | TYPE for SOUND | aud.type | CDATA | REQUIRED |
416 | VALUE | value | NOTATION | See elements |
419 [retAttrs addAttribute:curAttr uri:self->prefixURI rawName:curAttr
420 type:@"CDATA" value:[attributes objectForKey:curAttr]];
423 [attributes release];
428 - (VSSaxTag *)_beginTag:(NSString *)_tagName group:(NSString *)_group
429 withAttrs:(SaxAttributes *)_attrs
433 tag = [VSSaxTag beginTag:_tagName group:_group attributes:_attrs];
434 [self->elementList addObject:tag];
438 - (void)_endTag:(NSString *)_tagName {
441 tag = [[VSSaxTag alloc] initEndTag:_tagName];
442 [self->elementList addObject:tag];
443 [tag release]; tag = nil;
446 - (void)_addSubItems:(NSArray *)_items group:(NSString *)_group
447 withData:(NSString *)_content
449 NSEnumerator *itemEnum, *contentEnum;
452 itemEnum = [_items objectEnumerator];
453 contentEnum = [[_content componentsSeparatedByString:@";"] objectEnumerator];
455 while ((subTag = [itemEnum nextObject]) != nil) {
456 NSString *subContent;
458 subContent = [contentEnum nextObject];
460 [self _beginTag:subTag group:_group withAttrs:nil];
461 if ([subContent length] > 0) {
464 a = [(VSSaxTag*)[VSSaxTag alloc] initWithContentString:subContent];
466 [self->elementList addObject:a];
470 [self _endTag:subTag];
474 - (void)_reportContentAsTag:(NSString *)_tagName
475 group:(NSString *)_group
476 withAttrs:(SaxAttributes *)_attrs
477 andContent:(NSString *)_content
480 This is called for all non-BEGIN|END types.
484 _content = [stringFormatter stringByUnescapingRFC2445Text:_content];
486 /* check whether type should be reported as an attribute in XML */
488 if ([self->attributeElements containsObject:_tagName]) {
490 Add tag as an attribute to last component in the cardstack. This is
491 stuff like the "VERSION" type contained in a "BEGIN:VCARD" which will
492 be reported as <vcard version=""> (as an attribute of the container).
496 element = [self->cardStack lastObject];
497 [element->attrs addAttribute:_tagName uri:self->prefixURI
498 rawName:_tagName type:@"CDATA" value:_content];
502 /* report type as an XML tag */
504 [self _beginTag:_tagName group:_group withAttrs:_attrs];
506 if ([_content length] > 0) {
507 if ((subItems = [self->subItemMapping objectForKey:_tagName]) != nil) {
508 [self _addSubItems:subItems group:_group withData:_content];
513 a = [(VSSaxTag *)[VSSaxTag alloc] initWithContentString:_content];
515 [self->elementList addObject:a];
521 [self _endTag:_tagName];
524 /* report events for collected elements */
526 - (void)reportStartGroup:(NSString *)_group {
527 SaxAttributes *attrs;
529 attrs = [[SaxAttributes alloc] init];
530 [attrs addAttribute:@"name" uri:self->prefixURI rawName:@"name"
531 type:@"CDATA" value:_group];
533 [self->contentHandler startElement:@"group" namespace:self->prefixURI
534 rawName:@"group" attributes:attrs];
537 - (void)reportEndGroup {
538 [self->contentHandler endElement:@"group" namespace:self->prefixURI
542 - (void)reportQueuedTags {
544 Why does the parser need the list instead of reporting the events
547 Because some vCard tags like the 'version' are reported as attributes
548 on the container tag. So we have a sequence like:
552 which will get reported as:
553 <vcard version="3.0">
556 VSSaxTag *tagToReport;
560 enu = [self->elementList objectEnumerator];
561 while ((tagToReport = [enu nextObject]) != nil) {
562 if ([tagToReport isStartTag]) {
565 tg = [tagToReport group];
566 if (![lastGroup isEqualToString:tg] && lastGroup != tg) {
567 if (lastGroup != nil) [self reportEndGroup];
568 ASSIGNCOPY(lastGroup, tg);
569 if (lastGroup != nil) [self reportStartGroup:lastGroup];
573 if ([tagToReport isStartTag]) {
574 [self->contentHandler startElement:[tagToReport tagName]
575 namespace:self->prefixURI
576 rawName:[tagToReport tagName]
577 attributes:tagToReport->attrs];
579 else if ([tagToReport isEndTag]) {
580 [self->contentHandler endElement:[tagToReport tagName]
581 namespace:self->prefixURI
582 rawName:[tagToReport tagName]];
585 [self->contentHandler characters:tagToReport->data
586 length:tagToReport->datalen];
590 /* flush event group */
591 [self->elementList removeAllObjects];
593 /* close open groups */
594 if (lastGroup != nil) {
595 [self reportEndGroup];
596 [lastGroup release]; lastGroup = nil;
602 - (void)error:(NSString *)_text {
603 SaxParseException *e;
605 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
608 [self->errorHandler error:e];
610 - (void)warn:(NSString *)_warn {
611 SaxParseException *e;
613 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
616 [self->errorHandler warning:e];
619 /* parsing raw string */
621 - (void)_beginComponentWithValue:(NSString *)tagValue {
624 tag = [self _beginTag:[self _mapTagName:tagValue]
626 withAttrs:[[[SaxAttributes alloc] init] autorelease]];
627 [self->cardStack addObject:tag];
630 - (void)_endComponent:(NSString *)tagName value:(NSString *)tagValue {
633 mtName = [self _mapTagName:tagValue];
634 if ([self->cardStack count] > 0) {
635 NSString *expectedName;
637 expectedName = [(VSSaxTag *)[self->cardStack lastObject] tagName];
638 if (![expectedName isEqualToString:mtName]) {
641 // TODO: rather report an error?
642 // TODO: setup userinfo dict with details
643 s = [NSString stringWithFormat:
644 @"Found end tag '%@' which does not match expected "
646 @" Tag '%@' has not been closed properly. Given "
647 @"document contains errors!",
648 mtName, expectedName, expectedName];
651 /* probably futile attempt to parse anyways */
653 NSLog(@"%s trying to fix previous error by inserting bogus end "
655 __PRETTY_FUNCTION__);
657 [self _endTag:expectedName];
658 [self->cardStack removeLastObject];
662 // TOOD: generate error?
663 [self error:[@"found end tag without any open tags left: "
664 stringByAppendingString:mtName]];
666 [self _endTag:mtName];
667 [self->cardStack removeLastObject];
669 /* report parsed elements */
671 if ([self->cardStack count] == 0)
672 [self reportQueuedTags];
675 - (void)_parseLine:(NSString *)_line {
676 NSString *tagName, *tagValue;
677 NSMutableArray *tagAttributes;
678 NSRange r, todoRange;
681 length = [_line length];
682 todoRange = NSMakeRange(0, length);
683 r = [_line rangeOfCharacterFromSet:colonAndSemicolonCharSet
686 /* is line well-formed? */
688 [self error:[@"got an improper content line! ->\n"
689 stringByAppendingString:_line]];
693 /* tagname is everything up to a ':' or ';' (value or parameter) */
694 tagName = [[_line substringToIndex:r.location] uppercaseString];
695 tagAttributes = [[NSMutableArray alloc] initWithCapacity:16];
698 possible shortcut: if we spotted a ':', we don't have to do "expensive"
699 argument scanning/processing.
701 if ([_line characterAtIndex:r.location] != ':') {
703 BOOL isInDquote = NO;
706 start = NSMaxRange(r);
707 todoRange = NSMakeRange(start, length - start);
711 /* scan for parameters */
712 r = [_line rangeOfCharacterFromSet:colonSemicolonAndDquoteCharSet
716 /* is line well-formed? */
717 if (r.length == 0 || r.location == 0) {
718 [self error:[@"got an improper content line! ->\n"
719 stringByAppendingString:_line]];
720 [tagAttributes release]; tagAttributes = nil;
724 /* first check if delimiter candidate is escaped */
725 if ([_line characterAtIndex:(r.location - 1)] != '\\') {
729 delimiter = [_line characterAtIndex:r.location];
730 if (delimiter == '\"') {
731 /* not a real delimiter - toggle isInDquote for proper escaping */
732 isInDquote = !isInDquote;
736 /* is a delimiter, which one? */
738 if (delimiter == ':') {
741 copyRange = NSMakeRange(start, r.location - start);
742 [tagAttributes addObject:[_line substringWithRange:copyRange]];
744 /* adjust start, todoRange */
745 start = NSMaxRange(r);
746 todoRange = NSMakeRange(start, length - start);
752 /* adjust todoRange */
753 unsigned offset = NSMaxRange(r);
754 todoRange = NSMakeRange(offset, length - offset);
758 tagValue = [_line substringFromIndex:NSMaxRange(r)];
761 At this point we have:
762 name: 'BEGIN', 'TEL', 'EMAIL', 'ITEM1.ADR' etc
763 value: ';;;Magdeburg;;;Germany'
764 atributes: ("type=INTERNET", "type=HOME", "type=pref")
769 if ([tagName isEqualToString:@"BEGIN"]) {
770 if ([tagAttributes count] > 0)
771 [self warn:@"Losing unexpected parameters of BEGIN line."];
772 [self _beginComponentWithValue:tagValue];
774 else if ([tagName isEqualToString:@"END"]) {
775 if ([tagAttributes count] > 0)
776 [self warn:@"Losing unexpected parameters of END line."];
777 [self _endComponent:tagName value:tagValue];
780 [self _reportContentAsTag:[self _mapTagName:tagName]
781 group:[self _groupFromTagName:tagName]
782 withAttrs:[self _mapAttrs:tagAttributes forTag:tagName]
783 andContent:tagValue];
786 [tagAttributes release];
790 /* top level parsing method */
792 - (void)reportDocStart {
793 [self->contentHandler startDocument];
794 [self->contentHandler startPrefixMapping:@"" uri:self->prefixURI];
796 - (void)reportDocEnd {
797 [self->contentHandler endPrefixMapping:@""];
798 [self->contentHandler endDocument];
801 - (void)_parseString:(NSString *)_rawString {
803 This method split the string into content lines for actual vCard
807 contentline = name *(";" param ) ":" value CRLF
808 ; When parsing a content line, folded lines MUST first
811 NSMutableString *line;
812 unsigned pos, length;
815 [self reportDocStart];
819 length = [_rawString length];
820 r = NSMakeRange(0, 0);
821 line = [[NSMutableString alloc] initWithCapacity:75 + 2];
823 for (pos = 0; pos < length; pos++) {
826 c = [_rawString characterAtIndex:pos];
829 if (((length - 1) - pos) >= 1) {
830 if ([_rawString characterAtIndex:pos + 1] == '\n') {
831 BOOL isAtEndOfLine = YES;
833 /* test for folding first */
834 if (((length - 1) - pos) >= 2) {
837 ws = [_rawString characterAtIndex:pos + 2];
838 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO :YES;
839 if (!isAtEndOfLine) {
840 /* assemble part of line up to pos */
842 [line appendString:[_rawString substringWithRange:r]];
846 r = NSMakeRange(pos + 1, 0); /* begin new range */
850 /* assemble part of line up to pos */
852 [line appendString:[_rawString substringWithRange:r]];
854 [self _parseLine:line];
856 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
858 r = NSMakeRange(pos + 1, 0); /* begin new range */
863 /* garbled last line! */
864 [self warn:@"last line is truncated, trying to parse anyways!"];
867 else if (c == '\n') { /* broken, non-standard */
868 BOOL isAtEndOfLine = YES;
870 /* test for folding first */
871 if (((length - 1) - pos) >= 1) {
874 ws = [_rawString characterAtIndex:(pos + 1)];
876 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO : YES;
877 if (!isAtEndOfLine) {
878 /* assemble part of line up to pos */
880 [line appendString:[_rawString substringWithRange:r]];
884 r = NSMakeRange(pos + 1, 0); /* begin new range */
888 /* assemble part of line up to pos */
890 [line appendString:[_rawString substringWithRange:r]];
892 [self _parseLine:line];
894 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
895 r = NSMakeRange(pos + 1, 0); /* begin new range */
903 [self warn:@"Last line of parse string is not properly terminated!"];
904 [line appendString:[_rawString substringWithRange:r]];
905 [self _parseLine:line];
908 if ([self->cardStack count] != 0) {
909 [self warn:@"found elements on cardStack. This indicates an improper "
910 @"nesting structure! Not all required events will have been "
911 @"generated, leading to unpredictable results!"];
912 [self->cardStack removeAllObjects]; // clean up
915 [line release]; line = nil;
920 /* main entry functions */
922 - (id)sourceForData:(NSData *)_data systemId:(NSString *)_sysId {
923 SaxParseException *e = nil;
924 NSStringEncoding encoding;
926 const unsigned char *bytes;
930 NSLog(@"%s: trying to decode data (0x%08X,len=%d) ...",
931 __PRETTY_FUNCTION__, _data, [_data length]);
934 if ((len = [_data length]) == 0) {
935 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
936 reason:@"Got no parsing data!"
938 [self->errorHandler fatalError:e];
942 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
943 reason:@"Input data to short for vCard!"
945 [self->errorHandler fatalError:e];
949 bytes = [_data bytes];
950 if ((bytes[0] == 0xFF && bytes[1] == 0xFE) ||
951 (bytes[0] == 0xFE && bytes[1] == 0xFF)) {
952 encoding = NSUnicodeStringEncoding;
955 encoding = NSUTF8StringEncoding;
957 // FIXME: Data is not always utf-8.....
958 source = [[[NSString alloc] initWithData:_data encoding:encoding]
961 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
962 reason:@"Could not convert input to string!"
964 [self->errorHandler fatalError:e];
969 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
971 NSLog(@"%s: parse: %@ (sysid=%@)", __PRETTY_FUNCTION__, _source, _sysId);
973 if ([_source isKindOfClass:[NSURL class]]) {
974 if (_sysId == nil) _sysId = [_source absoluteString];
977 NSLog(@"%s: trying to load URL: %@ (sysid=%@)",__PRETTY_FUNCTION__,
981 // TODO: remember encoding of source
982 _source = [_source resourceDataUsingCache:NO];
985 if ([_source isKindOfClass:[NSData class]]) {
986 if (_sysId == nil) _sysId = @"<data>";
987 if ((_source = [self sourceForData:_source systemId:_sysId]) == nil)
991 if (![_source isKindOfClass:[NSString class]]) {
992 SaxParseException *e;
996 NSLog(@"%s: unrecognizable source: %@", __PRETTY_FUNCTION__,_source);
998 s = [@"cannot handle data-source: " stringByAppendingString:
999 [_source description]];
1000 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1004 [self->errorHandler fatalError:e];
1008 /* ensure consistent state */
1010 [self->cardStack removeAllObjects];
1011 [self->elementList removeAllObjects];
1016 NSLog(@"%s: trying to parse string (0x%08X,len=%d) ...",
1017 __PRETTY_FUNCTION__, _source, [_source length]);
1019 if (_sysId == nil) _sysId = @"<string>";
1020 [self _parseString:_source];
1024 [self->cardStack removeAllObjects];
1025 [self->elementList removeAllObjects];
1028 - (void)parseFromSource:(id)_source {
1029 [self parseFromSource:_source systemId:nil];
1032 - (void)parseFromSystemId:(NSString *)_sysId {
1035 if ([_sysId rangeOfString:@"://"].length == 0) {
1036 /* seems to be a path, path to be a proper URL */
1037 url = [NSURL fileURLWithPath:_sysId];
1040 /* Note: Cocoa NSURL doesn't complain on "/abc/def" like input! */
1041 url = [NSURL URLWithString:_sysId];
1045 SaxParseException *e;
1047 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1048 reason:@"cannot handle system-id"
1050 [self->errorHandler fatalError:e];
1054 [self parseFromSource:url systemId:_sysId];
1059 - (BOOL)isDebuggingEnabled {
1063 @end /* VersitSaxDriver */