]> err.no Git - sope/blobdiff - sope-ical/versitSaxDriver/VSSaxDriver.m
improved input processing of sax driver
[sope] / sope-ical / versitSaxDriver / VSSaxDriver.m
index ee219cc5bf35a91b7c11d0631bae30fdea74d31c..e740f5bdfd4248eb7a0983579d01d47c52bd1014 100644 (file)
@@ -1,6 +1,6 @@
 /*
  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).
@@ -23,6 +23,7 @@
 
 #include "VSSaxDriver.h"
 #include "VSStringFormatter.h"
+#include <SaxObjC/SaxException.h>
 #include "common.h"
 
 @implementation VSSaxDriver
@@ -42,7 +43,7 @@ static VSStringFormatter *stringFormatter = nil;
   static BOOL didInit = NO;
   NSUserDefaults *ud;
 
-  if(didInit)
+  if (didInit)
     return;
   didInit = YES;
 
@@ -69,16 +70,17 @@ static VSStringFormatter *stringFormatter = nil;
 - (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];
@@ -106,7 +108,7 @@ static VSStringFormatter *stringFormatter = nil;
 /* handlers */
 
 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
-  ASSIGN(self->contentHandler,_handler);
+  ASSIGN(self->contentHandler, _handler);
 }
 
 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
@@ -114,7 +116,7 @@ static VSStringFormatter *stringFormatter = nil;
 }
 
 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
-  // FIXME
+  ASSIGN(self->errorHandler, _handler);
 }
 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
   // FIXME
@@ -126,16 +128,15 @@ static VSStringFormatter *stringFormatter = nil;
 
 - (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 {
@@ -166,14 +167,12 @@ static VSStringFormatter *stringFormatter = nil;
 - (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];  
 }
 
@@ -252,8 +251,8 @@ static VSStringFormatter *stringFormatter = nil;
     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;
@@ -264,7 +263,7 @@ static VSStringFormatter *stringFormatter = nil;
         attrValue = [_attr substringFromIndex:left];
       }
     }
-    else if(left == right) {
+    else if (left == right) {
       attrValue = [_attr substringFromIndex:left];
     }
     else {
@@ -368,14 +367,15 @@ static VSStringFormatter *stringFormatter = nil;
   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];
       }
@@ -383,21 +383,21 @@ static VSStringFormatter *stringFormatter = nil;
         [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];
     
@@ -441,8 +441,8 @@ static VSStringFormatter *stringFormatter = nil;
              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);
@@ -456,7 +456,7 @@ static VSStringFormatter *stringFormatter = nil;
   /* 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);
 
@@ -469,8 +469,8 @@ static VSStringFormatter *stringFormatter = nil;
                  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);
@@ -479,25 +479,25 @@ static VSStringFormatter *stringFormatter = nil;
         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);
@@ -505,7 +505,7 @@ static VSStringFormatter *stringFormatter = nil;
           }
         }
       }
-      if(skip) {
+      if (skip) {
         /* adjust todoRange */
         unsigned offset = NSMaxRange(r);
         todoRange = NSMakeRange(offset, length - offset);
@@ -525,12 +525,12 @@ static VSStringFormatter *stringFormatter = nil;
     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!",
@@ -540,7 +540,7 @@ static VSStringFormatter *stringFormatter = nil;
                 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__);
@@ -550,7 +550,7 @@ static VSStringFormatter *stringFormatter = nil;
       }
     }
     else {
-      if(debugOn) {
+      if (debugOn) {
         NSLog(@"%s found end tag '%@' without any open tags left?!",
               __PRETTY_FUNCTION__,
               mtName);
@@ -590,18 +590,18 @@ static VSStringFormatter *stringFormatter = nil;
   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 */
@@ -609,9 +609,9 @@ static VSStringFormatter *stringFormatter = nil;
               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];
@@ -624,22 +624,22 @@ static VSStringFormatter *stringFormatter = nil;
       }
       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 */
@@ -647,9 +647,9 @@ static VSStringFormatter *stringFormatter = nil;
           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];
@@ -662,8 +662,8 @@ static VSStringFormatter *stringFormatter = nil;
       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__);
     }
@@ -671,8 +671,8 @@ static VSStringFormatter *stringFormatter = nil;
     [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!",
@@ -685,46 +685,88 @@ static VSStringFormatter *stringFormatter = nil;
   [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 */