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>
27 #include <NGExtensions/NGQuotedPrintableCoding.h>
30 @interface VSSaxTag : NSObject
42 + (id)beginTag:(NSString *)_tag group:(NSString *)_group
43 attributes:(SaxAttributes *)_attrs;
44 - (id)initEndTag:(NSString *)_tag;
46 - (id)initWithContentString:(NSString *)_data;
48 - (NSString *)tagName;
56 @implementation VSSaxTag
58 + (id)beginTag:(NSString *)_tag group:(NSString *)_group
59 attributes:(SaxAttributes *)_attrs
63 tag = [[[self alloc] init] autorelease];
65 tag->tagName = [_tag copy];
66 tag->group = [_group copy];
67 tag->attrs = [_attrs retain];
70 - (id)initEndTag:(NSString *)_tag {
72 self->tagName = [_tag copy];
75 - (id)initWithContentString:(NSString *)_data {
81 self->datalen = [_data length];
82 self->data = calloc(self->datalen + 1, sizeof(unichar));
83 [_data getCharacters:self->data range:NSMakeRange(0, self->datalen)];
88 if (self->data) free(self->data);
89 [self->group release];
90 [self->tagName release];
91 [self->attrs release];
97 - (NSString *)tagName {
100 - (NSString *)group {
105 return self->type == 'B' ? YES : NO;
108 return self->type == 'E' ? YES : NO;
111 return (self->type == 'B' || self->type == 'E') ? YES : NO;
116 @implementation VSSaxDriver
118 static BOOL debugOn = NO;
120 static NSCharacterSet *dotCharSet = nil;
121 static NSCharacterSet *equalSignCharSet = nil;
122 static NSCharacterSet *commaCharSet = nil;
123 static NSCharacterSet *colonAndSemicolonCharSet = nil;
124 static NSCharacterSet *colonSemicolonAndDquoteCharSet = nil;
125 static NSCharacterSet *whitespaceCharSet = nil;
127 static VSStringFormatter *stringFormatter = nil;
130 static BOOL didInit = NO;
137 ud = [NSUserDefaults standardUserDefaults];
138 debugOn = [ud boolForKey:@"VSSaxDriverDebugEnabled"];
141 [[NSCharacterSet characterSetWithCharactersInString:@"."] retain];
143 [[NSCharacterSet characterSetWithCharactersInString:@"="] retain];
145 [[NSCharacterSet characterSetWithCharactersInString:@","] retain];
146 colonAndSemicolonCharSet =
147 [[NSCharacterSet characterSetWithCharactersInString:@":;"] retain];
148 colonSemicolonAndDquoteCharSet =
149 [[NSCharacterSet characterSetWithCharactersInString:@":;\""] retain];
151 [[NSCharacterSet whitespaceCharacterSet] retain];
153 stringFormatter = [VSStringFormatter sharedFormatter];
158 if ((self = [super init])) {
159 self->prefixURI = @"";
160 self->cardStack = [[NSMutableArray alloc] initWithCapacity:4];
161 self->elementList = [[NSMutableArray alloc] initWithCapacity:8];
162 self->attributeMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
163 self->subItemMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
169 [self->contentHandler release];
170 [self->errorHandler release];
171 [self->prefixURI release];
172 [self->cardStack release];
173 [self->elementList release];
174 [self->attributeElements release];
175 [self->elementMapping release];
176 [self->attributeMapping release];
177 [self->subItemMapping release];
183 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
185 - (BOOL)feature:(NSString *)_name {
189 - (void)setProperty:(NSString *)_name to:(id)_value {
191 - (id)property:(NSString *)_name {
197 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
198 ASSIGN(self->contentHandler, _handler);
201 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
205 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
206 ASSIGN(self->errorHandler, _handler);
208 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
212 - (id<NSObject,SaxContentHandler>)contentHandler {
213 return self->contentHandler;
216 - (id<NSObject,SaxDTDHandler>)dtdHandler {
221 - (id<NSObject,SaxErrorHandler>)errorHandler {
222 return self->errorHandler;
224 - (id<NSObject,SaxEntityResolver>)entityResolver {
229 - (void)setPrefixURI:(NSString *)_uri {
230 ASSIGNCOPY(self->prefixURI, _uri);
232 - (NSString *)prefixURI {
233 return self->prefixURI;
236 - (void)setAttributeElements:(NSSet *)_elements {
237 ASSIGNCOPY(self->attributeElements, _elements);
239 - (NSSet *)attributeElements {
240 return self->attributeElements;
243 - (void)setElementMapping:(NSDictionary *)_mapping {
244 ASSIGNCOPY(self->elementMapping, _mapping);
246 - (NSDictionary *)elementMapping {
247 return self->elementMapping;
250 - (void)setAttributeMapping:(NSDictionary *)_mapping {
251 [self setAttributeMapping:_mapping forElement:@""];
254 - (void)setAttributeMapping:(NSDictionary *)_mapping
255 forElement:(NSString *)_element
259 [attributeMapping setObject:_mapping forKey:_element];
262 - (void)setSubItemMapping:(NSArray *)_mapping forElement:(NSString *)_element {
263 [subItemMapping setObject:_mapping forKey:_element];
270 - (NSString *)_groupFromTagName:(NSString *)_tagName {
273 r = [_tagName rangeOfCharacterFromSet:dotCharSet];
277 return [_tagName substringToIndex:r.location];
280 - (NSString *)_mapTagName:(NSString *)_tagName {
284 if ((ret = [self->elementMapping objectForKey:_tagName]) != nil)
287 //NSLog(@"Unknown Key: %@ in %@",_tagName,self->elementMapping);
291 This is to allow parsing of vCards produced by Apple
293 The dot-notation is described as 'grouping' in RFC 2425, section 5.8.2.
295 r = [_tagName rangeOfCharacterFromSet:dotCharSet];
297 ret = [self _mapTagName:[_tagName substringFromIndex:(r.location + 1)]];
302 - (NSString *)_mapAttrName:(NSString *)_attrName forTag:(NSString *)_tagName {
303 NSDictionary *tagMap;
304 NSString *mappedName;
306 /* check whether we have a attr-map stored under the element-name */
307 tagMap = [self->attributeMapping objectForKey:_tagName];
308 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
311 /* check whether we have a attr-map stored under the mapped element-name */
312 tagMap = [self->attributeMapping objectForKey:[self _mapTagName:_tagName]];
313 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
316 /* check whether we have a global attr-map */
317 tagMap = [self->attributeMapping objectForKey:@""];
318 if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
321 /* return the name as-is */
325 - (void)_parseAttr:(NSString *)_attr
326 forTag:(NSString *)_tagName
327 intoAttr:(NSString **)attr_
328 intoValue:(NSString **)value_
331 NSString *attrName, *attrValue, *mappedName;
333 r = [_attr rangeOfCharacterFromSet:equalSignCharSet];
335 unsigned left, right;
337 attrName = [[_attr substringToIndex:r.location] uppercaseString];
338 left = NSMaxRange(r);
339 right = [_attr length] - 1;
341 if (([_attr characterAtIndex:left] == '"') &&
342 ([_attr characterAtIndex:right] == '"'))
345 r = NSMakeRange(left, right - left);
346 attrValue = [_attr substringWithRange:r];
349 attrValue = [_attr substringFromIndex:left];
352 else if (left == right) {
353 attrValue = [_attr substringFromIndex:left];
365 // ZNeK: what's this for?
366 r = [attrValue rangeOfCharacterFromSet:commaCharSet];
367 while (r.length > 0) {
368 [attrValue replaceCharactersInRange:r withString:@" "];
369 r = [attrValue rangeOfCharacterFromSet:commaCharSet];
373 mappedName = [self _mapAttrName:attrName forTag:_tagName];
375 *value_ = [stringFormatter stringByUnescapingRFC2445Text:attrValue];
378 - (SaxAttributes *)_mapAttrs:(NSArray *)_attrs forTag:(NSString *)_tagName {
379 SaxAttributes *retAttrs;
380 NSEnumerator *attrEnum;
381 NSString *curAttr, *mappedAttr, *mappedValue, *oldValue;
382 NSMutableDictionary *attributes;
384 if (_attrs == nil || [_attrs count] == 0)
387 attributes = [[NSMutableDictionary alloc] initWithCapacity:4];
388 retAttrs = [[[SaxAttributes alloc] init] autorelease];
390 attrEnum = [_attrs objectEnumerator];
391 while ((curAttr = [attrEnum nextObject]) != nil) {
392 [self _parseAttr:curAttr
395 intoValue:&mappedValue];
396 if ((oldValue = [attributes objectForKey:mappedAttr]) != nil) {
400 // TODO: hh asks: what does 'duh' is supposed to mean?
401 val = [[NSString alloc] initWithFormat:@"%@ %@",oldValue, mappedValue];
402 [attributes setObject:val forKey:mappedAttr];
406 [attributes setObject:mappedValue forKey:mappedAttr];
409 attrEnum = [attributes keyEnumerator];
410 while ((curAttr = [attrEnum nextObject]) != nil) {
412 TODO: values are not always mapped to CDATA! Eg in the dawson draft:
413 | TYPE for TEL | tel.type | NMTOKENS | 'VOICE' |
414 | TYPE for EMAIL | email.type | NMTOKENS | 'INTERNET' |
415 | TYPE for PHOTO,| img.type | CDATA | REQUIRED |
417 | TYPE for SOUND | aud.type | CDATA | REQUIRED |
418 | VALUE | value | NOTATION | See elements |
421 [retAttrs addAttribute:curAttr uri:self->prefixURI rawName:curAttr
422 type:@"CDATA" value:[attributes objectForKey:curAttr]];
425 [attributes release];
430 - (VSSaxTag *)_beginTag:(NSString *)_tagName group:(NSString *)_group
431 withAttrs:(SaxAttributes *)_attrs
435 tag = [VSSaxTag beginTag:_tagName group:_group attributes:_attrs];
436 [self->elementList addObject:tag];
440 - (void)_endTag:(NSString *)_tagName {
443 tag = [[VSSaxTag alloc] initEndTag:_tagName];
444 [self->elementList addObject:tag];
445 [tag release]; tag = nil;
448 - (void)_addSubItems:(NSArray *)_items group:(NSString *)_group
449 withData:(NSString *)_content
451 NSEnumerator *itemEnum, *contentEnum;
454 itemEnum = [_items objectEnumerator];
455 contentEnum = [[_content componentsSeparatedByString:@";"] objectEnumerator];
457 while ((subTag = [itemEnum nextObject]) != nil) {
458 NSString *subContent;
460 subContent = [contentEnum nextObject];
462 [self _beginTag:subTag group:_group withAttrs:nil];
463 if ([subContent length] > 0) {
466 a = [(VSSaxTag*)[VSSaxTag alloc] initWithContentString:subContent];
468 [self->elementList addObject:a];
472 [self _endTag:subTag];
476 - (void)_reportContentAsTag:(NSString *)_tagName
477 group:(NSString *)_group
478 withAttrs:(SaxAttributes *)_attrs
479 andContent:(NSString *)_content
482 This is called for all non-BEGIN|END types.
486 _content = [stringFormatter stringByUnescapingRFC2445Text:_content];
488 /* check whether type should be reported as an attribute in XML */
490 if ([self->attributeElements containsObject:_tagName]) {
492 Add tag as an attribute to last component in the cardstack. This is
493 stuff like the "VERSION" type contained in a "BEGIN:VCARD" which will
494 be reported as <vcard version=""> (as an attribute of the container).
498 element = [self->cardStack lastObject];
499 [element->attrs addAttribute:_tagName uri:self->prefixURI
500 rawName:_tagName type:@"CDATA" value:_content];
504 /* report type as an XML tag */
506 [self _beginTag:_tagName group:_group withAttrs:_attrs];
508 if ([_content length] > 0) {
509 if ((subItems = [self->subItemMapping objectForKey:_tagName]) != nil) {
510 [self _addSubItems:subItems group:_group withData:_content];
515 a = [(VSSaxTag *)[VSSaxTag alloc] initWithContentString:_content];
517 [self->elementList addObject:a];
523 [self _endTag:_tagName];
526 /* report events for collected elements */
528 - (void)reportStartGroup:(NSString *)_group {
529 SaxAttributes *attrs;
531 attrs = [[SaxAttributes alloc] init];
532 [attrs addAttribute:@"name" uri:self->prefixURI rawName:@"name"
533 type:@"CDATA" value:_group];
535 [self->contentHandler startElement:@"group" namespace:self->prefixURI
536 rawName:@"group" attributes:attrs];
539 - (void)reportEndGroup {
540 [self->contentHandler endElement:@"group" namespace:self->prefixURI
544 - (void)reportQueuedTags {
546 Why does the parser need the list instead of reporting the events
549 Because some vCard tags like the 'version' are reported as attributes
550 on the container tag. So we have a sequence like:
554 which will get reported as:
555 <vcard version="3.0">
558 VSSaxTag *tagToReport;
562 enu = [self->elementList objectEnumerator];
563 while ((tagToReport = [enu nextObject]) != nil) {
564 if ([tagToReport isStartTag]) {
567 tg = [tagToReport group];
568 if (![lastGroup isEqualToString:tg] && lastGroup != tg) {
569 if (lastGroup != nil) [self reportEndGroup];
570 ASSIGNCOPY(lastGroup, tg);
571 if (lastGroup != nil) [self reportStartGroup:lastGroup];
575 if ([tagToReport isStartTag]) {
576 [self->contentHandler startElement:[tagToReport tagName]
577 namespace:self->prefixURI
578 rawName:[tagToReport tagName]
579 attributes:tagToReport->attrs];
581 else if ([tagToReport isEndTag]) {
582 [self->contentHandler endElement:[tagToReport tagName]
583 namespace:self->prefixURI
584 rawName:[tagToReport tagName]];
587 [self->contentHandler characters:tagToReport->data
588 length:tagToReport->datalen];
592 /* flush event group */
593 [self->elementList removeAllObjects];
595 /* close open groups */
596 if (lastGroup != nil) {
597 [self reportEndGroup];
598 [lastGroup release]; lastGroup = nil;
604 - (void)error:(NSString *)_text {
605 SaxParseException *e;
607 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
610 [self->errorHandler error:e];
612 - (void)warn:(NSString *)_warn {
613 SaxParseException *e;
615 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
618 [self->errorHandler warning:e];
621 /* parsing raw string */
623 - (void)_beginComponentWithValue:(NSString *)tagValue {
626 tag = [self _beginTag:[self _mapTagName:tagValue]
628 withAttrs:[[[SaxAttributes alloc] init] autorelease]];
629 [self->cardStack addObject:tag];
632 - (void)_endComponent:(NSString *)tagName value:(NSString *)tagValue {
635 mtName = [self _mapTagName:tagValue];
636 if ([self->cardStack count] > 0) {
637 NSString *expectedName;
639 expectedName = [(VSSaxTag *)[self->cardStack lastObject] tagName];
640 if (![expectedName isEqualToString:mtName]) {
643 // TODO: rather report an error?
644 // TODO: setup userinfo dict with details
645 s = [NSString stringWithFormat:
646 @"Found end tag '%@' which does not match expected "
648 @" Tag '%@' has not been closed properly. Given "
649 @"document contains errors!",
650 mtName, expectedName, expectedName];
653 /* probably futile attempt to parse anyways */
655 NSLog(@"%s trying to fix previous error by inserting bogus end "
657 __PRETTY_FUNCTION__);
659 [self _endTag:expectedName];
660 [self->cardStack removeLastObject];
664 // TOOD: generate error?
665 [self error:[@"found end tag without any open tags left: "
666 stringByAppendingString:mtName]];
668 [self _endTag:mtName];
669 [self->cardStack removeLastObject];
671 /* report parsed elements */
673 if ([self->cardStack count] == 0)
674 [self reportQueuedTags];
677 - (void)_parseLine:(NSString *)_line {
678 NSString *tagName, *tagValue;
679 NSMutableArray *tagAttributes;
680 NSRange r, todoRange;
683 length = [_line length];
684 todoRange = NSMakeRange(0, length);
685 r = [_line rangeOfCharacterFromSet:colonAndSemicolonCharSet
688 /* is line well-formed? */
690 [self error:[@"got an improper content line! ->\n"
691 stringByAppendingString:_line]];
695 /* tagname is everything up to a ':' or ';' (value or parameter) */
696 tagName = [[_line substringToIndex:r.location] uppercaseString];
697 tagAttributes = [[NSMutableArray alloc] initWithCapacity:16];
700 possible shortcut: if we spotted a ':', we don't have to do "expensive"
701 argument scanning/processing.
703 if ([_line characterAtIndex:r.location] != ':') {
705 BOOL isInDquote = NO;
708 start = NSMaxRange(r);
709 todoRange = NSMakeRange(start, length - start);
713 /* scan for parameters */
714 r = [_line rangeOfCharacterFromSet:colonSemicolonAndDquoteCharSet
718 /* is line well-formed? */
719 if (r.length == 0 || r.location == 0) {
720 [self error:[@"got an improper content line! ->\n"
721 stringByAppendingString:_line]];
722 [tagAttributes release]; tagAttributes = nil;
726 /* first check if delimiter candidate is escaped */
727 if ([_line characterAtIndex:(r.location - 1)] != '\\') {
731 delimiter = [_line characterAtIndex:r.location];
732 if (delimiter == '\"') {
733 /* not a real delimiter - toggle isInDquote for proper escaping */
734 isInDquote = !isInDquote;
738 /* is a delimiter, which one? */
740 if (delimiter == ':') {
743 copyRange = NSMakeRange(start, r.location - start);
744 [tagAttributes addObject:[_line substringWithRange:copyRange]];
746 /* adjust start, todoRange */
747 start = NSMaxRange(r);
748 todoRange = NSMakeRange(start, length - start);
754 /* adjust todoRange */
755 unsigned offset = NSMaxRange(r);
756 todoRange = NSMakeRange(offset, length - offset);
760 tagValue = [_line substringFromIndex:NSMaxRange(r)];
763 At this point we have:
764 name: 'BEGIN', 'TEL', 'EMAIL', 'ITEM1.ADR' etc
765 value: ';;;Magdeburg;;;Germany'
766 atributes: ("type=INTERNET", "type=HOME", "type=pref")
771 if ([tagName isEqualToString:@"BEGIN"]) {
772 if ([tagAttributes count] > 0)
773 [self warn:@"Losing unexpected parameters of BEGIN line."];
774 [self _beginComponentWithValue:tagValue];
776 else if ([tagName isEqualToString:@"END"]) {
777 if ([tagAttributes count] > 0)
778 [self warn:@"Losing unexpected parameters of END line."];
779 [self _endComponent:tagName value:tagValue];
782 // TODO: make this more generic, this one is used with Outlook vCards
783 if ([tagAttributes containsObject:@"ENCODING=QUOTED-PRINTABLE"]) {
784 // TODO: QP is charset specific! The one below decodes in Unicode!
785 tagValue = [tagValue stringByDecodingQuotedPrintable];
786 [tagAttributes removeObject:@"ENCODING=QUOTED-PRINTABLE"];
789 [self _reportContentAsTag:[self _mapTagName:tagName]
790 group:[self _groupFromTagName:tagName]
791 withAttrs:[self _mapAttrs:tagAttributes forTag:tagName]
792 andContent:tagValue];
795 [tagAttributes release];
799 /* top level parsing method */
801 - (void)reportDocStart {
802 [self->contentHandler startDocument];
803 [self->contentHandler startPrefixMapping:@"" uri:self->prefixURI];
805 - (void)reportDocEnd {
806 [self->contentHandler endPrefixMapping:@""];
807 [self->contentHandler endDocument];
810 - (void)_parseString:(NSString *)_rawString {
812 This method split the string into content lines for actual vCard
816 contentline = name *(";" param ) ":" value CRLF
817 ; When parsing a content line, folded lines MUST first
820 NSMutableString *line;
821 unsigned pos, length;
824 [self reportDocStart];
828 length = [_rawString length];
829 r = NSMakeRange(0, 0);
830 line = [[NSMutableString alloc] initWithCapacity:75 + 2];
832 for (pos = 0; pos < length; pos++) {
835 c = [_rawString characterAtIndex:pos];
838 if (((length - 1) - pos) >= 1) {
839 if ([_rawString characterAtIndex:pos + 1] == '\n') {
840 BOOL isAtEndOfLine = YES;
842 /* test for folding first */
843 if (((length - 1) - pos) >= 2) {
846 ws = [_rawString characterAtIndex:pos + 2];
847 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO :YES;
848 if (!isAtEndOfLine) {
849 /* assemble part of line up to pos */
851 [line appendString:[_rawString substringWithRange:r]];
855 r = NSMakeRange(pos + 1, 0); /* begin new range */
859 /* assemble part of line up to pos */
861 [line appendString:[_rawString substringWithRange:r]];
863 [self _parseLine:line];
865 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
867 r = NSMakeRange(pos + 1, 0); /* begin new range */
872 /* garbled last line! */
873 [self warn:@"last line is truncated, trying to parse anyways!"];
876 else if (c == '\n') { /* broken, non-standard */
877 BOOL isAtEndOfLine = YES;
879 /* test for folding first */
880 if (((length - 1) - pos) >= 1) {
883 ws = [_rawString characterAtIndex:(pos + 1)];
885 isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO : YES;
886 if (!isAtEndOfLine) {
887 /* assemble part of line up to pos */
889 [line appendString:[_rawString substringWithRange:r]];
893 r = NSMakeRange(pos + 1, 0); /* begin new range */
897 /* assemble part of line up to pos */
899 [line appendString:[_rawString substringWithRange:r]];
901 [self _parseLine:line];
903 [line deleteCharactersInRange:NSMakeRange(0, [line length])];
904 r = NSMakeRange(pos + 1, 0); /* begin new range */
912 [self warn:@"Last line of parse string is not properly terminated!"];
913 [line appendString:[_rawString substringWithRange:r]];
914 [self _parseLine:line];
917 if ([self->cardStack count] != 0) {
918 [self warn:@"found elements on cardStack. This indicates an improper "
919 @"nesting structure! Not all required events will have been "
920 @"generated, leading to unpredictable results!"];
921 [self->cardStack removeAllObjects]; // clean up
924 [line release]; line = nil;
929 /* main entry functions */
931 - (id)sourceForData:(NSData *)_data systemId:(NSString *)_sysId {
932 SaxParseException *e = nil;
933 NSStringEncoding encoding;
935 const unsigned char *bytes;
939 NSLog(@"%s: trying to decode data (0x%08X,len=%d) ...",
940 __PRETTY_FUNCTION__, _data, [_data length]);
943 if ((len = [_data length]) == 0) {
944 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
945 reason:@"Got no parsing data!"
947 [self->errorHandler fatalError:e];
951 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
952 reason:@"Input data to short for vCard!"
954 [self->errorHandler fatalError:e];
958 bytes = [_data bytes];
959 if ((bytes[0] == 0xFF && bytes[1] == 0xFE) ||
960 (bytes[0] == 0xFE && bytes[1] == 0xFF)) {
961 encoding = NSUnicodeStringEncoding;
964 encoding = NSUTF8StringEncoding;
966 // FIXME: Data is not always utf-8.....
967 source = [[[NSString alloc] initWithData:_data encoding:encoding]
970 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
971 reason:@"Could not convert input to string!"
973 [self->errorHandler fatalError:e];
978 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
980 NSLog(@"%s: parse: %@ (sysid=%@)", __PRETTY_FUNCTION__, _source, _sysId);
982 if ([_source isKindOfClass:[NSURL class]]) {
983 if (_sysId == nil) _sysId = [_source absoluteString];
986 NSLog(@"%s: trying to load URL: %@ (sysid=%@)",__PRETTY_FUNCTION__,
990 // TODO: remember encoding of source
991 _source = [_source resourceDataUsingCache:NO];
994 if ([_source isKindOfClass:[NSData class]]) {
995 if (_sysId == nil) _sysId = @"<data>";
996 if ((_source = [self sourceForData:_source systemId:_sysId]) == nil)
1000 if (![_source isKindOfClass:[NSString class]]) {
1001 SaxParseException *e;
1005 NSLog(@"%s: unrecognizable source: %@", __PRETTY_FUNCTION__,_source);
1007 s = [@"cannot handle data-source: " stringByAppendingString:
1008 [_source description]];
1009 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1013 [self->errorHandler fatalError:e];
1017 /* ensure consistent state */
1019 [self->cardStack removeAllObjects];
1020 [self->elementList removeAllObjects];
1025 NSLog(@"%s: trying to parse string (0x%08X,len=%d) ...",
1026 __PRETTY_FUNCTION__, _source, [_source length]);
1028 if (_sysId == nil) _sysId = @"<string>";
1029 [self _parseString:_source];
1033 [self->cardStack removeAllObjects];
1034 [self->elementList removeAllObjects];
1037 - (void)parseFromSource:(id)_source {
1038 [self parseFromSource:_source systemId:nil];
1041 - (void)parseFromSystemId:(NSString *)_sysId {
1044 if ([_sysId rangeOfString:@"://"].length == 0) {
1045 /* seems to be a path, path to be a proper URL */
1046 url = [NSURL fileURLWithPath:_sysId];
1049 /* Note: Cocoa NSURL doesn't complain on "/abc/def" like input! */
1050 url = [NSURL URLWithString:_sysId];
1054 SaxParseException *e;
1056 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1057 reason:@"cannot handle system-id"
1059 [self->errorHandler fatalError:e];
1063 [self parseFromSource:url systemId:_sysId];
1068 - (BOOL)isDebuggingEnabled {
1072 @end /* VersitSaxDriver */