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] init];
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])) {
397 val = [NSString stringWithFormat:@"%@ %@",oldValue, mappedValue];
398 [attributes setObject:val forKey:mappedAttr];
401 [attributes setObject:mappedValue forKey:mappedAttr];
404 attrEnum = [attributes keyEnumerator];
405 while ((curAttr = [attrEnum nextObject]) != nil) {
406 [retAttrs addAttribute:curAttr uri:self->prefixURI rawName:curAttr
407 type:@"CDATA" value:[attributes objectForKey:curAttr]];
410 [attributes release];
415 - (VSSaxTag *)_beginTag:(NSString *)_tagName group:(NSString *)_group
416 withAttrs:(SaxAttributes *)_attrs
420 tag = [VSSaxTag beginTag:_tagName group:_group attributes:_attrs];
421 [self->elementList addObject:tag];
425 - (void)_endTag:(NSString *)_tagName {
428 tag = [[VSSaxTag alloc] initEndTag:_tagName];
429 [self->elementList addObject:tag];
430 [tag release]; tag = nil;
433 - (void)_addSubItems:(NSArray *)_items group:(NSString *)_group
434 withData:(NSString *)_content
436 NSEnumerator *itemEnum, *contentEnum;
439 itemEnum = [_items objectEnumerator];
440 contentEnum = [[_content componentsSeparatedByString:@";"] objectEnumerator];
442 while ((subTag = [itemEnum nextObject]) != nil) {
443 NSString *subContent;
445 subContent = [contentEnum nextObject];
447 [self _beginTag:subTag group:_group withAttrs:nil];
448 if ([subContent length] > 0) {
451 a = [(VSSaxTag*)[VSSaxTag alloc] initWithContentString:subContent];
453 [self->elementList addObject:a];
457 [self _endTag:subTag];
461 - (void)_reportContentAsTag:(NSString *)_tagName
462 group:(NSString *)_group
463 withAttrs:(SaxAttributes *)_attrs
464 andContent:(NSString *)_content
467 This is called for all non-BEGIN|END types.
471 _content = [stringFormatter stringByUnescapingRFC2445Text:_content];
473 /* check whether type should be reported as an attribute in XML */
475 if ([self->attributeElements containsObject:_tagName]) {
477 Add tag as an attribute to last component in the cardstack. This is
478 stuff like the "VERSION" type contained in a "BEGIN:VCARD" which will
479 be reported as <vcard version=""> (as an attribute of the container).
483 element = [self->cardStack lastObject];
484 [element->attrs addAttribute:_tagName uri:self->prefixURI
485 rawName:_tagName type:@"CDATA" value:_content];
489 /* report type as an XML tag */
491 [self _beginTag:_tagName group:_group withAttrs:_attrs];
493 if ([_content length] > 0) {
494 if ((subItems = [self->subItemMapping objectForKey:_tagName]) != nil) {
495 [self _addSubItems:subItems group:_group withData:_content];
500 a = [(VSSaxTag *)[VSSaxTag alloc] initWithContentString:_content];
502 [self->elementList addObject:a];
508 [self _endTag:_tagName];
511 /* report events for collected elements */
513 - (void)reportStartGroup:(NSString *)_group {
514 SaxAttributes *attrs;
516 attrs = [[SaxAttributes alloc] init];
517 [attrs addAttribute:@"name" uri:self->prefixURI rawName:@"name"
518 type:@"CDATA" value:_group];
520 [self->contentHandler startElement:@"group" namespace:self->prefixURI
521 rawName:@"group" attributes:attrs];
524 - (void)reportEndGroup {
525 [self->contentHandler endElement:@"group" namespace:self->prefixURI
529 - (void)reportQueuedTags {
531 Why does the parser need the list instead of reporting the events
534 Because some vCard tags like the 'version' are reported as attributes
535 on the container tag. So we have a sequence like:
539 which will get reported as:
540 <vcard version="3.0">
543 VSSaxTag *tagToReport;
547 enu = [self->elementList objectEnumerator];
548 while ((tagToReport = [enu nextObject]) != nil) {
549 if ([tagToReport isStartTag]) {
552 tg = [tagToReport group];
553 if (![lastGroup isEqualToString:tg] && lastGroup != tg) {
554 if (lastGroup != nil) [self reportEndGroup];
555 ASSIGNCOPY(lastGroup, tg);
556 if (lastGroup != nil) [self reportStartGroup:lastGroup];
560 if ([tagToReport isStartTag]) {
561 [self->contentHandler startElement:[tagToReport tagName]
562 namespace:self->prefixURI
563 rawName:[tagToReport tagName]
564 attributes:tagToReport->attrs];
566 else if ([tagToReport isEndTag]) {
567 [self->contentHandler endElement:[tagToReport tagName]
568 namespace:self->prefixURI
569 rawName:[tagToReport tagName]];
572 [self->contentHandler characters:tagToReport->data
573 length:tagToReport->datalen];
577 /* flush event group */
578 [self->elementList removeAllObjects];
580 /* close open groups */
581 if (lastGroup != nil) {
582 [self reportEndGroup];
583 [lastGroup release]; lastGroup = nil;
589 - (void)error:(NSString *)_text {
590 SaxParseException *e;
592 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
595 [self->errorHandler error:e];
597 - (void)warn:(NSString *)_warn {
598 SaxParseException *e;
600 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
603 [self->errorHandler warning:e];
606 /* parsing raw string */
608 - (void)_beginComponentWithValue:(NSString *)tagValue {
611 tag = [self _beginTag:[self _mapTagName:tagValue]
613 withAttrs:[[[SaxAttributes alloc] init] autorelease]];
614 [self->cardStack addObject:tag];
617 - (void)_endComponent:(NSString *)tagName value:(NSString *)tagValue {
620 mtName = [self _mapTagName:tagValue];
621 if ([self->cardStack count] > 0) {
622 NSString *expectedName;
624 expectedName = [(VSSaxTag *)[self->cardStack lastObject] tagName];
625 if (![expectedName isEqualToString:mtName]) {
628 // TODO: rather report an error?
629 // TODO: setup userinfo dict with details
630 s = [NSString stringWithFormat:
631 @"Found end tag '%@' which does not match expected "
633 @" Tag '%@' has not been closed properly. Given "
634 @"document contains errors!",
635 mtName, expectedName, expectedName];
638 /* probably futile attempt to parse anyways */
640 NSLog(@"%s trying to fix previous error by inserting bogus end "
642 __PRETTY_FUNCTION__);
644 [self _endTag:expectedName];
645 [self->cardStack removeLastObject];
649 // TOOD: generate error?
650 [self error:[@"found end tag without any open tags left: "
651 stringByAppendingString:mtName]];
653 [self _endTag:mtName];
654 [self->cardStack removeLastObject];
656 /* report parsed elements */
658 if ([self->cardStack count] == 0)
659 [self reportQueuedTags];
662 - (void)_parseLine:(NSString *)_line {
663 NSString *tagName, *tagValue;
664 NSMutableArray *tagAttributes;
665 NSRange r, todoRange;
668 length = [_line length];
669 todoRange = NSMakeRange(0, length);
670 r = [_line rangeOfCharacterFromSet:colonAndSemicolonCharSet
673 /* is line well-formed? */
675 [self error:[@"got an improper content line! ->\n"
676 stringByAppendingString:_line]];
680 /* tagname is everything up to a ':' or ';' (value or parameter) */
681 tagName = [[_line substringToIndex:r.location] uppercaseString];
682 tagAttributes = [[NSMutableArray alloc] initWithCapacity:16];
685 possible shortcut: if we spotted a ':', we don't have to do "expensive"
686 argument scanning/processing.
688 if ([_line characterAtIndex:r.location] != ':') {
690 BOOL isInDquote = NO;
693 start = NSMaxRange(r);
694 todoRange = NSMakeRange(start, length - start);
698 /* scan for parameters */
699 r = [_line rangeOfCharacterFromSet:colonSemicolonAndDquoteCharSet
703 /* is line well-formed? */
704 if (r.length == 0 || r.location == 0) {
705 [self error:[@"got an improper content line! ->\n"
706 stringByAppendingString:_line]];
707 [tagAttributes release]; tagAttributes = nil;
711 /* first check if delimiter candidate is escaped */
712 if ([_line characterAtIndex:(r.location - 1)] != '\\') {
716 delimiter = [_line characterAtIndex:r.location];
717 if (delimiter == '\"') {
718 /* not a real delimiter - toggle isInDquote for proper escaping */
719 isInDquote = !isInDquote;
723 /* is a delimiter, which one? */
725 if (delimiter == ':') {
728 copyRange = NSMakeRange(start, r.location - start);
729 [tagAttributes addObject:[_line substringWithRange:copyRange]];
731 /* adjust start, todoRange */
732 start = NSMaxRange(r);
733 todoRange = NSMakeRange(start, length - start);
739 /* adjust todoRange */
740 unsigned offset = NSMaxRange(r);
741 todoRange = NSMakeRange(offset, length - offset);
745 tagValue = [_line substringFromIndex:NSMaxRange(r)];
748 At this point we have:
749 name: 'BEGIN', 'TEL', 'EMAIL', 'ITEM1.ADR' etc
750 value: ';;;Magdeburg;;;Germany'
751 atributes: ("type=INTERNET", "type=HOME", "type=pref")
756 if ([tagName isEqualToString:@"BEGIN"]) {
757 if ([tagAttributes count] > 0)
758 [self warn:@"Losing unexpected parameters of BEGIN line."];
759 [self _beginComponentWithValue:tagValue];
761 else if ([tagName isEqualToString:@"END"]) {
762 if ([tagAttributes count] > 0)
763 [self warn:@"Losing unexpected parameters of END line."];
764 [self _endComponent:tagName value:tagValue];
767 [self _reportContentAsTag:[self _mapTagName:tagName]
768 group:[self _groupFromTagName:tagName]
769 withAttrs:[self _mapAttrs:tagAttributes forTag:tagName]
770 andContent:tagValue];
773 [tagAttributes release];
777 /* top level parsing method */
779 - (void)reportDocStart {
780 [self->contentHandler startDocument];
781 [self->contentHandler startPrefixMapping:@"" uri:self->prefixURI];
783 - (void)reportDocEnd {
784 [self->contentHandler endPrefixMapping:@""];
785 [self->contentHandler endDocument];
788 - (void)_parseString:(NSString *)_rawString {
790 This method split the string into content lines for actual vCard
794 contentline = name *(";" param ) ":" value CRLF
795 ; When parsing a content line, folded lines MUST first
798 NSMutableString *line;
799 unsigned pos, length;
802 [self reportDocStart];
806 length = [_rawString length];
807 r = NSMakeRange(0, 0);
808 line = [[NSMutableString alloc] initWithCapacity:75 + 2];
810 for (pos = 0; pos < length; pos++) {
813 c = [_rawString characterAtIndex:pos];
816 if (((length - 1) - pos) >= 1) {
817 if ([_rawString characterAtIndex:pos + 1] == '\n') {
818 BOOL isAtEndOfLine = YES;
820 /* test for folding first */
821 if (((length - 1) - pos) >= 2) {
824 ws = [_rawString characterAtIndex:pos + 2];
825 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO :YES;
826 if (!isAtEndOfLine) {
827 /* assemble part of line up to pos */
829 [line appendString:[_rawString substringWithRange:r]];
833 r = NSMakeRange(pos + 1, 0); /* begin new range */
837 /* assemble part of line up to pos */
839 [line appendString:[_rawString substringWithRange:r]];
841 [self _parseLine:line];
843 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
845 r = NSMakeRange(pos + 1, 0); /* begin new range */
850 /* garbled last line! */
851 [self warn:@"last line is truncated, trying to parse anyways!"];
854 else if (c == '\n') { /* broken, non-standard */
855 BOOL isAtEndOfLine = YES;
857 /* test for folding first */
858 if (((length - 1) - pos) >= 1) {
861 ws = [_rawString characterAtIndex:(pos + 1)];
863 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO : YES;
864 if (!isAtEndOfLine) {
865 /* assemble part of line up to pos */
867 [line appendString:[_rawString substringWithRange:r]];
871 r = NSMakeRange(pos + 1, 0); /* begin new range */
875 /* assemble part of line up to pos */
877 [line appendString:[_rawString substringWithRange:r]];
879 [self _parseLine:line];
881 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
882 r = NSMakeRange(pos + 1, 0); /* begin new range */
890 [self warn:@"Last line of parse string is not properly terminated!"];
891 [line appendString:[_rawString substringWithRange:r]];
892 [self _parseLine:line];
895 if ([self->cardStack count] != 0) {
896 [self warn:@"found elements on cardStack. This indicates an improper "
897 @"nesting structure! Not all required events will have been "
898 @"generated, leading to unpredictable results!"];
899 [self->cardStack removeAllObjects]; // clean up
902 [line release]; line = nil;
907 /* main entry functions */
909 - (id)sourceForData:(NSData *)_data systemId:(NSString *)_sysId {
910 SaxParseException *e = nil;
911 NSStringEncoding encoding;
913 const unsigned char *bytes;
917 NSLog(@"%s: trying to decode data (0x%08X,len=%d) ...",
918 __PRETTY_FUNCTION__, _data, [_data length]);
921 if ((len = [_data length]) == 0) {
922 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
923 reason:@"Got no parsing data!"
925 [self->errorHandler fatalError:e];
929 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
930 reason:@"Input data to short for vCard!"
932 [self->errorHandler fatalError:e];
936 bytes = [_data bytes];
937 if ((bytes[0] == 0xFF && bytes[1] == 0xFE) ||
938 (bytes[0] == 0xFE && bytes[1] == 0xFF)) {
939 encoding = NSUnicodeStringEncoding;
942 encoding = NSUTF8StringEncoding;
944 // FIXME: Data is not always utf-8.....
945 source = [[[NSString alloc] initWithData:_data encoding:encoding]
948 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
949 reason:@"Could not convert input to string!"
951 [self->errorHandler fatalError:e];
956 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
958 NSLog(@"%s: parse: %@ (sysid=%@)", __PRETTY_FUNCTION__, _source, _sysId);
960 if ([_source isKindOfClass:[NSURL class]]) {
961 if (_sysId == nil) _sysId = [_source absoluteString];
964 NSLog(@"%s: trying to load URL: %@ (sysid=%@)",__PRETTY_FUNCTION__,
968 // TODO: remember encoding of source
969 _source = [_source resourceDataUsingCache:NO];
972 if ([_source isKindOfClass:[NSData class]]) {
973 if (_sysId == nil) _sysId = @"<data>";
974 if ((_source = [self sourceForData:_source systemId:_sysId]) == nil)
978 if (![_source isKindOfClass:[NSString class]]) {
979 SaxParseException *e;
983 NSLog(@"%s: unrecognizable source: %@", __PRETTY_FUNCTION__,_source);
985 s = [@"cannot handle data-source: " stringByAppendingString:
986 [_source description]];
987 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
991 [self->errorHandler fatalError:e];
995 /* ensure consistent state */
997 [self->cardStack removeAllObjects];
998 [self->elementList removeAllObjects];
1003 NSLog(@"%s: trying to parse string (0x%08X,len=%d) ...",
1004 __PRETTY_FUNCTION__, _source, [_source length]);
1006 if (_sysId == nil) _sysId = @"<string>";
1007 [self _parseString:_source];
1011 [self->cardStack removeAllObjects];
1012 [self->elementList removeAllObjects];
1015 - (void)parseFromSource:(id)_source {
1016 [self parseFromSource:_source systemId:nil];
1019 - (void)parseFromSystemId:(NSString *)_sysId {
1022 if ([_sysId rangeOfString:@"://"].length == 0) {
1023 /* seems to be a path, path to be a proper URL */
1024 url = [NSURL fileURLWithPath:_sysId];
1027 /* Note: Cocoa NSURL doesn't complain on "/abc/def" like input! */
1028 url = [NSURL URLWithString:_sysId];
1032 SaxParseException *e;
1034 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1035 reason:@"cannot handle system-id"
1037 [self->errorHandler fatalError:e];
1041 [self parseFromSource:url systemId:_sysId];
1046 - (BOOL)isDebuggingEnabled {
1050 @end /* VersitSaxDriver */