]> err.no Git - sope/blob - sope-ical/NGiCal/NGVCardSaxHandler.m
more work on vCard parsing
[sope] / sope-ical / NGiCal / NGVCardSaxHandler.m
1 /*
2   Copyright (C) 2005 Helge Hess
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "NGVCardSaxHandler.h"
23 #include "NGVCard.h"
24 #include "NGVCardValue.h"
25 #include "NGVCardSimpleValue.h"
26 #include "NGVCardAddress.h"
27 #include "NGVCardPhone.h"
28 #include "NGVCardName.h"
29 #include "NGVCardOrg.h"
30 #include "NGVCardCategories.h"
31 #include "common.h"
32
33 #ifndef XMLNS_VCARD_XML_03
34 #  define XMLNS_VCARD_XML_03 \
35      @"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
36 #endif
37
38 @implementation NGVCardSaxHandler
39
40 - (void)dealloc {
41   if (self->content != NULL) free(self->content);
42   [self->subvalues    release];
43   [self->xtags        release];
44   [self->tel          release];
45   [self->adr          release];
46   [self->email        release];
47   [self->label        release];
48   [self->types        release];
49   [self->args         release];
50   [self->vCards       release];
51   [self->vCard        release];
52   [self->currentGroup release];
53   [super dealloc];
54 }
55
56 /* results */
57
58 - (NSArray *)vCards {
59   return self->vCards;
60 }
61
62 /* state */
63
64 - (void)resetExceptResult {
65   [self->currentGroup release]; self->currentGroup = nil;
66   [self->vCard        release]; self->vCard        = nil;
67
68   [self->tel    removeAllObjects];
69   [self->adr    removeAllObjects];
70   [self->email  removeAllObjects];
71   [self->label  removeAllObjects];
72   [self->types  removeAllObjects];
73   [self->args   removeAllObjects];
74   
75   if (self->content != NULL) {
76     free(self->content);
77     self->content = NULL;
78   }
79   
80   self->vcs.isInVCardSet   = 0;
81   self->vcs.isInVCard      = 0;
82   self->vcs.isInN          = 0;
83   self->vcs.isInAdr        = 0;
84   self->vcs.isInOrg        = 0;
85   self->vcs.isInGroup      = 0;
86   self->vcs.collectContent = 0;
87 }
88
89 - (void)reset {
90   [self resetExceptResult];
91   [self->vCards removeAllObjects];
92 }
93
94 /* document events */
95
96 - (void)startDocument {
97   [self reset];
98   
99   if (self->vCards == nil)
100     self->vCards = [[NSMutableArray alloc] initWithCapacity:16];
101
102   if (self->tel == nil)
103     self->tel = [[NSMutableArray alloc] initWithCapacity:8];
104   if (self->adr == nil)
105     self->adr = [[NSMutableArray alloc] initWithCapacity:8];
106   if (self->email == nil)
107     self->email = [[NSMutableArray alloc] initWithCapacity:8];
108   if (self->label == nil)
109     self->label = [[NSMutableArray alloc] initWithCapacity:8];
110
111   if (self->types == nil)
112     self->types = [[NSMutableArray alloc] initWithCapacity:4];
113   if (self->args == nil)
114     self->args = [[NSMutableDictionary alloc] initWithCapacity:8];
115   
116   if (self->subvalues == nil)
117     self->subvalues = [[NSMutableDictionary alloc] initWithCapacity:16];
118   if (self->xtags == nil)
119     self->xtags = [[NSMutableDictionary alloc] initWithCapacity:32];
120 }
121 - (void)endDocument {
122   [self resetExceptResult];
123 }
124
125 /* common tags */
126
127 - (void)startValueTag:(NSString *)_tag attributes:(id<SaxAttributes>)_attrs {
128   unsigned i, count;
129   
130   [self->types removeAllObjects];
131   [self->args  removeAllObjects];
132   
133   for (i = 0, count = [_attrs count]; i < count; i++) {
134     NSString *n, *v;
135     
136     n = [_attrs nameAtIndex:i];
137     v = [_attrs valueAtIndex:i];
138     
139     if ([n hasSuffix:@".type"] || [n isEqualToString:@"TYPE"]) {
140       if (![self->types containsObject:v]) 
141         [self->types addObjectsFromArray:[v componentsSeparatedByString:@" "]];
142     }
143     else
144       [self->args setObject:v forKey:n];
145   }
146 }
147 - (void)endValueTag {
148   [self->types removeAllObjects];
149   [self->args  removeAllObjects];
150 }
151
152 /* handle elements */
153
154 - (void)startGroup:(NSString *)_name {
155   self->vcs.isInGroup = 1;
156   ASSIGNCOPY(self->currentGroup, _name);
157 }
158 - (void)endGroup {
159   self->vcs.isInGroup = 0;
160   [self->currentGroup release]; self->currentGroup = nil;
161 }
162
163 - (void)startN {
164   [self->subvalues removeAllObjects];
165   self->vcs.isInN = 1;
166 }
167 - (void)endN {
168   NGVCardName *n;
169   
170   self->vcs.isInN = 0;
171
172   n = [[NGVCardName alloc] initWithPropertyList:self->subvalues
173                            group:self->currentGroup
174                            types:self->types arguments:self->args];
175   NSLog(@"N: %@", n);
176   
177   [self->subvalues removeAllObjects];
178   [n release];
179 }
180
181 - (void)startOrg {
182   [self->subvalues removeAllObjects];
183   self->vcs.isInOrg = 1;
184 }
185 - (void)endOrg {
186   NGVCardOrg *o;
187
188   self->vcs.isInOrg = 0;
189
190   o = [[NGVCardOrg alloc] initWithGroup:self->currentGroup
191                           types:self->types arguments:self->args];
192   
193   NSLog(@"O: %@: %@", o, self->subvalues);
194   
195   [self->subvalues removeAllObjects];
196 }
197
198 - (void)startVCard:(id<SaxAttributes>)_attrs {
199   NSString *uid, *version;
200   NSString *t;
201   
202   [self->tel   removeAllObjects];
203   [self->adr   removeAllObjects];
204   [self->email removeAllObjects];
205   [self->label removeAllObjects];
206   [self->xtags removeAllObjects];
207   
208   self->vcs.isInVCard = 1;
209   if (self->vCard != nil) {
210     [self->vCards addObject:self->vCard];
211     [self logWithFormat:@"ERROR: vCard nesting not supported!"];
212     [self->vCard release]; self->vCard = nil;
213   }
214   
215   if ((uid = [_attrs valueForName:@"uid" uri:XMLNS_VCARD_XML_03]) == nil)
216     uid = [_attrs valueForName:@"X-ABUID" uri:XMLNS_VCARD_XML_03];
217   
218   version = [_attrs valueForName:@"version" uri:XMLNS_VCARD_XML_03];
219   
220   self->vCard = [[NGVCard alloc] initWithUid:uid version:version];
221
222   if ((t = [_attrs valueForName:@"class" uri:XMLNS_VCARD_XML_03]) != nil)
223     [self->vCard setVClass:t];
224   if ((t = [_attrs valueForName:@"rev" uri:XMLNS_VCARD_XML_03]) != nil) {
225     [self logWithFormat:@"WARNING: vCard revision not yet supported!"];
226     // TODO
227   }
228   if ((t = [_attrs valueForName:@"prodid" uri:XMLNS_VCARD_XML_03]) != nil)
229     [self->vCard setProdID:t];
230   
231   [self debugWithFormat:@"started vCard: %@", self->vCard];
232 }
233 - (void)endVCard {
234   self->vcs.isInVCard = 0;
235
236   /* fill collected objects */
237   
238   if ([self->tel   count] > 0) [self->vCard setTel:self->tel];
239   if ([self->adr   count] > 0) [self->vCard setAdr:self->adr];
240   if ([self->email count] > 0) [self->vCard setEmail:self->email];
241   if ([self->label count] > 0) [self->vCard setLabel:self->label];
242   if ([self->xtags count] > 0) [self->vCard setX:self->xtags];
243   [self->tel   removeAllObjects];
244   [self->adr   removeAllObjects];
245   [self->email removeAllObjects];
246   [self->label removeAllObjects];
247   [self->xtags removeAllObjects];
248
249   /* finish vCard, add to results */
250   
251   [self debugWithFormat:@"finished vCard: %@", self->vCard];
252   [self->vCards addObject:self->vCard];
253   [self->vCard release]; self->vCard = nil;
254 }
255
256 - (void)startVCardSet:(id<SaxAttributes>)_attrs {
257   self->vcs.isInVCardSet = 1;
258 }
259 - (void)endVCardSet {
260   self->vcs.isInVCardSet = 0;
261 }
262
263 - (void)endBaseContentTagWithClass:(Class)_clazz andAddTo:(NSMutableArray*)_a {
264   NGVCardSimpleValue *v;
265
266   v = [[_clazz alloc] initWithValue:[self finishCollectingContent]
267                       group:self->currentGroup
268                       types:self->types arguments:self->args];
269   [_a addObject:v];
270   
271   [self endValueTag];
272   [v release];
273 }
274
275 - (void)startTel:(id<SaxAttributes>)_attrs {
276   [self startValueTag:@"tel" attributes:_attrs];
277   [self startCollectingContent];
278 }
279 - (void)endTel {
280   [self endBaseContentTagWithClass:[NGVCardPhone class] andAddTo:self->tel];
281 }
282
283 - (void)startAdr:(id<SaxAttributes>)_attrs {
284   [self->subvalues removeAllObjects];
285
286   self->vcs.isInAdr = 1;
287   [self startValueTag:@"adr" attributes:_attrs];
288 }
289 - (void)endAdr {
290   NGVCardAddress *address;
291
292   self->vcs.isInAdr = 0;
293   
294   address = [[NGVCardAddress alloc] initWithPropertyList:self->subvalues
295                                     group:self->currentGroup
296                                     types:self->types arguments:self->args];
297   NSLog(@"A: %@", address);
298   [self->adr addObject:address];
299   
300   [self->subvalues removeAllObjects];
301   [self endValueTag];
302   [address release];
303 }
304
305 - (void)startEmail:(id<SaxAttributes>)_attrs {
306   [self startValueTag:@"email" attributes:_attrs];
307   [self startCollectingContent];
308 }
309 - (void)endEmail {
310   [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
311         andAddTo:self->email];
312 }
313
314 - (void)startLabel:(id<SaxAttributes>)_attrs {
315   [self startValueTag:@"LABEL" attributes:_attrs];
316   [self startCollectingContent];
317 }
318 - (void)endLabel {
319   [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
320         andAddTo:self->label];
321 }
322
323 - (void)startURL:(id<SaxAttributes>)_attrs {
324   [self startValueTag:@"url" attributes:_attrs];
325   [self startCollectingContent];
326 }
327 - (void)endURL {
328   // TODO: use special URL class?
329   [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
330         andAddTo:self->url];
331 }
332
333 - (void)startSubContentTag:(id<SaxAttributes>)_attrs {
334   if ([_attrs count] > 0)
335     [self logWithFormat:@"WARNING: loosing attrs of subtag: %@", _attrs];
336   
337   [self startCollectingContent];
338 }
339 - (void)endSubContentTag:(NSString *)_key {
340   NSString *s;
341   id o;
342   
343   if ((s = [[self finishCollectingContent] copy]) == nil)
344     return;
345   
346   if ((o = [self->subvalues objectForKey:_key]) == nil) {
347     [self->subvalues setObject:s forKey:_key];
348   }
349   else {
350     /* multivalue (eg 'org') */
351     if ([o isKindOfClass:[NSMutableArray class]]) {
352       [o addObject:s];
353     }
354     else {
355       NSMutableArray *a;
356       
357       a = [[NSMutableArray alloc] initWithCapacity:4];
358       [a addObject:o];
359       [a addObject:s];
360       [self->subvalues setObject:a forKey:_key];
361       [a release]; a = nil;
362     }
363   }
364   [s release];
365 }
366
367
368 - (void)startX:(NSString *)_name attributes:(id<SaxAttributes>)_attrs {
369   [self startValueTag:_name attributes:_attrs];
370   [self startCollectingContent];
371 }
372 - (void)endX:(NSString *)_name {
373   NGVCardSimpleValue *v;
374   NSString *s;
375   id o;
376   
377   s = [self finishCollectingContent];
378   v = [[NGVCardSimpleValue alloc] initWithValue:s
379                                   group:self->currentGroup
380                                   types:self->types arguments:self->args];
381   
382   if ((o = [self->xtags objectForKey:_name]) == nil)
383     [self->xtags setObject:v forKey:_name];
384   else if ([o isKindOfClass:[NSMutableArray class]])
385     [o addObject:v];
386   else {
387     NSMutableArray *a;
388     
389     a = [[NSMutableArray alloc] initWithCapacity:4];
390     [a addObject:o];
391     [a addObject:v];
392     [self->xtags setObject:a forKey:_name];
393     [a release];
394   }
395   
396   [v release];
397   [self endValueTag];
398 }
399
400
401 /* element events */
402
403 - (void)startElement:(NSString *)_localName
404   namespace:(NSString *)_ns
405   rawName:(NSString *)_rawName
406   attributes:(id<SaxAttributes>)_attrs
407 {
408   unichar c0 = [_localName characterAtIndex:0];
409
410   if (c0 == 'g' && [_localName isEqualToString:@"group"])
411     [self startGroup:[_attrs valueForName:@"name" uri:_ns]];
412   else if (c0 == 'n' && [_localName length] == 1)
413     [self startN];
414   else if (c0 == 'o' && [_localName isEqualToString:@"org"])
415     [self startOrg];
416   else if (c0 == 't' && [_localName isEqualToString:@"tel"])
417     [self startTel:_attrs];
418   else if (c0 == 'u' && [_localName isEqualToString:@"url"])
419     [self startURL:_attrs];
420   else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
421     [self startAdr:_attrs];
422   else if (c0 == 'e' && [_localName isEqualToString:@"email"])
423     [self startEmail:_attrs];
424   else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
425     [self startLabel:_attrs];
426   else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
427     [self startVCard:_attrs];
428   else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
429     [self startVCardSet:_attrs];
430   else {
431     if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr)
432       [self startSubContentTag:_attrs];
433     else if (c0 == 'X')
434       [self startX:_localName attributes:_attrs];
435     else
436       NSLog(@"U: %@", _localName);
437   }
438 }
439
440 - (void)endElement:(NSString *)_localName
441   namespace:(NSString *)_ns
442   rawName:(NSString *)_rawName
443 {
444   unichar c0 = [_localName characterAtIndex:0];
445
446   if (c0 == 'g' && [_localName isEqualToString:@"group"])
447     [self endGroup];
448   else if (c0 == 'n' && [_localName isEqualToString:@"n"])
449     [self endN];
450   else if (c0 == 'o' && [_localName isEqualToString:@"org"])
451     [self endOrg];
452   else if (c0 == 't' && [_localName isEqualToString:@"tel"])
453     [self endTel];
454   else if (c0 == 'u' && [_localName isEqualToString:@"url"])
455     [self endURL];
456   else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
457     [self endAdr];
458   else if (c0 == 'e' && [_localName isEqualToString:@"email"])
459     [self endEmail];
460   else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
461     [self endLabel];
462   else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
463     [self endVCard];
464   else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
465     [self endVCardSet];
466   else {
467     if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr)
468       [self endSubContentTag:_localName];
469     else if (c0 == 'X')
470       [self endX:_localName];
471   }
472 }
473
474 /* content */
475
476 - (void)startCollectingContent {
477   if (self->content != NULL) {
478     free(self->content);
479     self->content = NULL;
480   }
481   self->vcs.collectContent = 1;
482 }
483
484 - (NSString *)finishCollectingContent {
485   NSString *s;
486   
487   self->vcs.collectContent = 0;
488   
489   if (self->content == NULL)
490     return nil;
491   
492   if (self->contentLength == 0)
493     return @"";
494   
495   s = [NSString stringWithCharacters:self->content length:self->contentLength];
496   if (self->content != NULL) {
497     free(self->content);
498     self->content = NULL;
499   }
500   return s;
501 }
502
503 - (void)characters:(unichar *)_chars length:(int)_len {
504   if (_len == 0 || _chars == NULL)
505     return;
506   
507   if (self->content == NULL) {
508     /* first content */
509     self->contentLength = _len;
510     self->content       = calloc(_len + 1, sizeof(unichar));
511     memcpy(self->content, _chars, (_len * sizeof(unichar)));
512   }
513   else {
514     /* increase content */
515     self->content = 
516       realloc(self->content, (self->contentLength + _len+2) * sizeof(unichar));
517     memcpy(&(self->content[self->contentLength]), _chars, 
518            (_len * sizeof(unichar)));
519     self->contentLength += _len;
520   }
521   self->content[self->contentLength] = 0;
522 }
523
524 @end /* NGVCardSaxHandler */