]> err.no Git - sope/blobdiff - sope-ical/NGiCal/NGVCardSaxHandler.m
more work on vCard parsing
[sope] / sope-ical / NGiCal / NGVCardSaxHandler.m
index 71ccd1cf5dfb3bc9f0f519e9295d6d9f681282fb..1c0dd60dc4814cffa1322de66dbe88421928c9fe 100644 (file)
 */
 
 #include "NGVCardSaxHandler.h"
+#include "NGVCard.h"
+#include "NGVCardValue.h"
+#include "NGVCardSimpleValue.h"
+#include "NGVCardAddress.h"
+#include "NGVCardPhone.h"
+#include "NGVCardName.h"
+#include "NGVCardOrg.h"
+#include "NGVCardCategories.h"
 #include "common.h"
 
+#ifndef XMLNS_VCARD_XML_03
+#  define XMLNS_VCARD_XML_03 \
+     @"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
+#endif
+
 @implementation NGVCardSaxHandler
 
 - (void)dealloc {
   if (self->content != NULL) free(self->content);
+  [self->subvalues    release];
+  [self->xtags        release];
+  [self->tel          release];
+  [self->adr          release];
+  [self->email        release];
+  [self->label        release];
+  [self->types        release];
+  [self->args         release];
   [self->vCards       release];
   [self->vCard        release];
   [self->currentGroup release];
   [super dealloc];
 }
 
+/* results */
+
+- (NSArray *)vCards {
+  return self->vCards;
+}
+
 /* state */
 
 - (void)resetExceptResult {
   [self->currentGroup release]; self->currentGroup = nil;
   [self->vCard        release]; self->vCard        = nil;
 
+  [self->tel    removeAllObjects];
+  [self->adr    removeAllObjects];
+  [self->email  removeAllObjects];
+  [self->label  removeAllObjects];
+  [self->types  removeAllObjects];
+  [self->args   removeAllObjects];
+  
   if (self->content != NULL) {
     free(self->content);
     self->content = NULL;
@@ -46,6 +80,8 @@
   self->vcs.isInVCardSet   = 0;
   self->vcs.isInVCard      = 0;
   self->vcs.isInN          = 0;
+  self->vcs.isInAdr        = 0;
+  self->vcs.isInOrg        = 0;
   self->vcs.isInGroup      = 0;
   self->vcs.collectContent = 0;
 }
 
 - (void)startDocument {
   [self reset];
+  
   if (self->vCards == nil)
     self->vCards = [[NSMutableArray alloc] initWithCapacity:16];
+
+  if (self->tel == nil)
+    self->tel = [[NSMutableArray alloc] initWithCapacity:8];
+  if (self->adr == nil)
+    self->adr = [[NSMutableArray alloc] initWithCapacity:8];
+  if (self->email == nil)
+    self->email = [[NSMutableArray alloc] initWithCapacity:8];
+  if (self->label == nil)
+    self->label = [[NSMutableArray alloc] initWithCapacity:8];
+
+  if (self->types == nil)
+    self->types = [[NSMutableArray alloc] initWithCapacity:4];
+  if (self->args == nil)
+    self->args = [[NSMutableDictionary alloc] initWithCapacity:8];
+  
+  if (self->subvalues == nil)
+    self->subvalues = [[NSMutableDictionary alloc] initWithCapacity:16];
+  if (self->xtags == nil)
+    self->xtags = [[NSMutableDictionary alloc] initWithCapacity:32];
 }
 - (void)endDocument {
   [self resetExceptResult];
 }
 
+/* common tags */
+
+- (void)startValueTag:(NSString *)_tag attributes:(id<SaxAttributes>)_attrs {
+  unsigned i, count;
+  
+  [self->types removeAllObjects];
+  [self->args  removeAllObjects];
+  
+  for (i = 0, count = [_attrs count]; i < count; i++) {
+    NSString *n, *v;
+    
+    n = [_attrs nameAtIndex:i];
+    v = [_attrs valueAtIndex:i];
+    
+    if ([n hasSuffix:@".type"] || [n isEqualToString:@"TYPE"]) {
+      if (![self->types containsObject:v]) 
+       [self->types addObjectsFromArray:[v componentsSeparatedByString:@" "]];
+    }
+    else
+      [self->args setObject:v forKey:n];
+  }
+}
+- (void)endValueTag {
+  [self->types removeAllObjects];
+  [self->args  removeAllObjects];
+}
+
 /* handle elements */
 
 - (void)startGroup:(NSString *)_name {
 }
 
 - (void)startN {
+  [self->subvalues removeAllObjects];
   self->vcs.isInN = 1;
 }
 - (void)endN {
+  NGVCardName *n;
+  
   self->vcs.isInN = 0;
+
+  n = [[NGVCardName alloc] initWithPropertyList:self->subvalues
+                          group:self->currentGroup
+                          types:self->types arguments:self->args];
+  NSLog(@"N: %@", n);
+  
+  [self->subvalues removeAllObjects];
+  [n release];
+}
+
+- (void)startOrg {
+  [self->subvalues removeAllObjects];
+  self->vcs.isInOrg = 1;
+}
+- (void)endOrg {
+  NGVCardOrg *o;
+
+  self->vcs.isInOrg = 0;
+
+  o = [[NGVCardOrg alloc] initWithGroup:self->currentGroup
+                         types:self->types arguments:self->args];
+  
+  NSLog(@"O: %@: %@", o, self->subvalues);
+  
+  [self->subvalues removeAllObjects];
 }
 
 - (void)startVCard:(id<SaxAttributes>)_attrs {
+  NSString *uid, *version;
+  NSString *t;
+  
+  [self->tel   removeAllObjects];
+  [self->adr   removeAllObjects];
+  [self->email removeAllObjects];
+  [self->label removeAllObjects];
+  [self->xtags removeAllObjects];
+  
   self->vcs.isInVCard = 1;
+  if (self->vCard != nil) {
+    [self->vCards addObject:self->vCard];
+    [self logWithFormat:@"ERROR: vCard nesting not supported!"];
+    [self->vCard release]; self->vCard = nil;
+  }
+  
+  if ((uid = [_attrs valueForName:@"uid" uri:XMLNS_VCARD_XML_03]) == nil)
+    uid = [_attrs valueForName:@"X-ABUID" uri:XMLNS_VCARD_XML_03];
+  
+  version = [_attrs valueForName:@"version" uri:XMLNS_VCARD_XML_03];
+  
+  self->vCard = [[NGVCard alloc] initWithUid:uid version:version];
+
+  if ((t = [_attrs valueForName:@"class" uri:XMLNS_VCARD_XML_03]) != nil)
+    [self->vCard setVClass:t];
+  if ((t = [_attrs valueForName:@"rev" uri:XMLNS_VCARD_XML_03]) != nil) {
+    [self logWithFormat:@"WARNING: vCard revision not yet supported!"];
+    // TODO
+  }
+  if ((t = [_attrs valueForName:@"prodid" uri:XMLNS_VCARD_XML_03]) != nil)
+    [self->vCard setProdID:t];
+  
+  [self debugWithFormat:@"started vCard: %@", self->vCard];
 }
 - (void)endVCard {
   self->vcs.isInVCard = 0;
+
+  /* fill collected objects */
+  
+  if ([self->tel   count] > 0) [self->vCard setTel:self->tel];
+  if ([self->adr   count] > 0) [self->vCard setAdr:self->adr];
+  if ([self->email count] > 0) [self->vCard setEmail:self->email];
+  if ([self->label count] > 0) [self->vCard setLabel:self->label];
+  if ([self->xtags count] > 0) [self->vCard setX:self->xtags];
+  [self->tel   removeAllObjects];
+  [self->adr   removeAllObjects];
+  [self->email removeAllObjects];
+  [self->label removeAllObjects];
+  [self->xtags removeAllObjects];
+
+  /* finish vCard, add to results */
+  
+  [self debugWithFormat:@"finished vCard: %@", self->vCard];
+  [self->vCards addObject:self->vCard];
+  [self->vCard release]; self->vCard = nil;
 }
 
 - (void)startVCardSet:(id<SaxAttributes>)_attrs {
   self->vcs.isInVCardSet = 0;
 }
 
+- (void)endBaseContentTagWithClass:(Class)_clazz andAddTo:(NSMutableArray*)_a {
+  NGVCardSimpleValue *v;
+
+  v = [[_clazz alloc] initWithValue:[self finishCollectingContent]
+                     group:self->currentGroup
+                     types:self->types arguments:self->args];
+  [_a addObject:v];
+  
+  [self endValueTag];
+  [v release];
+}
+
+- (void)startTel:(id<SaxAttributes>)_attrs {
+  [self startValueTag:@"tel" attributes:_attrs];
+  [self startCollectingContent];
+}
+- (void)endTel {
+  [self endBaseContentTagWithClass:[NGVCardPhone class] andAddTo:self->tel];
+}
+
+- (void)startAdr:(id<SaxAttributes>)_attrs {
+  [self->subvalues removeAllObjects];
+
+  self->vcs.isInAdr = 1;
+  [self startValueTag:@"adr" attributes:_attrs];
+}
+- (void)endAdr {
+  NGVCardAddress *address;
+
+  self->vcs.isInAdr = 0;
+  
+  address = [[NGVCardAddress alloc] initWithPropertyList:self->subvalues
+                                   group:self->currentGroup
+                                   types:self->types arguments:self->args];
+  NSLog(@"A: %@", address);
+  [self->adr addObject:address];
+  
+  [self->subvalues removeAllObjects];
+  [self endValueTag];
+  [address release];
+}
+
+- (void)startEmail:(id<SaxAttributes>)_attrs {
+  [self startValueTag:@"email" attributes:_attrs];
+  [self startCollectingContent];
+}
+- (void)endEmail {
+  [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
+       andAddTo:self->email];
+}
+
+- (void)startLabel:(id<SaxAttributes>)_attrs {
+  [self startValueTag:@"LABEL" attributes:_attrs];
+  [self startCollectingContent];
+}
+- (void)endLabel {
+  [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
+       andAddTo:self->label];
+}
+
+- (void)startURL:(id<SaxAttributes>)_attrs {
+  [self startValueTag:@"url" attributes:_attrs];
+  [self startCollectingContent];
+}
+- (void)endURL {
+  // TODO: use special URL class?
+  [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
+       andAddTo:self->url];
+}
+
+- (void)startSubContentTag:(id<SaxAttributes>)_attrs {
+  if ([_attrs count] > 0)
+    [self logWithFormat:@"WARNING: loosing attrs of subtag: %@", _attrs];
+  
+  [self startCollectingContent];
+}
+- (void)endSubContentTag:(NSString *)_key {
+  NSString *s;
+  id o;
+  
+  if ((s = [[self finishCollectingContent] copy]) == nil)
+    return;
+  
+  if ((o = [self->subvalues objectForKey:_key]) == nil) {
+    [self->subvalues setObject:s forKey:_key];
+  }
+  else {
+    /* multivalue (eg 'org') */
+    if ([o isKindOfClass:[NSMutableArray class]]) {
+      [o addObject:s];
+    }
+    else {
+      NSMutableArray *a;
+      
+      a = [[NSMutableArray alloc] initWithCapacity:4];
+      [a addObject:o];
+      [a addObject:s];
+      [self->subvalues setObject:a forKey:_key];
+      [a release]; a = nil;
+    }
+  }
+  [s release];
+}
+
+
+- (void)startX:(NSString *)_name attributes:(id<SaxAttributes>)_attrs {
+  [self startValueTag:_name attributes:_attrs];
+  [self startCollectingContent];
+}
+- (void)endX:(NSString *)_name {
+  NGVCardSimpleValue *v;
+  NSString *s;
+  id o;
+  
+  s = [self finishCollectingContent];
+  v = [[NGVCardSimpleValue alloc] initWithValue:s
+                                 group:self->currentGroup
+                                 types:self->types arguments:self->args];
+  
+  if ((o = [self->xtags objectForKey:_name]) == nil)
+    [self->xtags setObject:v forKey:_name];
+  else if ([o isKindOfClass:[NSMutableArray class]])
+    [o addObject:v];
+  else {
+    NSMutableArray *a;
+    
+    a = [[NSMutableArray alloc] initWithCapacity:4];
+    [a addObject:o];
+    [a addObject:v];
+    [self->xtags setObject:a forKey:_name];
+    [a release];
+  }
+  
+  [v release];
+  [self endValueTag];
+}
+
+
 /* element events */
 
 - (void)startElement:(NSString *)_localName
   rawName:(NSString *)_rawName
   attributes:(id<SaxAttributes>)_attrs
 {
-  if ([_localName isEqualToString:@"group"])
+  unichar c0 = [_localName characterAtIndex:0];
+
+  if (c0 == 'g' && [_localName isEqualToString:@"group"])
     [self startGroup:[_attrs valueForName:@"name" uri:_ns]];
-  else if ([_localName isEqualToString:@"n"])
+  else if (c0 == 'n' && [_localName length] == 1)
     [self startN];
-  else if ([_localName isEqualToString:@"tel"])
-    ;
-  else if ([_localName isEqualToString:@"vCard"])
+  else if (c0 == 'o' && [_localName isEqualToString:@"org"])
+    [self startOrg];
+  else if (c0 == 't' && [_localName isEqualToString:@"tel"])
+    [self startTel:_attrs];
+  else if (c0 == 'u' && [_localName isEqualToString:@"url"])
+    [self startURL:_attrs];
+  else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
+    [self startAdr:_attrs];
+  else if (c0 == 'e' && [_localName isEqualToString:@"email"])
+    [self startEmail:_attrs];
+  else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
+    [self startLabel:_attrs];
+  else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
     [self startVCard:_attrs];
-  else if ([_localName isEqualToString:@"vCardSet"])
+  else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
     [self startVCardSet:_attrs];
-  
-  if (self->vcs.isInN) {
+  else {
+    if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr)
+      [self startSubContentTag:_attrs];
+    else if (c0 == 'X')
+      [self startX:_localName attributes:_attrs];
+    else
+      NSLog(@"U: %@", _localName);
   }
 }
 
   namespace:(NSString *)_ns
   rawName:(NSString *)_rawName
 {
-  if (self->vcs.isInN) {
-  }
-  
-  if ([_localName isEqualToString:@"group"])
+  unichar c0 = [_localName characterAtIndex:0];
+
+  if (c0 == 'g' && [_localName isEqualToString:@"group"])
     [self endGroup];
-  else if ([_localName isEqualToString:@"n"])
+  else if (c0 == 'n' && [_localName isEqualToString:@"n"])
     [self endN];
-  else if ([_localName isEqualToString:@"tel"])
-    ;
-  else if ([_localName isEqualToString:@"vCard"])
+  else if (c0 == 'o' && [_localName isEqualToString:@"org"])
+    [self endOrg];
+  else if (c0 == 't' && [_localName isEqualToString:@"tel"])
+    [self endTel];
+  else if (c0 == 'u' && [_localName isEqualToString:@"url"])
+    [self endURL];
+  else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
+    [self endAdr];
+  else if (c0 == 'e' && [_localName isEqualToString:@"email"])
+    [self endEmail];
+  else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
+    [self endLabel];
+  else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
     [self endVCard];
-  else if ([_localName isEqualToString:@"vCardSet"])
+  else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
     [self endVCardSet];
+  else {
+    if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr)
+      [self endSubContentTag:_localName];
+    else if (c0 == 'X')
+      [self endX:_localName];
+  }
 }
 
 /* content */