]> err.no Git - sope/commitdiff
added support vcard type groupings
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 6 May 2005 15:46:44 +0000 (15:46 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 6 May 2005 15:46:44 +0000 (15:46 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@800 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-ical/samples/GNUmakefile
sope-ical/samples/ical3.m
sope-ical/samples/vcf2xml.m [new file with mode: 0644]
sope-ical/versitSaxDriver/ChangeLog
sope-ical/versitSaxDriver/README
sope-ical/versitSaxDriver/VSSaxDriver.h
sope-ical/versitSaxDriver/VSSaxDriver.m
sope-ical/versitSaxDriver/VSiCalSaxDriver.m
sope-ical/versitSaxDriver/VSvCardSaxDriver.h
sope-ical/versitSaxDriver/VSvCardSaxDriver.m
sope-ical/versitSaxDriver/Version

index b7aed7c3630117a53bbe6165007d941cc98fbbb2..245e4ccc72eb7c8a1a53a975008e65b7cbb712cc 100644 (file)
@@ -3,13 +3,15 @@
 -include ../../config.make
 include $(GNUSTEP_MAKEFILES)/common.make
 
-TOOL_NAME = ical2 ical3
+TOOL_NAME = ical2 ical3 vcf2xml
 
-ical2_OBJC_FILES = ical2.m
-ical3_OBJC_FILES = ical3.m
+ical2_OBJC_FILES   = ical2.m
+ical3_OBJC_FILES   = ical3.m
+vcf2xml_OBJC_FILES = vcf2xml.m
 
-ical2_TOOL_LIBS += -lNGiCal -lSaxObjC
-ical3_TOOL_LIBS += -lNGiCal -lSaxObjC
+ical2_TOOL_LIBS   += -lNGiCal -lSaxObjC
+ical3_TOOL_LIBS   += -lNGiCal -lSaxObjC
+vcf2xml_TOOL_LIBS += -lSaxObjC
 
 -include GNUmakefile.preamble
 include $(GNUSTEP_MAKEFILES)/tool.make
index 6c480b44dd9f3ea1fd60a7a94bcfacf34cb85b1f..bcbbb52661c1fa85454f67c616756cdd2a2831df 100644 (file)
@@ -145,12 +145,11 @@ int main(int argc, char **argv, char **env)  {
   iCal3Tool *tool;
   int rc;
 
+  pool = [[NSAutoreleasePool alloc] init];
 #if LIB_FOUNDATION_LIBRARY  
   [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
 #endif
   
-  pool = [[NSAutoreleasePool alloc] init];
-  
   if ((tool = [[iCal3Tool alloc] init])) {
     NS_DURING
       rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
diff --git a/sope-ical/samples/vcf2xml.m b/sope-ical/samples/vcf2xml.m
new file mode 100644 (file)
index 0000000..3f56c2c
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+// Note: this does not yet produce valid XML output
+
+#import <Foundation/NSObject.h>
+#include <SaxObjC/SaxObjC.h>
+
+@interface vcf2xml : NSObject
+{
+  id<NSObject,SaxXMLReader> parser;
+  id sax;
+}
+
+- (int)runWithArguments:(NSArray *)_args;
+
+@end
+
+#include "common.h"
+
+@interface MySAXHandler : SaxDefaultHandler
+{
+  id  locator;
+  int indent;
+
+  NSString *lastNS;
+}
+
+- (void)indent;
+
+@end
+
+@implementation vcf2xml
+
+- (id)init {
+  if ((self = [super init]) != nil) {
+    self->parser = [[[SaxXMLReaderFactory standardXMLReaderFactory] 
+                     createXMLReaderForMimeType:@"text/x-vcard"] retain];
+    if (parser == nil) {
+      fprintf(stderr, "Error: could not load a vCard SAX driver bundle!\n");
+      exit(2);
+    }
+    //NSLog(@"Using parser: %@", self->parser);
+    
+    self->sax = [[MySAXHandler alloc] init];
+    [parser setContentHandler:self->sax];
+    [parser setErrorHandler:self->sax];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self->sax    release];
+  [self->parser release];
+  [super dealloc];
+}
+
+/* process files */
+
+- (void)processFile:(NSString *)_path {
+  [self->parser parseFromSystemId:_path];
+}
+
+/* error handling */
+
+- (NSException *)handleException:(NSException *)_exc onPath:(NSString *)_p {
+  fprintf(stderr, "Error: catched exception on path '%s': %s\n",
+         [_p cString], [[_exc description] cString]);
+  return nil;
+}
+
+/* main entry */
+
+- (int)runWithArguments:(NSArray *)_args {
+  NSEnumerator *args;
+  NSString     *arg;
+  
+  /* begin processing */
+  
+  args = [_args objectEnumerator];
+  [args nextObject]; // skip tool name ...
+  
+  while ((arg = [args nextObject]) != nil) {
+    NSAutoreleasePool *pool2;
+    
+    if ([arg hasPrefix:@"-"]) { /* consume defaults */
+      [args nextObject];
+      continue;
+    }
+    
+    pool2 = [[NSAutoreleasePool alloc] init];
+
+
+    if (![arg isAbsolutePath]) {
+      arg = [[[NSFileManager defaultManager] currentDirectoryPath]
+             stringByAppendingPathComponent:arg];
+    }
+    
+    NS_DURING
+      [self->parser parseFromSystemId:arg];
+    NS_HANDLER
+      [[self handleException:localException onPath:arg] raise];
+    NS_ENDHANDLER;
+    
+    [pool2 release];
+  }
+  return 0;
+}
+
+@end /* vcf2xml */
+
+
+@implementation MySAXHandler
+
+- (void)dealloc {
+  [self->lastNS  release];
+  [self->locator release];
+  [super dealloc];
+}
+
+/* output */
+
+- (void)indent {
+  int i;
+  
+  for (i = 0; i < (self->indent * 4); i++)
+    fputc(' ', stdout);
+}
+
+/* documents */
+
+- (void)setDocumentLocator:(id<NSObject,SaxLocator>)_loc {
+  [self->locator autorelease];
+  self->locator = [_loc retain];
+}
+
+- (void)startDocument {
+  //puts("start document ..");
+  //self->indent++;
+}
+- (void)endDocument {
+  //self->indent--;
+  //puts("end document.");
+}
+
+- (void)startPrefixMapping:(NSString *)_prefix uri:(NSString *)_uri {
+  [self indent];
+  //printf("ns-map: %s=%s\n", [_prefix cString], [_uri cString]);
+}
+- (void)endPrefixMapping:(NSString *)_prefix {
+  [self indent];
+  //printf("ns-unmap: %s\n", [_prefix cString]);
+}
+
+- (void)startElement:(NSString *)_localName
+  namespace:(NSString *)_ns
+  rawName:(NSString *)_rawName
+  attributes:(id<SaxAttributes>)_attrs
+{
+  int i, c;
+  [self indent];
+  printf("<%s", [_localName cString]);
+  
+  if ([_ns length] > 0) {
+    if ([_ns isEqualToString:self->lastNS])
+      ;
+    else {
+      printf(" xmlns='%s'", [_ns cString]);
+      ASSIGNCOPY(self->lastNS, _ns);
+    }
+  }
+  
+  for (i = 0, c = [_attrs count]; i < c; i++) {
+    NSString *type;
+    NSString *ans;
+
+    ans = [_attrs uriAtIndex:i];
+    
+    printf(" %s=\"%s\"",
+           [[_attrs nameAtIndex:i] cString],
+           [[_attrs valueAtIndex:i] cString]);
+    
+    if (![_ns isEqualToString:ans])
+      printf("(ns=%s)", [ans cString]);
+    
+    type = [_attrs typeAtIndex:i];
+    if (![type isEqualToString:@"CDATA"] && (type != nil))
+      printf("[%s]", [type cString]);
+  }
+  puts(">");
+  self->indent++;
+}
+- (void)endElement:(NSString *)_localName
+  namespace:(NSString *)_ns
+  rawName:(NSString *)_rawName
+{
+  self->indent--;
+  [self indent];
+  printf("</%s>\n", [_localName cString]);
+}
+
+- (void)characters:(unichar *)_chars length:(int)_len {
+  NSString *str;
+  id tmp;
+  unsigned i, len;
+
+  if (_len == 0) {
+    [self indent];
+    printf("\"\"\n");
+    return;
+  }
+  
+  for (i = 0; i < (unsigned)_len; i++) {
+    if (_chars[i] > 255) {
+      NSLog(@"detected large char: o%04o d%03i h%04X",
+            _chars[i], _chars[i], _chars[i]);
+    }
+  }
+  
+  str = [NSString stringWithCharacters:_chars length:_len];
+  len = [str length];
+  
+  tmp = [str componentsSeparatedByString:@"\n"];
+  str = [tmp componentsJoinedByString:@"\\n"];
+  tmp = [str componentsSeparatedByString:@"\r"];
+  str = [tmp componentsJoinedByString:@"\\r"];
+  
+  [self indent];
+  printf("\"%s\"\n", [str cString]);
+}
+- (void)ignorableWhitespace:(unichar *)_chars length:(int)_len {
+  NSString *data;
+  id tmp;
+
+  data = [NSString stringWithCharacters:_chars length:_len];
+  tmp  = [data componentsSeparatedByString:@"\n"];
+  data = [tmp componentsJoinedByString:@"\\n"];
+  tmp  = [data componentsSeparatedByString:@"\r"];
+  data = [tmp componentsJoinedByString:@"\\r"];
+  
+  [self indent];
+  printf("whitespace: \"%s\"\n", [data cString]);
+}
+
+- (void)processingInstruction:(NSString *)_pi data:(NSString *)_data {
+  [self indent];
+  printf("PI: '%s' '%s'\n", [_pi cString], [_data cString]);
+}
+
+#if 0
+- (xmlEntityPtr)getEntity:(NSString *)_name {
+  NSLog(@"get entity %@", _name);
+  return NULL;
+}
+- (xmlEntityPtr)getParameterEntity:(NSString *)_name {
+  NSLog(@"get para entity %@", _name);
+  return NULL;
+}
+#endif
+
+/* entities */
+
+- (id)resolveEntityWithPublicId:(NSString *)_pubId
+  systemId:(NSString *)_sysId
+{
+  [self indent];
+  printf("shall resolve entity with '%s' '%s'",
+         [_pubId cString], [_sysId cString]);
+  return nil;
+}
+
+/* errors */
+
+- (void)warning:(SaxParseException *)_exception {
+  NSLog(@"warning(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+}
+
+- (void)error:(SaxParseException *)_exception {
+  NSLog(@"error(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+}
+
+- (void)fatalError:(SaxParseException *)_exception {
+  NSLog(@"fatal error(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+  [_exception raise];
+}
+
+@end /* MySAXHandler */
+
+
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  vcf2xml *tool;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  if ((tool = [[vcf2xml alloc] init])) {
+    NS_DURING
+      rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [tool release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+  return rc;
+}
index f1c735a0f8ca64e13a1e284b9cf89f2a44a9eab7..a40da82bbd8574435208c1316f63dcb565f8946a 100644 (file)
@@ -1,3 +1,8 @@
+2005-05-06  Helge Hess  <helge.hess@opengroupware.org>
+
+       * VSSaxDriver.m: more reorganisations, added support for groupings
+         (v4.5.15)
+
 2005-05-05  Helge Hess  <helge.hess@opengroupware.org>
 
        * VSSaxDriver.m: code cleanups / reorgs, properly embed reported
         
        * added unfold method (doesnt do anything yet)
 
-
 2003-11-23  Max Berger  <max@berger.name>
 
        * Initial Version (v0.1.1)
-
index 4fd5253f1b2e7a58c48d11de0414362a4eafb04c..85d148e669b21cab579632002f2afc967642bd63 100644 (file)
@@ -1,5 +1,3 @@
-# $Id$
-
 Overview
 ========
 
index 48ad72ec57f6a76a22d5b7ffd7fd7bcbf97f8a62..9fe490b69f7779054bbe6a84315be0e4ea105c06 100644 (file)
@@ -35,7 +35,7 @@
   id<NSObject,SaxErrorHandler>   errorHandler;
   NSString                       *prefixURI;
   NSMutableArray                 *cardStack;
-  NSMutableArray                 *elementList;
+  NSMutableArray                 *elementList; /* a list of tags to be rep. */
   
   NSSet                          *attributeElements;
   NSDictionary                   *elementMapping; 
 - (void)setSubItemMapping:(NSArray *)_mapping 
   forElement:(NSString *)_element;
 
+/* events */
+
+- (void)reportDocStart;
+- (void)reportDocEnd;
+
 @end
 
 #endif /* __versitSaxDriver_VersitSaxDriver_H__ */
index 0113f2bccca698db6e29594d64c063e3b1316687..4f0881be084aa0abb018fa10bce188d7189bd836 100644 (file)
 
 @interface VSSaxTag : NSObject
 {
-@public
-  NSString      *type;
+@private
+  char          type;
   NSString      *tagName;
+  NSString      *group;
+@public
   SaxAttributes *attrs;
-  NSString      *data;
+  unichar       *data;
+  unsigned int  datalen;
 }
 
-+ (id)beginTag:(NSString *)_tag attributes:(SaxAttributes *)_attrs;
++ (id)beginTag:(NSString *)_tag group:(NSString *)_group
+  attributes:(SaxAttributes *)_attrs;
 - (id)initEndTag:(NSString *)_tag;
-- (id)initWithData:(NSString *)_data;
+
+- (id)initWithContentString:(NSString *)_data;
 
 - (NSString *)tagName;
+- (BOOL)isStartTag;
+- (BOOL)isEndTag;
+- (BOOL)isTag;
 
 @end
 
-static NSString *VSBeginType = @"BEGIN";
-static NSString *VSEndType   = @"END";
-static NSString *VSDataType  = @"DATA";
-
 @implementation VSSaxTag
 
-+ (id)beginTag:(NSString *)_tag attributes:(SaxAttributes *)_attrs {
++ (id)beginTag:(NSString *)_tag group:(NSString *)_group
+  attributes:(SaxAttributes *)_attrs
+{
   VSSaxTag *tag;
 
   tag = [[[self alloc] init] autorelease];
-  tag->type    = VSBeginType;
+  tag->type    = 'B';
   tag->tagName = [_tag copy];
+  tag->group   = [_group copy];
   tag->attrs   = [_attrs retain];
   return tag;
 }
 - (id)initEndTag:(NSString *)_tag {
-  self->type    = VSEndType;
+  self->type    = 'E';
   self->tagName = [_tag copy];
   return self;
 }
-- (id)initWithData:(NSString *)_data {
-  self->type = VSDataType;
-  self->data = [_data retain];
+- (id)initWithContentString:(NSString *)_data {
+  if (_data == nil) {
+    [self release];
+    return nil;
+  }
+  
+  self->datalen = [_data length];
+  self->data    = calloc(self->datalen + 1, sizeof(unichar));
+  [_data getCharacters:self->data range:NSMakeRange(0, self->datalen)];
   return self;
 }
 
 - (void)dealloc {
-  [self->data    release];
+  if (self->data) free(self->data);
+  [self->group   release];
   [self->tagName release];
   [self->attrs   release];
   [super dealloc];
 }
 
+/* accessors */
+
 - (NSString *)tagName {
   return self->tagName;
 }
+- (NSString *)group {
+  return self->group;
+}
+
+- (BOOL)isStartTag {
+  return self->type == 'B' ? YES : NO;
+}
+- (BOOL)isEndTag {
+  return self->type == 'E' ? YES : NO;
+}
+- (BOOL)isTag {
+  return (self->type == 'B' || self->type == 'E') ? YES : NO;
+}
 
 @end /* VSSaxTag */
 
@@ -236,61 +265,59 @@ static VSStringFormatter *stringFormatter = nil;
 
 /* parsing */
 
+- (NSString *)_groupFromTagName:(NSString *)_tagName {
+  NSRange  r;
+  
+  r = [_tagName rangeOfCharacterFromSet:dotCharSet];
+  if (r.length == 0)
+    return nil;
+  
+  return [_tagName substringToIndex:r.location];
+}
+
 - (NSString *)_mapTagName:(NSString *)_tagName {
   NSString *ret;
   NSRange  r;
 
-  if ((ret = [self->elementMapping objectForKey:_tagName]) == nil) {
-    //NSLog(@"Unknown Key: %@ in %@",_tagName,self->elementMapping);
-    ret = _tagName;
+  if ((ret = [self->elementMapping objectForKey:_tagName]) != nil)
+    return ret;
 
-    /* This is to allow parsing of vCards produced by Apple
-       Addressbook. AFAIK the .dot notation is a non-standard
-       extension */
-    r = [_tagName rangeOfCharacterFromSet:dotCharSet];
-    if (r.length > 0) {
-      ret = [self _mapTagName:[_tagName substringFromIndex:(r.location + 1)]];
-    }
-  }
-  return ret;
-}
-
-- (void)_addAttribute:(NSString *)_attribute
-  value:(NSString *)_value 
-  toAttrs:(SaxAttributes *)_attrs
-{
-  [_attrs addAttribute:_attribute
-          uri:self->prefixURI 
-          rawName:_attribute
-          type:@"CDATA"
-          value:_value];
-}
-
-- (void)_addAttribute:(NSString *)_attribute value:(NSString *)_value {
-  VSSaxTag *element;
+  //NSLog(@"Unknown Key: %@ in %@",_tagName,self->elementMapping);
+  ret = _tagName;
   
-  element = [cardStack lastObject];
-  [self _addAttribute:_attribute value:_value toAttrs:element->attrs];
+  /*
+    This is to allow parsing of vCards produced by Apple
+    Addressbook.
+    The dot-notation is described as 'grouping' in RFC 2425, section 5.8.2.
+  */
+  r = [_tagName rangeOfCharacterFromSet:dotCharSet];
+  if (r.length > 0)
+    ret = [self _mapTagName:[_tagName substringFromIndex:(r.location + 1)]];
+  
+  return ret;
 }
 
 - (NSString *)_mapAttrName:(NSString *)_attrName forTag:(NSString *)_tagName {
+  NSDictionary *tagMap;
   NSString *mappedName;
+
+  /* check whether we have a attr-map stored under the element-name */
+  tagMap = [self->attributeMapping objectForKey:_tagName];
+  if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
+    return mappedName;
   
-  mappedName = [(NSDictionary *)[self->attributeMapping objectForKey:_tagName]
-                                        objectForKey:_attrName];
-  if (mappedName == nil) {
-    mappedName = [(NSDictionary *)[self->attributeMapping objectForKey:
-                                          [self _mapTagName:_tagName]]
-                                          objectForKey:_attrName];
-  }
-  if (mappedName == nil) {
-    mappedName = [(NSDictionary *)[self->attributeMapping objectForKey:@""]
-                                          objectForKey:_attrName];
-  }
-  if (mappedName == nil)
-    mappedName = _attrName;
+  /* check whether we have a attr-map stored under the mapped element-name */
+  tagMap = [self->attributeMapping objectForKey:[self _mapTagName:_tagName]];
+  if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
+    return mappedName;
+
+  /* check whether we have a global attr-map */
+  tagMap = [self->attributeMapping objectForKey:@""];
+  if ((mappedName = [tagMap objectForKey:_attrName]) != nil)
+    return mappedName;
   
-  return mappedName;
+  /* return the name as-is */
+  return _attrName;
 }
 
 - (void)_parseAttr:(NSString *)_attr 
@@ -376,9 +403,8 @@ static VSStringFormatter *stringFormatter = nil;
 
   attrEnum = [attributes keyEnumerator];
   while ((curAttr = [attrEnum nextObject]) != nil) {
-    [self _addAttribute:curAttr
-          value:[attributes objectForKey:curAttr]
-          toAttrs:retAttrs];
+    [retAttrs addAttribute:curAttr uri:self->prefixURI rawName:curAttr
+             type:@"CDATA" value:[attributes objectForKey:curAttr]];
   }
   
   [attributes release];
@@ -386,10 +412,12 @@ static VSStringFormatter *stringFormatter = nil;
   return retAttrs;
 }
 
-- (VSSaxTag *)_beginTag:(NSString *)_tagName withAttrs:(SaxAttributes *)_attrs{
+- (VSSaxTag *)_beginTag:(NSString *)_tagName group:(NSString *)_group
+  withAttrs:(SaxAttributes *)_attrs
+{
   VSSaxTag *tag;
   
-  tag = [VSSaxTag beginTag:_tagName attributes:_attrs];
+  tag = [VSSaxTag beginTag:_tagName group:_group attributes:_attrs];
   [self->elementList addObject:tag];
   return tag;
 }
@@ -402,7 +430,9 @@ static VSStringFormatter *stringFormatter = nil;
   [tag release]; tag = nil;
 }
 
-- (void)_addSubItems:(NSArray *)_items withData:(NSString *)_content {
+- (void)_addSubItems:(NSArray *)_items group:(NSString *)_group
+  withData:(NSString *)_content
+{
   NSEnumerator *itemEnum, *contentEnum;
   NSString *subTag;
   
@@ -414,11 +444,12 @@ static VSStringFormatter *stringFormatter = nil;
     
     subContent = [contentEnum nextObject];
     
-    [self _beginTag:subTag withAttrs:nil];
+    [self _beginTag:subTag group:_group withAttrs:nil];
     if ([subContent length] > 0) {
       VSSaxTag *a;
       
-      if ((a = [(VSSaxTag*)[VSSaxTag alloc] initWithData:subContent]) != nil) {
+      a = [(VSSaxTag*)[VSSaxTag alloc] initWithContentString:subContent];
+      if (a != nil) {
        [self->elementList addObject:a];
        [a release];
       }
@@ -427,73 +458,142 @@ static VSStringFormatter *stringFormatter = nil;
   }
 }
 
-- (void)_dataTag:(NSString *)_tagName 
+- (void)_reportContentAsTag:(NSString *)_tagName
+  group:(NSString *)_group
   withAttrs:(SaxAttributes *)_attrs 
   andContent:(NSString *)_content 
 {
+  /*
+    This is called for all non-BEGIN|END types.
+  */
   NSArray *subItems;
   
   _content = [stringFormatter stringByUnescapingRFC2445Text:_content];
+
+  /* check whether type should be reported as an attribute in XML */
   
   if ([self->attributeElements containsObject:_tagName]) {
-    [self _addAttribute:_tagName value:_content];
+    /* 
+       Add tag as an attribute to last component in the cardstack. This is
+       stuff like the "VERSION" type contained in a "BEGIN:VCARD" which will
+       be reported as <vcard version=""> (as an attribute of the container).
+    */
+    VSSaxTag *element;
+    
+    element = [self->cardStack lastObject];
+    [element->attrs addAttribute:_tagName uri:self->prefixURI
+                   rawName:_tagName type:@"CDATA" value:_content];
     return;
-  } 
+  }
+
+  /* report type as an XML tag */
+  
+  [self _beginTag:_tagName group:_group withAttrs:_attrs];
   
-  [self _beginTag:_tagName withAttrs:_attrs];
   if ([_content length] > 0) {
     if ((subItems = [self->subItemMapping objectForKey:_tagName]) != nil) {
-      [self _addSubItems:subItems withData:_content];
+      [self _addSubItems:subItems group:_group withData:_content];
     }
     else {
       VSSaxTag *a;
-       
-      if ((a = [(VSSaxTag *)[VSSaxTag alloc] initWithData:_content]) != nil) {
+      
+      a = [(VSSaxTag *)[VSSaxTag alloc] initWithContentString:_content];
+      if (a != nil) {
        [self->elementList addObject:a];
        [a release];
       }
     }
   }
+
   [self _endTag:_tagName];
 }
 
 /* report events for collected elements */
 
-- (void)_eventsForElements {
+- (void)reportStartGroup:(NSString *)_group {
+  SaxAttributes *attrs;
+  
+  attrs = [[SaxAttributes alloc] init];
+  [attrs addAttribute:@"name" uri:self->prefixURI rawName:@"name"
+        type:@"CDATA" value:_group];
+  
+  [self->contentHandler startElement:@"group" namespace:self->prefixURI
+                        rawName:@"group" attributes:attrs];
+  [attrs release];
+}
+- (void)reportEndGroup {
+  [self->contentHandler endElement:@"group" namespace:self->prefixURI
+                        rawName:@"group"];
+}
+
+- (void)reportQueuedTags {
+  /*
+    Why does the parser need the list instead of reporting the events
+    straight away?
+    
+    Because some vCard tags like the 'version' are reported as attributes
+    on the container tag. So we have a sequence like:
+      BEGIN:VCARD
+      ...
+      VERSION:3.0
+    which will get reported as:
+      <vcard version="3.0">
+  */
   NSEnumerator *enu;
-  VSSaxTag *obj;
+  VSSaxTag *tagToReport;
+  NSString *lastGroup;
   
+  lastGroup = nil;
   enu = [self->elementList objectEnumerator];
-  while ((obj = [enu nextObject]) != nil) {
-    if ([obj->type isEqualToString:VSBeginType]) {
-      [self->contentHandler startElement:obj->tagName
+  while ((tagToReport = [enu nextObject]) != nil) {
+    if ([tagToReport isStartTag]) {
+      NSString *tg;
+      
+      tg = [tagToReport group];
+      if (![lastGroup isEqualToString:tg] && lastGroup != tg) {
+       if (lastGroup != nil) [self reportEndGroup];
+       ASSIGNCOPY(lastGroup, tg);
+       if (lastGroup != nil) [self reportStartGroup:lastGroup];
+      }
+    }
+    
+    if ([tagToReport isStartTag]) {
+      [self->contentHandler startElement:[tagToReport tagName]
                             namespace:self->prefixURI
-                            rawName:obj->tagName
-                            attributes:obj->attrs];
-    } 
-    else if ([obj->type isEqualToString:VSEndType]) {
-      [self->contentHandler endElement:obj->tagName
+                            rawName:[tagToReport tagName]
+                            attributes:tagToReport->attrs];
+    }
+    else if ([tagToReport isEndTag]) {
+      [self->contentHandler endElement:[tagToReport tagName]
                             namespace:self->prefixURI
-                            rawName:obj->tagName];
+                            rawName:[tagToReport tagName]];
     }
     else {
-      unichar  *chardata;
-      unsigned len;
-      
-      // TODO: better move to tag itself?
-      len      = [obj->data length];
-      chardata = calloc(len + 1, sizeof(unichar));
-      [obj->data getCharacters:chardata range:NSMakeRange(0, len)];
-      
-      [self->contentHandler characters:chardata length:len];
-      if (chardata != NULL) free(chardata); chardata = NULL;
+      [self->contentHandler characters:tagToReport->data
+                            length:tagToReport->datalen];
     }
   }
-  [elementList removeAllObjects];
+  
+  /* flush event group */
+  [self->elementList removeAllObjects];
+  
+  /* close open groups */
+  if (lastGroup != nil) {
+    [self reportEndGroup];
+    [lastGroup release]; lastGroup = nil;
+  }
 }
 
 /* errors */
 
+- (void)error:(NSString *)_text {
+  SaxParseException *e;
+
+  e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
+                            reason:_text
+                            userInfo:nil];
+  [self->errorHandler error:e];
+}
 - (void)warn:(NSString *)_warn {
   SaxParseException *e;
 
@@ -505,6 +605,60 @@ static VSStringFormatter *stringFormatter = nil;
 
 /* parsing raw string */
 
+- (void)_beginComponentWithValue:(NSString *)tagValue {
+  VSSaxTag *tag;
+  
+  tag = [self _beginTag:[self _mapTagName:tagValue]
+             group:nil
+             withAttrs:[[[SaxAttributes alloc] init] autorelease]];
+  [self->cardStack addObject:tag];
+}
+
+- (void)_endComponent:(NSString *)tagName value:(NSString *)tagValue {
+  NSString *mtName;
+    
+  mtName = [self _mapTagName:tagValue];
+  if ([self->cardStack count] > 0) {
+      NSString *expectedName;
+      
+      expectedName = [(VSSaxTag *)[self->cardStack lastObject] tagName];
+      if (![expectedName isEqualToString:mtName]) {
+       NSString *s;
+       
+       // TODO: rather report an error?
+       // TODO: setup userinfo dict with details
+       s = [NSString stringWithFormat:
+                       @"Found end tag '%@' which does not match expected "
+                       @"name '%@'!"
+                       @" Tag '%@' has not been closed properly. Given "
+                       @"document contains errors!",
+                       mtName, expectedName, expectedName];
+       [self error:s];
+       
+        /* probably futile attempt to parse anyways */
+        if (debugOn) {
+          NSLog(@"%s trying to fix previous error by inserting bogus end "
+                @"tag.",
+                __PRETTY_FUNCTION__);
+        }
+        [self _endTag:expectedName];
+        [self->cardStack removeLastObject];
+      }
+  }
+  else {
+      // TOOD: generate error?
+      [self error:[@"found end tag without any open tags left: "
+                  stringByAppendingString:mtName]];
+  }
+  [self _endTag:mtName];
+  [self->cardStack removeLastObject];
+    
+  /* report parsed elements */
+    
+  if ([self->cardStack count] == 0)
+    [self reportQueuedTags];
+}
+
 - (void)_parseLine:(NSString *)_line {
   NSString       *tagName, *tagValue;
   NSMutableArray *tagAttributes;
@@ -518,21 +672,25 @@ static VSStringFormatter *stringFormatter = nil;
              range:todoRange];
   /* is line well-formed? */
   if (r.length == 0) {
-    [self warn:[@"got an improper content line! ->\n" 
-                stringByAppendingString:_line]];
+    [self error:[@"got an improper content line! ->\n" 
+                 stringByAppendingString:_line]];
     return;
   }
-
+  
+  /* tagname is everything up to a ':' or  ';' (value or parameter) */
   tagName       = [[_line substringToIndex:r.location] uppercaseString];
   tagAttributes = [[NSMutableArray alloc] initWithCapacity:16];
   
-  /* possible shortcut: if we spotted a ':', we don't have to do "expensive"
+  /* 
+     possible shortcut: if we spotted a ':', we don't have to do "expensive"
      argument scanning/processing.
   */
   if ([_line characterAtIndex:r.location] != ':') {
-    BOOL isAtEnd = NO, isInDquote = NO;
-    unsigned start = NSMaxRange(r);
-
+    BOOL isAtEnd    = NO;
+    BOOL isInDquote = NO;
+    unsigned start;
+    
+    start     = NSMaxRange(r);
     todoRange = NSMakeRange(start, length - start);
     while(!isAtEnd) {
       BOOL skip = YES;
@@ -541,10 +699,11 @@ static VSStringFormatter *stringFormatter = nil;
       r = [_line rangeOfCharacterFromSet:colonSemicolonAndDquoteCharSet
                  options:0
                  range:todoRange];
+      
       /* is line well-formed? */
       if (r.length == 0 || r.location == 0) {
-       [self warn:[@"got an improper content line! ->\n" 
-                    stringByAppendingString:_line]];
+       [self error:[@"got an improper content line! ->\n" 
+                     stringByAppendingString:_line]];
         [tagAttributes release]; tagAttributes = nil;
         return;
       }
@@ -584,105 +743,70 @@ static VSStringFormatter *stringFormatter = nil;
     }
   }
   tagValue = [_line substringFromIndex:NSMaxRange(r)];
-
+  
+  /*
+    At this point we have:
+      name:      'BEGIN', 'TEL', 'EMAIL', 'ITEM1.ADR' etc
+      value:     ';;;Magdeburg;;;Germany'
+      atributes: ("type=INTERNET", "type=HOME", "type=pref")
+  */
   
   /* process tag */
   
   if ([tagName isEqualToString:@"BEGIN"]) {
-    VSSaxTag *tag;
-    tag = [self _beginTag:[self _mapTagName:tagValue] 
-                withAttrs:[[[SaxAttributes alloc] init] autorelease]];
-    [self->cardStack addObject:tag];
-  } 
+    if ([tagAttributes count] > 0)
+      [self warn:@"Losing unexpected parameters of BEGIN line."];
+    [self _beginComponentWithValue:tagValue];
+  }
   else if ([tagName isEqualToString:@"END"]) {
-    NSString *mtName;
-    
-    mtName = [self _mapTagName:tagValue];
-    if ([self->cardStack count] > 0) {
-      NSString *expectedName;
-      
-      expectedName = [(VSSaxTag *)[self->cardStack lastObject] tagName];
-      if (![expectedName isEqualToString:mtName]) {
-       NSString *s;
-       
-       // TODO: rather report an error?
-       // TODO: setup userinfo dict with details
-       s = [NSString stringWithFormat:
-                       @"Found end tag '%@' which does not match expected "
-                       @"name '%@'!"
-                       @" Tag '%@' has not been closed properly. Given "
-                       @"document contains errors!",
-                       mtName, expectedName, expectedName];
-       [self warn:s];
-       
-        /* probably futile attempt to parse anyways */
-        if (debugOn) {
-          NSLog(@"%s trying to fix previous error by inserting bogus end "
-                @"tag.",
-                __PRETTY_FUNCTION__);
-        }
-        [self _endTag:expectedName];
-        [self->cardStack removeLastObject];
-      }
-    }
-    else {
-      // TOOD: generate error?
-      [self warn:[@"found end tag without any open tags left: "
-                  stringByAppendingString:mtName]];
-    }
-    [self _endTag:mtName];
-    [self->cardStack removeLastObject];
-    
-    /* report parsed elements */
-    
-    if ([self->cardStack count] == 0)
-      [self _eventsForElements];
+    if ([tagAttributes count] > 0)
+      [self warn:@"Losing unexpected parameters of END line."];
+    [self _endComponent:tagName value:tagValue];
   }
   else {
-    [self _dataTag:[self _mapTagName:tagName]
+    [self _reportContentAsTag:[self _mapTagName:tagName]
+         group:[self _groupFromTagName:tagName]
          withAttrs:[self _mapAttrs:tagAttributes forTag:tagName] 
          andContent:tagValue];
   }
+  
   [tagAttributes release];
 }
 
 
 /* top level parsing method */
 
-- (void)_reportDocStart {
+- (void)reportDocStart {
   [self->contentHandler startDocument];
   [self->contentHandler startPrefixMapping:@"" uri:self->prefixURI];
-
-  [self->contentHandler startElement:@"vCardSet" namespace:self->prefixURI
-                        rawName:@"vCardSet" attributes:nil];
 }
-- (void)_reportDocEnd {
-  [self->contentHandler endElement:@"vCardSet" namespace:self->prefixURI
-                        rawName:@"vCardSet"];
-
+- (void)reportDocEnd {
   [self->contentHandler endPrefixMapping:@""];
   [self->contentHandler endDocument];
 }
 
 - (void)_parseString:(NSString *)_rawString {
+  /*
+    This method split the string into content lines for actual vCard
+    parsing.
+
+    RFC2445:
+     contentline        = name *(";" param ) ":" value CRLF
+     ; When parsing a content line, folded lines MUST first
+     ; be unfolded
+  */
   NSMutableString *line;
   unsigned pos, length;
   NSRange  r;
 
-  [self _reportDocStart];
+  [self reportDocStart];
   
   /* start parsing */
   
   length = [_rawString length];
-  /* RFC2445:
-     contentline        = name *(";" param ) ":" value CRLF
-     ; When parsing a content line, folded lines MUST first
-     ; be unfolded
-  */
-  r = NSMakeRange(0, 0);
-  /* probably too optimistic */ 
-  line = [[NSMutableString alloc] initWithCapacity:75 + 2];
-
+  r      = NSMakeRange(0, 0);
+  line   = [[NSMutableString alloc] initWithCapacity:75 + 2];
+  
   for (pos = 0; pos < length; pos++) {
     unichar c;
     
@@ -767,7 +891,7 @@ static VSStringFormatter *stringFormatter = nil;
     [line appendString:[_rawString substringWithRange:r]];
     [self _parseLine:line];
   }
-
+  
   if ([self->cardStack count] != 0) {
     [self warn:@"found elements on cardStack. This indicates an improper "
             @"nesting structure! Not all required events will have been "
@@ -777,7 +901,7 @@ static VSStringFormatter *stringFormatter = nil;
   
   [line release]; line = nil;
   
-  [self _reportDocEnd];
+  [self reportDocEnd];
 }
 
 /* main entry functions */
@@ -867,6 +991,11 @@ static VSStringFormatter *stringFormatter = nil;
     [self->errorHandler fatalError:e];
     return;
   }
+
+  /* ensure consistent state */
+
+  [self->cardStack   removeAllObjects];
+  [self->elementList removeAllObjects];
   
   /* start parsing */
   
@@ -876,6 +1005,11 @@ static VSStringFormatter *stringFormatter = nil;
   }
   if (_sysId == nil) _sysId = @"<string>";
   [self _parseString:_source];
+  
+  /* tear down */
+  
+  [self->cardStack   removeAllObjects];
+  [self->elementList removeAllObjects];
 }
 
 - (void)parseFromSource:(id)_source {
index 79f3c1a061de005b6092a043208f0fb90ceabe4d..02797a16eacb4e32a1840a8f75008a4842739a77 100644 (file)
@@ -51,7 +51,7 @@ static NSArray *defGeoMappings  = nil;
 
 + (NSDictionary *)xcalMapping  {
   static NSDictionary *dict = nil;
-  if (!dict) {
+  if (dict == nil) {
     NSMutableDictionary *xcal;
 
     xcal = [[NSMutableDictionary alloc] initWithCapacity:60];
@@ -290,10 +290,9 @@ static NSArray *defGeoMappings  = nil;
   return dict;
 }
 
-+ (NSDictionary *)xcalAttrMapping 
-{
++ (NSDictionary *)xcalAttrMapping {
   static NSDictionary *dict = nil;
-  if (!dict) {
+  if (dict == nil) {
     NSMutableDictionary *xcal;
 
     xcal = [[NSMutableDictionary alloc] initWithCapacity:20];
index e8b6116e53648bc88443d1c030767008f1c8368c..cf93a84b9e2c5d930d6b9136fb8dd05dbacbd06d 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "VSSaxDriver.h"
 
+// TODO: photo is reported incorrectly
+
 @interface VSvCardSaxDriver : VSSaxDriver 
 {
 }
index cdc08b996e85340299d6a4904e108f369b8ce1fe..3fe0a4c427e1ef739e55e3766452382d63b4033d 100644 (file)
@@ -308,9 +308,9 @@ static NSSet *defElementNames = nil;
 }
 
 - (id)init {
-  if ((self = [super init])) {
-    
-    [self setPrefixURI:@"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"];
+  if ((self = [super init]) != nil) {
+    [self setPrefixURI:
+           @"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"];
     [self setElementMapping:[[self class] xcardMapping]];
     [self setAttributeElements:defElementNames];
     [self _setVCardAttributeMappings];
@@ -319,4 +319,19 @@ static NSSet *defElementNames = nil;
   return self;
 }
 
+/* top level parsing method */
+
+- (void)reportDocStart {
+  [super reportDocStart];
+  
+  [self->contentHandler startElement:@"vCardSet" namespace:self->prefixURI
+                        rawName:@"vCardSet" attributes:nil];
+}
+- (void)reportDocEnd {
+  [self->contentHandler endElement:@"vCardSet" namespace:self->prefixURI
+                        rawName:@"vCardSet"];
+  
+  [super reportDocEnd];
+}
+
 @end /* VCardSaxDriver */
index f61be5386e4c1a375b68ef728141c463d6a08a8f..cb19d00e6ced7918a3ba059adf50aa14fb9d6789 100644 (file)
@@ -1,3 +1,3 @@
 # Version file
 
-SUBMINOR_VERSION:=14
+SUBMINOR_VERSION:=15