2 Copyright (C) 2005 Helge Hess
4 This file is part of SOPE.
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
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.
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
22 #include "NGVCardSaxHandler.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 "NGVCardStrArrayValue.h"
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"
38 // TODO: this is wayyy to big and complicated ;->
40 @implementation NGVCardSaxHandler
43 if (self->content != NULL) free(self->content);
44 [self->subvalues release];
45 [self->xtags release];
48 [self->email release];
49 [self->label release];
51 [self->fburl release];
52 [self->caluri release];
53 [self->types release];
55 [self->vCards release];
56 [self->vCard release];
57 [self->currentGroup release];
69 - (void)resetCardState {
70 [self->tel removeAllObjects];
71 [self->adr removeAllObjects];
72 [self->email removeAllObjects];
73 [self->label removeAllObjects];
74 [self->url removeAllObjects];
75 [self->fburl removeAllObjects];
76 [self->caluri removeAllObjects];
77 [self->xtags removeAllObjects];
78 [self->currentGroup release]; self->currentGroup = nil;
79 [self->types removeAllObjects];
80 [self->args removeAllObjects];
81 [self->vCard release]; self->vCard = nil;
84 - (void)resetExceptResult {
85 [self->vCard release]; self->vCard = nil;
87 [self resetCardState];
89 if (self->content != NULL) {
94 self->vcs.isInVCardSet = 0;
95 self->vcs.isInVCard = 0;
97 self->vcs.isInAdr = 0;
98 self->vcs.isInOrg = 0;
99 self->vcs.isInGroup = 0;
100 self->vcs.collectContent = 0;
104 [self resetExceptResult];
105 [self->vCards removeAllObjects];
108 /* document events */
110 - (void)startDocument {
113 if (self->vCards == nil)
114 self->vCards = [[NSMutableArray alloc] initWithCapacity:16];
116 if (self->tel == nil)
117 self->tel = [[NSMutableArray alloc] initWithCapacity:8];
118 if (self->adr == nil)
119 self->adr = [[NSMutableArray alloc] initWithCapacity:8];
120 if (self->email == nil)
121 self->email = [[NSMutableArray alloc] initWithCapacity:8];
122 if (self->label == nil)
123 self->label = [[NSMutableArray alloc] initWithCapacity:8];
125 if (self->url == nil)
126 self->url = [[NSMutableArray alloc] initWithCapacity:8];
127 if (self->fburl == nil)
128 self->fburl = [[NSMutableArray alloc] initWithCapacity:1];
129 if (self->caluri == nil)
130 self->caluri = [[NSMutableArray alloc] initWithCapacity:1];
132 if (self->types == nil)
133 self->types = [[NSMutableArray alloc] initWithCapacity:4];
134 if (self->args == nil)
135 self->args = [[NSMutableDictionary alloc] initWithCapacity:8];
137 if (self->subvalues == nil)
138 self->subvalues = [[NSMutableDictionary alloc] initWithCapacity:16];
139 if (self->xtags == nil)
140 self->xtags = [[NSMutableDictionary alloc] initWithCapacity:32];
142 - (void)endDocument {
143 [self resetExceptResult];
148 - (void)startValueTag:(NSString *)_tag attributes:(id<SaxAttributes>)_attrs {
149 /* a tag with types and attributes */
152 [self->types removeAllObjects];
153 [self->args removeAllObjects];
155 for (i = 0, count = [_attrs count]; i < count; i++) {
158 n = [_attrs nameAtIndex:i];
159 v = [_attrs valueAtIndex:i];
161 if ([n hasSuffix:@".type"] || [n isEqualToString:@"TYPE"]) {
162 if (![self->types containsObject:v])
163 [self->types addObjectsFromArray:[v componentsSeparatedByString:@" "]];
166 [self->args setObject:v forKey:n];
169 - (void)endValueTag {
170 [self->types removeAllObjects];
171 [self->args removeAllObjects];
174 /* handle elements */
176 - (void)startGroup:(NSString *)_name {
177 self->vcs.isInGroup = 1;
178 ASSIGNCOPY(self->currentGroup, _name);
181 self->vcs.isInGroup = 0;
182 [self->currentGroup release]; self->currentGroup = nil;
186 [self->subvalues removeAllObjects];
194 n = [[NGVCardName alloc] initWithPropertyList:self->subvalues
195 group:self->currentGroup
196 types:self->types arguments:self->args];
197 [self->vCard setN:n];
198 [self->subvalues removeAllObjects];
203 [self->subvalues removeAllObjects];
204 self->vcs.isInOrg = 1;
210 self->vcs.isInOrg = 0;
212 if ((u = [self->subvalues objectForKey:@"orgunit"]) != nil) {
213 if (![u isKindOfClass:[NSArray class]])
214 u = [NSArray arrayWithObjects:&u count:1];
217 // TODO: pass org values!
218 o = [[NGVCardOrg alloc] initWithName:[self->subvalues objectForKey:@"orgnam"]
220 group:self->currentGroup
221 types:self->types arguments:self->args];
222 [self->vCard setOrg:o];
224 [self->subvalues removeAllObjects];
228 [self->subvalues removeAllObjects];
229 self->vcs.isInGeo = 1;
234 self->vcs.isInGeo = 0;
236 [self logWithFormat:@"WARNING: not supporting geo in vCard."];
237 [self->subvalues removeAllObjects];
240 - (void)startVCard:(id<SaxAttributes>)_attrs {
241 NSString *uid, *version;
244 [self->tel removeAllObjects];
245 [self->adr removeAllObjects];
246 [self->email removeAllObjects];
247 [self->label removeAllObjects];
248 [self->url removeAllObjects];
249 [self->fburl removeAllObjects];
250 [self->caluri removeAllObjects];
251 [self->xtags removeAllObjects];
253 self->vcs.isInVCard = 1;
254 if (self->vCard != nil) {
255 [self->vCards addObject:self->vCard];
256 [self logWithFormat:@"ERROR: vCard nesting not supported!"];
257 [self->vCard release]; self->vCard = nil;
260 if ((uid = [_attrs valueForName:@"uid" uri:XMLNS_VCARD_XML_03]) == nil)
261 uid = [_attrs valueForName:@"X-ABUID" uri:XMLNS_VCARD_XML_03];
263 version = [_attrs valueForName:@"version" uri:XMLNS_VCARD_XML_03];
265 self->vCard = [[NGVCard alloc] initWithUid:uid version:version];
267 if ((t = [_attrs valueForName:@"class" uri:XMLNS_VCARD_XML_03]) != nil)
268 [self->vCard setVClass:t];
269 if ((t = [_attrs valueForName:@"rev" uri:XMLNS_VCARD_XML_03]) != nil) {
270 [self logWithFormat:@"WARNING: vCard revision not yet supported!"];
273 if ((t = [_attrs valueForName:@"prodid" uri:XMLNS_VCARD_XML_03]) != nil)
274 [self->vCard setProdID:t];
276 [self debugWithFormat:@"started vCard: %@", self->vCard];
279 self->vcs.isInVCard = 0;
281 /* fill collected objects */
283 if ([self->tel count] > 0) [self->vCard setTel:self->tel];
284 if ([self->adr count] > 0) [self->vCard setAdr:self->adr];
285 if ([self->email count] > 0) [self->vCard setEmail:self->email];
286 if ([self->label count] > 0) [self->vCard setLabel:self->label];
287 if ([self->url count] > 0) [self->vCard setUrl:self->url];
288 if ([self->fburl count] > 0) [self->vCard setFreeBusyURL:self->fburl];
289 if ([self->caluri count] > 0) [self->vCard setCalURI:self->caluri];
290 if ([self->xtags count] > 0) [self->vCard setX:self->xtags];
292 [self->vCards addObject:self->vCard];
293 //[self debugWithFormat:@"finished vCard: %@", self->vCard];
295 [self resetCardState];
298 - (void)startVCardSet:(id<SaxAttributes>)_attrs {
299 self->vcs.isInVCardSet = 1;
301 - (void)endVCardSet {
302 self->vcs.isInVCardSet = 0;
305 - (void)endBaseContentTagWithClass:(Class)_clazz andAddTo:(NSMutableArray*)_a {
306 NGVCardSimpleValue *v;
308 v = [[_clazz alloc] initWithValue:[self finishCollectingContent]
309 group:self->currentGroup
310 types:self->types arguments:self->args];
317 - (void)startTel:(id<SaxAttributes>)_attrs {
318 [self startValueTag:@"tel" attributes:_attrs];
319 [self startCollectingContent];
322 [self endBaseContentTagWithClass:[NGVCardPhone class] andAddTo:self->tel];
325 - (void)startAdr:(id<SaxAttributes>)_attrs {
326 [self->subvalues removeAllObjects];
328 self->vcs.isInAdr = 1;
329 [self startValueTag:@"adr" attributes:_attrs];
332 NGVCardAddress *address;
334 self->vcs.isInAdr = 0;
336 address = [[NGVCardAddress alloc] initWithPropertyList:self->subvalues
337 group:self->currentGroup
338 types:self->types arguments:self->args];
339 [self->adr addObject:address];
341 [self->subvalues removeAllObjects];
346 - (void)startEmail:(id<SaxAttributes>)_attrs {
347 [self startValueTag:@"email" attributes:_attrs];
348 [self startCollectingContent];
351 [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
352 andAddTo:self->email];
355 - (void)startLabel:(id<SaxAttributes>)_attrs {
356 [self startValueTag:@"LABEL" attributes:_attrs];
357 [self startCollectingContent];
360 [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
361 andAddTo:self->label];
364 - (void)startURL:(id<SaxAttributes>)_attrs {
365 [self startValueTag:@"url" attributes:_attrs];
366 [self startCollectingContent];
369 // TODO: use special URL class?
370 [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
374 /* tags with comma separated values */
376 - (void)startNickname:(id<SaxAttributes>)_attrs {
377 [self startValueTag:@"nickname" attributes:_attrs];
378 [self startCollectingContent];
380 - (void)endNickname {
381 NGVCardStrArrayValue *v;
385 a = [[self finishCollectingContent] componentsSeparatedByString:@","];
387 v = [[NGVCardStrArrayValue alloc] initWithArray:a
388 group:self->currentGroup
389 types:self->types arguments:self->args];
390 [self->vCard setNickname:v];
391 [v release]; v = nil;
396 - (void)startCategories:(id<SaxAttributes>)_attrs {
397 [self startValueTag:@"categories" attributes:_attrs];
398 [self startCollectingContent];
400 - (void)endCategories {
401 NGVCardStrArrayValue *v;
405 a = [[self finishCollectingContent] componentsSeparatedByString:@","];
407 v = [[NGVCardStrArrayValue alloc] initWithArray:a
408 group:self->currentGroup
409 types:self->types arguments:self->args];
410 [self->vCard setCategories:v];
411 [v release]; v = nil;
416 /* generic processing of tags with subtags */
418 - (void)startSubContentTag:(id<SaxAttributes>)_attrs {
419 if ([_attrs count] > 0)
420 [self logWithFormat:@"WARNING: loosing attrs of subtag: %@", _attrs];
422 [self startCollectingContent];
424 - (void)endSubContentTag:(NSString *)_key {
428 if ((s = [[self finishCollectingContent] copy]) == nil)
431 if ((o = [self->subvalues objectForKey:_key]) == nil) {
432 [self->subvalues setObject:s forKey:_key];
435 /* multivalue (eg 'org') */
436 if ([o isKindOfClass:[NSMutableArray class]]) {
442 a = [[NSMutableArray alloc] initWithCapacity:4];
445 [self->subvalues setObject:a forKey:_key];
446 [a release]; a = nil;
452 /* extended tags (X-) */
454 - (void)startX:(NSString *)_name attributes:(id<SaxAttributes>)_attrs {
455 [self startValueTag:_name attributes:_attrs];
456 [self startCollectingContent];
458 - (void)endX:(NSString *)_name {
459 NGVCardSimpleValue *v;
463 s = [self finishCollectingContent];
464 v = [[NGVCardSimpleValue alloc] initWithValue:s
465 group:self->currentGroup
466 types:self->types arguments:self->args];
468 if ((o = [self->xtags objectForKey:_name]) == nil)
469 [self->xtags setObject:v forKey:_name];
470 else if ([o isKindOfClass:[NSMutableArray class]])
475 a = [[NSMutableArray alloc] initWithCapacity:4];
478 [self->xtags setObject:a forKey:_name];
488 - (void)startFN:(id<SaxAttributes>)_attrs {
489 [self startValueTag:@"" attributes:_attrs];
490 [self startCollectingContent];
493 [self->vCard setFn:[self finishCollectingContent]];
497 - (void)startRole:(id<SaxAttributes>)_attrs {
498 [self startValueTag:@"" attributes:_attrs];
499 [self startCollectingContent];
502 [self->vCard setRole:[self finishCollectingContent]];
506 - (void)startTitle:(id<SaxAttributes>)_attrs {
507 [self startValueTag:@"" attributes:_attrs];
508 [self startCollectingContent];
511 [self->vCard setTitle:[self finishCollectingContent]];
515 - (void)startBDay:(id<SaxAttributes>)_attrs {
516 [self startValueTag:@"" attributes:_attrs];
517 [self startCollectingContent];
520 [self->vCard setBday:[self finishCollectingContent]];
524 - (void)startNote:(id<SaxAttributes>)_attrs {
525 [self startValueTag:@"" attributes:_attrs];
526 [self startCollectingContent];
529 [self->vCard setNote:[self finishCollectingContent]];
533 - (void)startCalURI:(id<SaxAttributes>)_attrs {
534 [self startValueTag:@"CALURI" attributes:_attrs];
535 [self startCollectingContent];
538 [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
539 andAddTo:self->caluri];
542 - (void)startFreeBusyURL:(id<SaxAttributes>)_attrs {
543 [self startValueTag:@"FBURL" attributes:_attrs];
544 [self startCollectingContent];
546 - (void)endFreeBusyURL {
547 [self endBaseContentTagWithClass:[NGVCardSimpleValue class]
548 andAddTo:self->fburl];
554 - (void)startElement:(NSString *)_localName
555 namespace:(NSString *)_ns
556 rawName:(NSString *)_rawName
557 attributes:(id<SaxAttributes>)_attrs
559 unichar c0 = [_localName characterAtIndex:0];
561 if (c0 == 'g' && [_localName isEqualToString:@"group"])
562 [self startGroup:[_attrs valueForName:@"name" uri:_ns]];
563 else if (c0 == 'n' && [_localName length] == 1)
565 else if (c0 == 'o' && [_localName isEqualToString:@"org"])
567 else if (c0 == 't' && [_localName isEqualToString:@"tel"])
568 [self startTel:_attrs];
569 else if (c0 == 'u' && [_localName isEqualToString:@"url"])
570 [self startURL:_attrs];
571 else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
572 [self startAdr:_attrs];
573 else if (c0 == 'e' && [_localName isEqualToString:@"email"])
574 [self startEmail:_attrs];
575 else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
576 [self startLabel:_attrs];
577 else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
578 [self startVCard:_attrs];
579 else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
580 [self startVCardSet:_attrs];
581 else if (c0 == 'n' && [_localName isEqualToString:@"nickname"])
582 [self startNickname:_attrs];
583 else if (c0 == 'c' && [_localName isEqualToString:@"categories"])
584 [self startCategories:_attrs];
585 else if (c0 == 'r' && [_localName isEqualToString:@"role"])
586 [self startRole:_attrs];
587 else if (c0 == 't' && [_localName isEqualToString:@"title"])
588 [self startTitle:_attrs];
589 else if (c0 == 'b' && [_localName isEqualToString:@"bday"])
590 [self startBDay:_attrs];
591 else if (c0 == 'n' && [_localName isEqualToString:@"note"])
592 [self startNote:_attrs];
593 else if (c0 == 'C' && [_localName isEqualToString:@"CALURI"])
594 [self startCalURI:_attrs];
595 else if (c0 == 'F' && [_localName isEqualToString:@"FBURL"])
596 [self startFreeBusyURL:_attrs];
597 else if (c0 == 'f' && [_localName isEqualToString:@"fn"])
598 [self startFN:_attrs];
599 else if (c0 == 'g' && [_localName isEqualToString:@"geo"])
602 if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr ||
604 [self startSubContentTag:_attrs];
606 [self startX:_localName attributes:_attrs];
608 NSLog(@"U: %@", _localName);
612 - (void)endElement:(NSString *)_localName
613 namespace:(NSString *)_ns
614 rawName:(NSString *)_rawName
616 unichar c0 = [_localName characterAtIndex:0];
618 if (c0 == 'g' && [_localName isEqualToString:@"group"])
620 else if (c0 == 'n' && [_localName isEqualToString:@"n"])
622 else if (c0 == 'o' && [_localName isEqualToString:@"org"])
624 else if (c0 == 't' && [_localName isEqualToString:@"tel"])
626 else if (c0 == 'u' && [_localName isEqualToString:@"url"])
628 else if (c0 == 'a' && [_localName isEqualToString:@"adr"])
630 else if (c0 == 'e' && [_localName isEqualToString:@"email"])
632 else if (c0 == 'L' && [_localName isEqualToString:@"LABEL"])
634 else if (c0 == 'v' && [_localName isEqualToString:@"vCard"])
636 else if (c0 == 'v' && [_localName isEqualToString:@"vCardSet"])
638 else if (c0 == 'n' && [_localName isEqualToString:@"nickname"])
640 else if (c0 == 'c' && [_localName isEqualToString:@"categories"])
641 [self endCategories];
642 else if (c0 == 'r' && [_localName isEqualToString:@"role"])
644 else if (c0 == 't' && [_localName isEqualToString:@"title"])
646 else if (c0 == 'b' && [_localName isEqualToString:@"bday"])
648 else if (c0 == 'n' && [_localName isEqualToString:@"note"])
650 else if (c0 == 'C' && [_localName isEqualToString:@"CALURI"])
652 else if (c0 == 'F' && [_localName isEqualToString:@"FBURL"])
653 [self endFreeBusyURL];
654 else if (c0 == 'f' && [_localName isEqualToString:@"fn"])
656 else if (c0 == 'g' && [_localName isEqualToString:@"geo"])
659 if (self->vcs.isInN || self->vcs.isInOrg || self->vcs.isInAdr ||
661 [self endSubContentTag:_localName];
663 [self endX:_localName];
669 - (void)startCollectingContent {
670 if (self->content != NULL) {
672 self->content = NULL;
674 self->vcs.collectContent = 1;
677 - (NSString *)finishCollectingContent {
680 self->vcs.collectContent = 0;
682 if (self->content == NULL)
685 if (self->contentLength == 0)
688 s = [NSString stringWithCharacters:self->content length:self->contentLength];
689 if (self->content != NULL) {
691 self->content = NULL;
696 - (void)characters:(unichar *)_chars length:(int)_len {
697 if (_len == 0 || _chars == NULL)
700 if (self->content == NULL) {
702 self->contentLength = _len;
703 self->content = calloc(_len + 1, sizeof(unichar));
704 memcpy(self->content, _chars, (_len * sizeof(unichar)));
707 /* increase content */
709 realloc(self->content, (self->contentLength + _len+2) * sizeof(unichar));
710 memcpy(&(self->content[self->contentLength]), _chars,
711 (_len * sizeof(unichar)));
712 self->contentLength += _len;
714 self->content[self->contentLength] = 0;
717 @end /* NGVCardSaxHandler */