/*
Copyright (C) 2003-2004 Max Berger
- Copyright (C) 2004 OpenGroupware.org
+ Copyright (C) 2004-2005 OpenGroupware.org
This file is part of versitSaxDriver, written for the OpenGroupware.org
project (OGo).
#include "VSSaxDriver.h"
#include "VSStringFormatter.h"
+#include <SaxObjC/SaxException.h>
#include "common.h"
@implementation VSSaxDriver
static BOOL didInit = NO;
NSUserDefaults *ud;
- if(didInit)
+ if (didInit)
return;
didInit = YES;
- (id)init {
if ((self = [super init])) {
self->prefixURI = @"";
- self->cardStack = [[NSMutableArray alloc] init];
- self->elementList = [[NSMutableArray alloc] init];
- self->attributeMapping = [[NSMutableDictionary alloc] init];
- self->subItemMapping = [[NSMutableDictionary alloc] init];
+ self->cardStack = [[NSMutableArray alloc] initWithCapacity:4];
+ self->elementList = [[NSMutableArray alloc] initWithCapacity:8];
+ self->attributeMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
+ self->subItemMapping = [[NSMutableDictionary alloc] initWithCapacity:8];
}
return self;
}
- (void)dealloc {
[self->contentHandler release];
+ [self->errorHandler release];
[self->prefixURI release];
[self->cardStack release];
[self->elementList release];
/* handlers */
- (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
- ASSIGN(self->contentHandler,_handler);
+ ASSIGN(self->contentHandler, _handler);
}
- (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
}
- (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
- // FIXME
+ ASSIGN(self->errorHandler, _handler);
}
- (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
// FIXME
- (id<NSObject,SaxDTDHandler>)dtdHandler {
// FIXME
- return NULL;
+ return nil;
}
- (id<NSObject,SaxErrorHandler>)errorHandler {
- // FIXME
- return NULL;
+ return self->errorHandler;
}
- (id<NSObject,SaxEntityResolver>)entityResolver {
// FIXME
- return NULL;
+ return nil;
}
- (void)setPrefixURI:(NSString *)_uri {
- (void)setAttributeMapping:(NSDictionary *)_mapping
forElement:(NSString *)_element
{
- if (!_element)
+ if (_element == nil)
_element = @"";
[attributeMapping setObject:_mapping forKey:_element];
}
-- (void)setSubItemMapping:(NSArray *)_mapping
- forElement:(NSString *)_element
-{
+- (void)setSubItemMapping:(NSArray *)_mapping forElement:(NSString *)_element {
[subItemMapping setObject:_mapping forKey:_element];
}
attrName = [[_attr substringToIndex:r.location] uppercaseString];
left = NSMaxRange(r);
right = [_attr length] - 1;
- if(left < right) {
- if(([_attr characterAtIndex:left] == '"') &&
+ if (left < right) {
+ if (([_attr characterAtIndex:left] == '"') &&
([_attr characterAtIndex:right] == '"'))
{
left += 1;
attrValue = [_attr substringFromIndex:left];
}
}
- else if(left == right) {
+ else if (left == right) {
attrValue = [_attr substringFromIndex:left];
}
else {
andContent:(NSString *)_content
{
NSArray *subItems;
-
+
_content = [stringFormatter stringByUnescapingRFC2445Text:_content];
if ([self->attributeElements containsObject:_tagName]) {
[self _addAttribute:_tagName value:_content];
+ return;
}
- else {
- [self _beginTag:_tagName withAttrs:_attrs];
- if ([_content length] > 0) {
+
+ [self _beginTag:_tagName withAttrs:_attrs];
+ if ([_content length] > 0) {
if ((subItems = [self->subItemMapping objectForKey:_tagName])) {
[self _addSubItems:subItems withData:_content];
}
[self->elementList addObject:
[NSArray arrayWithObjects:@"DATA", _content, nil]];
}
- }
- [self _endTag:_tagName];
}
+ [self _endTag:_tagName];
}
- (void)_eventsForElements {
NSEnumerator *enu;
NSArray *obj;
- NSString *type;
- NSString *name;
- unichar *chardata;
- id<NSObject,SaxAttributes> attrs;
enu = [elementList objectEnumerator];
- while ((obj = [enu nextObject])) {
+ while ((obj = [enu nextObject]) != nil) {
+ id<NSObject,SaxAttributes> attrs;
+ NSString *type;
+ NSString *name;
+ unichar *chardata;
+
type = [obj objectAtIndex:0];
name = [obj objectAtIndex:1];
options:0
range:todoRange];
/* is line well-formed? */
- if(r.length == 0) {
- if(debugOn) {
+ if (r.length == 0) {
+ if (debugOn) {
NSLog(@"%s got an improper content line! ->\n%@",
__PRETTY_FUNCTION__,
_line);
/* possible shortcut: if we spotted a ':', we don't have to do "expensive"
argument scanning/processing.
*/
- if([_line characterAtIndex:r.location] != ':') {
+ if ([_line characterAtIndex:r.location] != ':') {
BOOL isAtEnd = NO, isInDquote = NO;
unsigned start = NSMaxRange(r);
options:0
range:todoRange];
/* is line well-formed? */
- if(r.length == 0 || r.location == 0) {
- if(debugOn) {
+ if (r.length == 0 || r.location == 0) {
+ if (debugOn) {
NSLog(@"%s got an improper content line! ->\n%@",
__PRETTY_FUNCTION__,
_line);
return;
}
/* first check if delimiter candidate is escaped */
- if([_line characterAtIndex:(r.location - 1)] != '\\') {
+ if ([_line characterAtIndex:(r.location - 1)] != '\\') {
unichar delimiter;
NSRange copyRange;
delimiter = [_line characterAtIndex:r.location];
- if(delimiter == '\"') {
+ if (delimiter == '\"') {
/* not a real delimiter - toggle isInDquote for proper escaping */
isInDquote = !isInDquote;
}
else {
- if(!isInDquote) {
+ if (!isInDquote) {
/* is a delimiter, which one? */
skip = NO;
- if(delimiter == ':') {
+ if (delimiter == ':') {
isAtEnd = YES;
}
copyRange = NSMakeRange(start, r.location - start);
[tagAttributes addObject:[_line substringWithRange:copyRange]];
- if(!isAtEnd) {
+ if (!isAtEnd) {
/* adjust start, todoRange */
start = NSMaxRange(r);
todoRange = NSMakeRange(start, length - start);
}
}
}
- if(skip) {
+ if (skip) {
/* adjust todoRange */
unsigned offset = NSMaxRange(r);
todoRange = NSMakeRange(offset, length - offset);
NSString *mtName;
mtName = [self _mapTagName:tagValue];
- if([self->cardStack count] > 0) {
+ if ([self->cardStack count] > 0) {
NSString *expectedName;
expectedName = [[self->cardStack lastObject] objectAtIndex:1];
- if(![expectedName isEqualToString:mtName]) {
- if(debugOn) {
+ if (![expectedName isEqualToString:mtName]) {
+ if (debugOn) {
NSLog(@"%s found end tag '%@' which doesn't match expected name "
@"'%@'! Tag '%@' hasn't been closed properly. Given iCal "
@"document contains errors!",
expectedName);
}
/* probably futile attempt to parse anyways */
- if(debugOn) {
+ if (debugOn) {
NSLog(@"%s trying to fix previous error by inserting bogus end "
@"tag.",
__PRETTY_FUNCTION__);
}
}
else {
- if(debugOn) {
+ if (debugOn) {
NSLog(@"%s found end tag '%@' without any open tags left?!",
__PRETTY_FUNCTION__,
mtName);
for(pos = 0; pos < length; pos++) {
unichar c = [_rawString characterAtIndex:pos];
- if(c == '\r') {
- if(((length - 1) - pos) >= 1) {
- if([_rawString characterAtIndex:pos + 1] == '\n') {
+ if (c == '\r') {
+ if (((length - 1) - pos) >= 1) {
+ if ([_rawString characterAtIndex:pos + 1] == '\n') {
BOOL isAtEndOfLine = YES;
/* test for folding first */
- if(((length - 1) - pos) >= 2) {
+ if (((length - 1) - pos) >= 2) {
unichar ws = [_rawString characterAtIndex:pos + 2];
isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO
: YES;
- if(!isAtEndOfLine) {
+ if (!isAtEndOfLine) {
/* assemble part of line up to pos */
- if(r.length > 0) {
+ if (r.length > 0) {
[line appendString:[_rawString substringWithRange:r]];
}
/* unfold */
r = NSMakeRange(pos + 1, 0); /* begin new range */
}
}
- if(isAtEndOfLine) {
+ if (isAtEndOfLine) {
/* assemble part of line up to pos */
- if(r.length > 0) {
+ if (r.length > 0) {
[line appendString:[_rawString substringWithRange:r]];
}
[self _parseLine:line];
}
else {
/* garbled last line! */
- if(debugOn) {
+ if (debugOn) {
NSLog(@"%s Last line is truncated, trying to parse anyways!",
__PRETTY_FUNCTION__);
}
}
}
- else if(c == '\n') { /* broken, non-standard */
+ else if (c == '\n') { /* broken, non-standard */
BOOL isAtEndOfLine = YES;
/* test for folding first */
- if(((length - 1) - pos) >= 1) {
+ if (((length - 1) - pos) >= 1) {
unichar ws = [_rawString characterAtIndex:pos + 1];
isAtEndOfLine = [whitespaceCharSet characterIsMember:ws] ? NO
: YES;
- if(!isAtEndOfLine) {
+ if (!isAtEndOfLine) {
/* assemble part of line up to pos */
- if(r.length > 0) {
+ if (r.length > 0) {
[line appendString:[_rawString substringWithRange:r]];
}
/* unfold */
r = NSMakeRange(pos + 1, 0); /* begin new range */
}
}
- if(isAtEndOfLine) {
+ if (isAtEndOfLine) {
/* assemble part of line up to pos */
- if(r.length > 0) {
+ if (r.length > 0) {
[line appendString:[_rawString substringWithRange:r]];
}
[self _parseLine:line];
r.length += 1;
}
}
- if(r.length > 0) {
- if(debugOn) {
+ if (r.length > 0) {
+ if (debugOn) {
NSLog(@"%s Last line of iCal string is not properly terminated!",
__PRETTY_FUNCTION__);
}
[self _parseLine:line];
}
- if([self->cardStack count] != 0) {
- if(debugOn) {
+ if ([self->cardStack count] != 0) {
+ if (debugOn) {
NSLog(@"%s found elements on cardStack. This indicates an improper "
@"iCal structure! Not all required events will have been "
@"generated, leading to unpredictable results!",
[self->contentHandler endDocument];
}
-- (void)parseFromSource:(id)_source {
+/* main entry functions */
+
+- (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
if (debugOn)
- NSLog(@"%s: parse: %@", __PRETTY_FUNCTION__, _source);
+ NSLog(@"%s: parse: %@ (sysid=%@)", __PRETTY_FUNCTION__, _source, _sysId);
if ([_source isKindOfClass:[NSURL class]]) {
- if (debugOn)
- NSLog(@"%s: trying to load URL...",__PRETTY_FUNCTION__);
+ if (_sysId == nil) _sysId = [_source absoluteString];
+
+ if (debugOn) {
+ NSLog(@"%s: trying to load URL: %@ (sysid=%@)",__PRETTY_FUNCTION__,
+ _source, _sysId);
+ }
+
+ // TODO: remember encoding of source
_source = [_source resourceDataUsingCache:NO];
}
if ([_source isKindOfClass:[NSData class]]) {
+ if (debugOn) {
+ NSLog(@"%s: trying to decode data (0x%08X,len=%d) ...",
+ __PRETTY_FUNCTION__, _source, [_source length]);
+ }
+ if (_sysId == nil) _sysId = @"<data>";
+
// FIXME: Data is not always utf-8.....
- if (debugOn)
- NSLog(@"%s: trying to decode data...",__PRETTY_FUNCTION__);
_source = [[[NSString alloc]
initWithData:_source encoding:NSUTF8StringEncoding]
autorelease];
}
-
- if ([_source isKindOfClass:[NSString class]]) {
- if (debugOn)
- NSLog(@"%s: trying to parse string...",__PRETTY_FUNCTION__);
- [self _parseString:_source];
- }
- else {
+
+ if (![_source isKindOfClass:[NSString class]]) {
+ SaxParseException *e;
+
if (debugOn)
NSLog(@"%s: unrecognizable source: %@", __PRETTY_FUNCTION__,_source);
- // FIXME: Return Error
+
+ e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
+ reason:@"cannot handle data-source"
+ userInfo:nil];
+
+ [self->errorHandler fatalError:e];
+ return;
}
+
+ /* start parsing */
+
+ if (debugOn) {
+ NSLog(@"%s: trying to parse string (0x%08X,len=%d) ...",
+ __PRETTY_FUNCTION__, _source, [_source length]);
+ }
+ if (_sysId == nil) _sysId = @"<string>";
+ [self _parseString:_source];
}
-- (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
- [self parseFromSource:_source];
+- (void)parseFromSource:(id)_source {
+ [self parseFromSource:_source systemId:nil];
}
- (void)parseFromSystemId:(NSString *)_sysId {
NSURL *url;
- if ((url = [NSURL URLWithString:_sysId]))
- [self parseFromSource:url systemId:_sysId];
+ if ([_sysId rangeOfString:@"://"].length == 0) {
+ /* seems to be a path, path to be a proper URL */
+ url = [NSURL fileURLWithPath:_sysId];
+ }
+ else {
+ /* Note: Cocoa NSURL doesn't complain on "/abc/def" like input! */
+ url = [NSURL URLWithString:_sysId];
+ }
+
+ if (url == nil) {
+ SaxParseException *e;
+
+ e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
+ reason:@"cannot handle system-id"
+ userInfo:nil];
+ [self->errorHandler fatalError:e];
+ return;
+ }
+
+ [self parseFromSource:url systemId:_sysId];
}
/* debugging */