2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "ICalSaxParser.h"
23 #include "NSString+ICal.h"
24 #include <SaxObjC/XMLNamespaces.h>
25 #include <SaxObjC/SaxAttributes.h>
28 #import <libical/ical.h>
34 OPEN: apply default attributes in conformance to xcal !
37 @interface ICalSaxParser(Privates)
41 - (void)logUnknownComponentKind:(icalcomponent *)_comp;
42 - (void)walkSubComponents:(icalcomponent *)_comp;
43 - (void)walkComponent:(icalcomponent *)_comp;
47 static BOOL debugOn = NO;
49 static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd,
50 unichar **targetStart, const unichar *targetEnd);
52 @implementation ICalSaxParser
54 static Class StrClass = Nil;
57 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
58 StrClass = [NSString class];
60 debugOn = [ud boolForKey:@"iCalSaxDriverDebugEnabled"];
64 [self->contentHandler release];
65 [self->errorHandler release];
66 [self->attrs release];
70 /* logging of unknown elements etc */
72 - (void)logUnknownComponentKind:(icalcomponent *)_comp {
73 NSLog(@"%s: cannot process component kind: '%s'",
76 ?icalenum_component_kind_to_string(icalcomponent_isa(_comp)):"null");
79 - (void)logUnknownPropertyKind:(icalproperty *)_p {
80 NSLog(@"%s: cannot process property kind: '%s'",
82 _p ? icalenum_property_kind_to_string(icalproperty_isa(_p)) : "null");
85 - (void)logUnknownParameterKind:(icalparameter *)_pa {
86 NSLog(@"%s: cannot process parameter kind: '%s'",
88 _pa ? icalparameter_kind_to_string(icalparameter_isa(_pa)): "null");
93 - (NSString *)stringForValue:(icalvalue *)_val
94 ofProperty:(icalproperty *)_p
95 inComponent:(icalcomponent *)_comp
97 const unsigned char *s;
100 if (_val == NULL) return nil;
102 if ((s = icalvalue_as_ical_string(_val)) == NULL) {
104 NSLog(@"%s: couldn't process an iCal value (component=%s,property=%s).",
106 icalenum_component_kind_to_string(icalcomponent_isa(_comp)),
107 icalenum_property_kind_to_string(icalproperty_isa(_p)));
111 if ((len = strlen(s)) == 0)
114 /* SHOULD USE UTF-8 ?? */
115 return [StrClass stringWithCString:s];
118 - (void)walkValue:(icalvalue *)_val
119 ofProperty:(icalproperty *)_p
120 inComponent:(icalcomponent *)_comp
122 const unsigned char *s;
126 if (_val == NULL) return;
128 /* generic handling for value: pass them as SAX characters ... */
130 if ((s = icalvalue_as_ical_string(_val)) == NULL) {
132 NSLog(@"%s: couldn't process an iCal value (component=%s,property=%s).",
134 icalenum_component_kind_to_string(icalcomponent_isa(_comp)),
135 icalenum_property_kind_to_string(icalproperty_isa(_p)));
139 if ((len = strlen(s)) == 0) {
142 [self->contentHandler characters:data length:0];
146 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
148 if (_UTF8ToUTF16((void *)&s, (void *)(s + len),
149 (void *)&ts, ts + (len * sizeof(unichar)))) {
151 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
152 __PRETTY_FUNCTION__, __LINE__);
155 [self->contentHandler characters:data length:((unsigned)(ts - data))];
160 - (NSString *)stringForValueParameter:(icalparameter *)_pa {
161 switch (icalparameter_get_value(_pa)) {
162 case ICAL_VALUE_BINARY: return @"binary";
163 case ICAL_VALUE_BOOLEAN: return @"boolean";
164 case ICAL_VALUE_DATE: return @"date";
165 case ICAL_VALUE_DURATION: return @"duration";
166 case ICAL_VALUE_FLOAT: return @"float";
167 case ICAL_VALUE_INTEGER: return @"integer";
168 case ICAL_VALUE_PERIOD: return @"period";
169 case ICAL_VALUE_RECUR: return @"recur";
170 case ICAL_VALUE_TEXT: return @"text";
171 case ICAL_VALUE_TIME: return @"time";
172 case ICAL_VALUE_URI: return @"uri";
173 case ICAL_VALUE_ERROR: return @"error";
174 case ICAL_VALUE_DATETIME: return @"datetime";
175 case ICAL_VALUE_UTCOFFSET: return @"utcoffset";
176 case ICAL_VALUE_CALADDRESS: return @"caladdress";
181 - (NSString *)stringForTzIdParameter:(icalparameter *)_pa {
184 if ((s = icalparameter_get_tzid(_pa)))
185 return [StrClass stringWithCString:s];
188 - (NSString *)stringForXParameter:(icalparameter *)_pa {
191 if ((s = icalparameter_get_x(_pa)))
192 return [StrClass stringWithCString:s];
195 - (NSString *)stringForCnParameter:(icalparameter *)_pa {
198 if ((s = icalparameter_get_cn(_pa)))
199 return [StrClass stringWithCString:s];
202 - (NSString *)stringForDirParameter:(icalparameter *)_pa {
205 if ((s = icalparameter_get_dir(_pa)))
206 return [StrClass stringWithCString:s];
209 - (NSString *)stringForRoleParameter:(icalparameter *)_pa {
210 switch (icalparameter_get_role(_pa)) {
211 case ICAL_ROLE_CHAIR:
213 case ICAL_ROLE_REQPARTICIPANT:
215 case ICAL_ROLE_OPTPARTICIPANT:
217 case ICAL_ROLE_NONPARTICIPANT:
227 - (NSString *)stringForPartStatParameter:(icalparameter *)_pa {
228 switch (icalparameter_get_partstat(_pa)) {
229 case ICAL_PARTSTAT_NEEDSACTION: return @"NEEDSACTION";
230 case ICAL_PARTSTAT_ACCEPTED: return @"ACCEPTED";
231 case ICAL_PARTSTAT_DECLINED: return @"DECLINED";
232 case ICAL_PARTSTAT_TENTATIVE: return @"TENTATIVE";
233 case ICAL_PARTSTAT_DELEGATED: return @"DELEGATED";
234 case ICAL_PARTSTAT_COMPLETED: return @"COMPLETED";
235 case ICAL_PARTSTAT_INPROCESS: return @"INPROCESS";
237 case ICAL_PARTSTAT_X:
238 case ICAL_PARTSTAT_NONE:
244 - (NSString *)stringForRsvpParameter:(icalparameter *)_pa {
245 switch (icalparameter_get_rsvp(_pa)) {
246 case ICAL_RSVP_TRUE: return @"true";
247 case ICAL_RSVP_FALSE: return @"false";
256 - (id<SaxAttributes>)attrsForPropertyParameters:(icalproperty *)_p {
258 2.7 Mapping Property Parameters to XML
260 The property parameters defined in the standard iCalendar format are
261 represented in the XML representation as an attribute on element
262 types. The following table specifies the attribute name
263 corresponding to each property parameter.
265 +----------------+----------------+-----------+-----------------+
266 | Property | Attribute | Attribute | Default |
267 | Parameter Name | Name | Type | Value |
268 +----------------+----------------+-----------+-----------------+
269 | ALTREP | altrep | ENTITY | IMPLIED |
270 | CN | cn | CDATA | Null String |
271 | CUTYPE | cutype | NMTOKEN | INDIVIDUAL |
272 | DELEGATED-FROM | delegated-from | CDATA | IMPLIED |
273 | DELEGATED-TO | delegated-to | CDATA | IMPLIED |
274 | DIR | dir | ENTITY | IMPLIED |
275 | ENCODING | Not Used | n/a | n/a |
276 | FMTTYPE | fmttype | CDATA | REQUIRED |
277 | FBTYPE | fbtype | NMTOKEN | BUSY |
278 | LANGUAGE | language | CDATA | IMPLIED |
279 | MEMBER | member | CDATA | IMPLIED |
280 | PARTSTAT | partstat | NMTOKEN | NEEDS-ACTION |
281 | RANGE | range | NMTOKEN | THISONLY |
282 | RELATED | related | NMTOKEN | START |
283 | RELTYPE | reltype | NMTOKEN | PARENT |
284 | ROLE | role | NMTOKEN | REQ-PARTICIPANT |
285 | RSVP | rsvp | NMTOKEN | FALSE |
286 | SENT-BY | sent-by | CDATA | IMPLIED |
287 | TZID | tzid | CDATA | IMPLIED |
288 | VALUE | value | NOTATION | See elements |
289 +----------------+----------------+-----------+-----------------+
291 The inline "ENCODING" property parameter is not needed in the XML
292 representation. Inline binary information is always included as
293 parsable character data, after first being encoded using the BASE64
294 encoding of [RFC 2045].
299 for (pa = icalproperty_get_first_parameter(_p, ICAL_ANY_PARAMETER);
301 pa = icalproperty_get_next_parameter(_p, ICAL_ANY_PARAMETER)) {
303 NSString *attrName = nil;
305 switch (icalparameter_isa(pa)) {
306 case ICAL_VALUE_PARAMETER:
308 v = [self stringForValueParameter:pa];
311 case ICAL_TZID_PARAMETER:
313 v = [self stringForTzIdParameter:pa];
316 case ICAL_X_PARAMETER:
317 attrName = [StrClass stringWithFormat:@"x-unknown-%i", s++];
318 v = [self stringForXParameter:pa];
320 case ICAL_CN_PARAMETER:
322 v = [self stringForCnParameter:pa];
324 case ICAL_DIR_PARAMETER:
326 v = [self stringForDirParameter:pa];
328 case ICAL_RSVP_PARAMETER:
330 v = [self stringForRsvpParameter:pa];
332 case ICAL_PARTSTAT_PARAMETER:
333 attrName = @"partstat";
334 v = [self stringForPartStatParameter:pa];
336 case ICAL_ROLE_PARAMETER:
338 v = [self stringForRoleParameter:pa];
342 [self logUnknownParameterKind:pa];
346 if (attrName == nil) continue;
347 if (v == nil) continue;
349 if (self->attrs == nil)
350 self->attrs = [[SaxAttributes alloc] init];
353 addAttribute:attrName uri:XMLNS_XCAL_01 rawName:attrName
361 - (void)handleErrorProperty:(icalproperty *)_p
362 ofComponent:(icalcomponent *)_comp
364 switch (icalproperty_isa(_p)) {
365 case ICAL_XLICERROR_PROPERTY:
366 NSLog(@"iCalSaxDriver: found an error property: %s",
367 icalproperty_get_xlicerror(_p));
371 NSLog(@"iCalSaxDriver: found an unknown error property !");
376 - (void)walkProperty:(icalproperty *)_p ofComponent:(icalcomponent *)_comp {
378 2.9 Mapping Component Properties to XML
380 Component properties in the standard iCalendar format provide
381 calendar information about the calendar component. The component
382 properties defined in the standard iCalendar format are represented
383 in the XML representation as element types. The following tables
384 specify the element types corresponding to each of the properties in
385 the specified property category.
387 Descriptive Component Properties
388 +----------------+-------------+-----------------------------+
389 | Component | Element | Element Content Model |
390 | Property Name | Name | |
391 +----------------+-------------+-----------------------------+
392 | ATTACH | attach | extref or b64bin |
394 | | b64bin | PCDATA |
395 | CATEGORIES | categories | Any number of item elements |
397 | CLASS | class | PCDATA |
398 | COMMENT | comment | PCDATA |
399 | DESCRIPTION | description | PCDATA |
400 | GEO | geo | lat followed by lon element |
403 | LOCATION | location | PCDATA |
404 | PERCENT | percent | PCDATA |
405 | PRIORITY | priority | PCDATA |
406 | RESOURCES | resources | Any number of item elements |
407 | STATUS | status | PCDATA |
408 | SUMMARY | summary | PCDATA |
409 +----------------+-------------+-----------------------------+
410 Date and Time Component Properties
411 +----------------+------------+-----------------------------+
412 | Component | Element | Element Content Model |
413 | Property Name | Name | |
414 +----------------+------------+-----------------------------+
415 | COMPLETED | completed | PCDATA |
416 | DTEND | dtend | PCDATA |
417 | DUE | due | PCDATA |
418 | DTSTART | dtstart | PCDATA |
419 | DURATION | duration | PCDATA |
420 | FREEBUSY | freebusy | PCDATA |
421 | TRANSP | transp | PCDATA |
422 +----------------+------------+-----------------------------+
425 Time Zone Component Properties
426 +----------------+-------------+-----------------------------+
427 | Component | Element | Element Content Model |
428 | Property Name | Name | |
429 +----------------+-------------+-----------------------------+
430 | TZID | tzid | PCDATA |
431 | TZNAME | tzname | PCDATA |
432 | TZOFFSETFROM | tzoffsetfrom| PCDATA |
433 | TZOFFSETTO | tzoffsetto | PCDATA |
434 | TZURL | tzurl | EMPTY |
435 +----------------+-------------+-----------------------------+
438 Relationship Component Properties
439 +----------------+---------------+--------------------------+
440 | Component | Element | Element Content Model |
441 | Property Name | Name | |
442 +----------------+---------------+--------------------------+
443 | ATTENDEE | attendee | PCDATA |
444 | CONTACT | contact | PCDATA |
445 | ORGANIZER | organizer | PCDATA |
446 | RECURRENCE-ID | recurrence-id | PCDATA |
447 | RELATED-TO | related-to | PCDATA |
448 | URL | url | EMPTY |
449 | UID | uid | PCDATA |
450 +----------------+---------------+--------------------------+
453 Recurrence Component Properties
454 +----------------+------------+-----------------------------+
455 | Component | Element | Element Content Model |
456 | Property Name | Name | |
457 +----------------+------------+-----------------------------+
458 | EXDATE | exdate | PCDATA |
459 | EXRULE | exrule | PCDATA |
460 | RDATE | rdate | PCDATA |
461 | RRULE | rrule | PCDATA |
462 +----------------+------------+-----------------------------+
465 Alarm Component Properties
466 +----------------+------------+-----------------------------+
467 | Component | Element | Element Content Model |
468 | Property Name | Name | |
469 +----------------+------------+-----------------------------+
470 | ACTION | action | PCDATA |
471 | REPEAT | repeat | PCDATA |
472 | TRIGGER | trigger | PCDATA |
473 +----------------+------------+-----------------------------+
476 Change Management Component Properties
477 +----------------+---------------+--------------------------+
478 | Component | Element | Element Content Model |
479 | Property Name | Name | |
480 +----------------+---------------+--------------------------+
481 | CREATED | created | PCDATA |
482 | DTSTAMP | dtstamp | PCDATA |
483 | LAST-MODIFIED | last-modified | PCDATA |
484 | SEQUENCE | sequence | PCDATA |
485 +----------------+---------------+--------------------------+
488 Miscellaneous Component Properties
489 +----------------+----------------+-------------------------+
490 | Component | Element | Element Content Model |
491 | Property Name | Name | |
492 +----------------+----------------+-------------------------+
493 | REQUEST-STATUS | request-status | PCDATA |
494 +----------------+----------------+-------------------------+
496 NSString *tagName = nil;
498 if (_p == NULL) return;
500 switch (icalproperty_isa(_p)) {
501 case ICAL_ACTION_PROPERTY: tagName = @"action"; break;
502 case ICAL_ATTENDEE_PROPERTY: tagName = @"attendee"; break;
503 case ICAL_CLASS_PROPERTY: tagName = @"class"; break;
504 case ICAL_COMMENT_PROPERTY: tagName = @"comment"; break;
505 case ICAL_COMPLETED_PROPERTY: tagName = @"completed"; break;
506 case ICAL_CONTACT_PROPERTY: tagName = @"contact"; break;
507 case ICAL_CREATED_PROPERTY: tagName = @"created"; break;
508 case ICAL_DESCRIPTION_PROPERTY: tagName = @"description"; break;
509 case ICAL_DTEND_PROPERTY: tagName = @"dtend"; break;
510 case ICAL_DTSTAMP_PROPERTY: tagName = @"dtstamp"; break;
511 case ICAL_DTSTART_PROPERTY: tagName = @"dtstart"; break;
512 case ICAL_DUE_PROPERTY: tagName = @"due"; break;
513 case ICAL_DURATION_PROPERTY: tagName = @"duration"; break;
514 case ICAL_FREEBUSY_PROPERTY: tagName = @"freebusy"; break;
515 case ICAL_EXDATE_PROPERTY: tagName = @"exdate"; break;
516 case ICAL_EXRULE_PROPERTY: tagName = @"exrule"; break;
517 case ICAL_RDATE_PROPERTY: tagName = @"rdate"; break;
518 case ICAL_RRULE_PROPERTY: tagName = @"rrule"; break;
519 case ICAL_LASTMODIFIED_PROPERTY: tagName = @"last-modified"; break;
520 case ICAL_LOCATION_PROPERTY: tagName = @"location"; break;
521 case ICAL_ORGANIZER_PROPERTY: tagName = @"organizer"; break;
522 case ICAL_PRIORITY_PROPERTY: tagName = @"priority"; break;
523 case ICAL_REPEAT_PROPERTY: tagName = @"repeat"; break;
524 case ICAL_RELATEDTO_PROPERTY: tagName = @"related-to"; break;
525 case ICAL_RECURRENCEID_PROPERTY: tagName = @"recurrence-id"; break;
526 case ICAL_REQUESTSTATUS_PROPERTY: tagName = @"request-status"; break;
527 case ICAL_SEQUENCE_PROPERTY: tagName = @"sequence"; break;
528 case ICAL_STATUS_PROPERTY: tagName = @"status"; break;
529 case ICAL_SUMMARY_PROPERTY: tagName = @"summary"; break;
530 case ICAL_TRANSP_PROPERTY: tagName = @"transp"; break;
531 case ICAL_TRIGGER_PROPERTY: tagName = @"trigger"; break;
532 case ICAL_TZID_PROPERTY: tagName = @"tzid"; break;
533 case ICAL_TZNAME_PROPERTY: tagName = @"tzname"; break;
534 case ICAL_TZOFFSETFROM_PROPERTY: tagName = @"tzoffsetfrom"; break;
535 case ICAL_TZOFFSETTO_PROPERTY: tagName = @"tzoffsetto"; break;
536 case ICAL_TZURL_PROPERTY: tagName = @"tzurl"; break;
537 case ICAL_UID_PROPERTY: tagName = @"uid"; break;
538 case ICAL_URL_PROPERTY: tagName = @"url"; break;
539 case ICAL_PERCENTCOMPLETE_PROPERTY:
540 tagName = @"percent-complete";
543 /* special handling in xcal !! check ! */
545 case ICAL_GEO_PROPERTY:
546 tagName = @"geo"; /* lat followed by lon element */
551 case ICAL_RESOURCES_PROPERTY:
552 /* content model: Any number of item elements */
553 tagName = @"resources";
555 case ICAL_CATEGORIES_PROPERTY:
556 /* content model: Any number of item elements */
557 tagName = @"categories";
560 case ICAL_ATTACH_PROPERTY:
561 tagName = @"attach"; /* content model: extref || b64bin */
562 // tagName = @"extref"; /* content model: empty */
563 // tagName = @"b64bin"; /* content model: pcdata */
566 /* attribute properties */
567 case ICAL_PRODID_PROPERTY:
568 case ICAL_METHOD_PROPERTY:
569 case ICAL_VERSION_PROPERTY:
570 case ICAL_CALSCALE_PROPERTY:
573 /* extension properties */
574 case ICAL_XLICERROR_PROPERTY:
575 [self handleErrorProperty:_p ofComponent:_comp];
578 case ICAL_X_PROPERTY:
579 tagName = [StrClass stringWithCString:icalproperty_get_x_name(_p)];
583 [self logUnknownPropertyKind:_p];
588 [self->contentHandler
590 namespace:XMLNS_XCAL_01
592 attributes:[self attrsForPropertyParameters:_p]];
595 switch (icalproperty_isa(_p)) {
596 case ICAL_RRULE_PROPERTY: {
597 /* this should unparse the rrule parameters as-is */
598 NSLog(@"iCalSaxDriver: recurrence rules are not properly handled yet");
603 /* generic handling for value: pass them as SAX characters ... */
604 [self walkValue:icalproperty_get_value(_p)
605 ofProperty:_p inComponent:_comp];
608 [self->contentHandler
610 namespace:XMLNS_XCAL_01
614 - (id<SaxAttributes>)attrPropertiesOfComponent:(icalcomponent *)_comp {
617 for (p = icalcomponent_get_first_property(_comp,ICAL_ANY_PROPERTY);
619 p = icalcomponent_get_next_property(_comp,ICAL_ANY_PROPERTY)) {
621 NSString *attrName = nil;
623 switch (icalproperty_isa(p)) {
624 case ICAL_PRODID_PROPERTY: attrName = @"prodid"; break;
625 case ICAL_METHOD_PROPERTY: attrName = @"method"; break;
626 case ICAL_VERSION_PROPERTY: attrName = @"version"; break;
627 case ICAL_CALSCALE_PROPERTY: attrName = @"calscale"; break;
632 if (attrName == nil) continue;
634 v = [self stringForValue:icalproperty_get_value(p)
638 if (v == nil) continue;
640 if (self->attrs == nil)
641 self->attrs = [[SaxAttributes alloc] init];
644 addAttribute:attrName uri:XMLNS_XCAL_01 rawName:attrName
651 - (void)walkPropertiesOfComponent:(icalcomponent *)_comp {
654 for (p = icalcomponent_get_first_property(_comp,ICAL_ANY_PROPERTY);
656 p = icalcomponent_get_next_property(_comp,ICAL_ANY_PROPERTY)) {
657 [self walkProperty:p ofComponent:_comp];
661 - (void)walkSubComponents:(icalcomponent *)_comp {
664 for(c = icalcomponent_get_first_component(_comp, ICAL_ANY_COMPONENT);
666 c = icalcomponent_get_next_component(_comp, ICAL_ANY_COMPONENT)) {
667 [self walkComponent:c];
671 - (void)walkComponent:(icalcomponent *)_comp {
673 Component Structuring Properties
674 +----------------+------------+-------------------------------+
675 | Component | Element | Element Content Model |
676 | Property Name | Name | |
677 +----------------+------------+-------------------------------+
678 | Multiple iCal- | iCalendar | One or more iCal elements |
679 | endar objects | | |
680 | VCALENDAR | vcalendar | calcomp parameter entity |
681 | VEVENT | vevent | vevent.opt1 and vevent.optm |
682 | | | parameter entity and valarm |
684 | VTODO | vtodo | vtodo.opt1 and vtodo.optm |
685 | | | parameter entity and valarm |
687 | VJOURNAL | vjournal | vjournal.opt1 and |
688 | | | vjournal.optm parameter |
690 | VFREEBUSY | vfreebusy | vfreebusy.opt1 and |
691 | | | vfreebusy.optm parameter |
693 | VTIMEZONE | vtimezone | vtimezone.man, |
694 | | | vtimezone.opt1, |
695 | | | vtimezone.mann parameter |
697 | STANDARD | standard | standard.man or standard.optm |
699 | DAYLIGHT | daylight | daylight.man or daylight.optm |
701 | VALARM | valarm | valarm.audio, valarm.display, |
702 | | | valarm.email and |
703 | | | valarm.procedure entity |
704 +----------------+------------+-------------------------------+
706 NSString *tagName = nil;
708 switch (icalcomponent_isa(_comp)) {
709 case ICAL_VCALENDAR_COMPONENT:
710 tagName = @"vcalendar";
712 case ICAL_VFREEBUSY_COMPONENT:
713 tagName = @"vfreebusy";
715 case ICAL_VEVENT_COMPONENT:
718 case ICAL_VTODO_COMPONENT:
721 case ICAL_VJOURNAL_COMPONENT:
722 tagName = @"vjournal";
724 case ICAL_VTIMEZONE_COMPONENT:
725 tagName = @"vtimezone";
727 case ICAL_VALARM_COMPONENT:
731 case ICAL_XSTANDARD_COMPONENT:
732 tagName = @"standard"; // in xcal as "standard" component ?
734 case ICAL_XDAYLIGHT_COMPONENT:
735 tagName = @"daylight"; // in xcal as "daylight" component ?
738 case ICAL_XROOT_COMPONENT:
739 /* root component for a file with multiple components */
740 tagName = @"iCalendar";
744 [self logUnknownComponentKind:_comp];
750 calscale - cdata - implied
751 method - nmtoken - publish
752 version - cdata - required
753 prodid - cdata - implied
755 vcalendar: language - cdata - implied
759 [self->contentHandler
761 namespace:XMLNS_XCAL_01
763 attributes:[self attrPropertiesOfComponent:_comp]];
766 [self walkPropertiesOfComponent:_comp];
768 [self walkSubComponents:_comp];
770 [self->contentHandler
772 namespace:XMLNS_XCAL_01
776 - (void)walkRootComponent:(icalcomponent *)_comp {
777 [self->contentHandler startDocument];
778 [self->contentHandler startPrefixMapping:@"" uri:XMLNS_XCAL_01];
780 [self walkComponent:_comp];
782 [self->contentHandler endPrefixMapping:@""];
783 [self->contentHandler endDocument];
788 - (void)_reportICalErrno:(icalerrorenum)_errcode {
791 /* well, no error ... */
794 case ICAL_BADARG_ERROR:
795 case ICAL_NEWFAILED_ERROR:
796 case ICAL_ALLOCATION_ERROR:
797 case ICAL_MALFORMEDDATA_ERROR:
798 case ICAL_PARSE_ERROR:
799 case ICAL_INTERNAL_ERROR:
800 /* Like assert --internal consist. prob */
801 case ICAL_FILE_ERROR:
802 case ICAL_USAGE_ERROR:
803 case ICAL_UNIMPLEMENTED_ERROR:
804 case ICAL_UNKNOWN_ERROR:
805 /* Used for problems in input to icalerror_strerror()*/
808 SaxParseException *e;
812 errstr = icalerror_strerror(_errcode);
814 NSLog(@"%s: generic ical parsing error(code=%i): %s",
815 __PRETTY_FUNCTION__, _errcode, errstr);
817 s = [[StrClass alloc] initWithFormat:@"generic libical error %i: %s",
819 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
823 [self->errorHandler fatalError:e];
829 - (BOOL)parseString:(NSString *)_string {
834 NSLog(@"%s: parse string (len=%i)", __PRETTY_FUNCTION__,
837 if (_string == nil) {
838 if (debugOn) NSLog(@"%s: got no string ...", __PRETTY_FUNCTION__);
841 if ((str = [_string icalCString]) == NULL) {
842 if (debugOn) NSLog(@"%s: got no icalCString ...", __PRETTY_FUNCTION__);
846 // printf("STR: '%s'(%i)\n", str, strlen(str));
848 if ((c = icalparser_parse_string(str)) == NULL) {
849 /* parsing failed ... */
851 NSLog(@"%s: libical parsing failed ...", __PRETTY_FUNCTION__);
852 [self _reportICalErrno:icalerrno];
856 [self walkRootComponent:c];
860 - (BOOL)parseData:(NSData *)_data {
865 if (_data == nil) return NO;
867 len = [_data length];
868 str = malloc(len + 10);
869 [_data getBytes:str length:len];
872 if ((c = icalparser_parse_string(str)) == NULL)
873 /* parsing failed ... */
876 [self walkRootComponent:c];
881 - (void)parseFileAtPath:(NSString *)_path encoding:(NSStringEncoding)_enc {
882 NSAutoreleasePool *pool;
887 NSLog(@"%s: parse file: %@", __PRETTY_FUNCTION__, _path);
889 pool = [[NSAutoreleasePool alloc] init];
891 data = [[NSData alloc] initWithContentsOfMappedFile:_path];
893 /* throw Sax Exception ! */
895 NSLog(@"%s: could not map data: %@", __PRETTY_FUNCTION__, _path);
899 s = [[[StrClass alloc] initWithData:data encoding:_enc] autorelease];
902 [self parseString:s];
906 - (void)parseFileAtPath:(NSString *)_path {
907 [self parseFileAtPath:_path encoding:NSUTF8StringEncoding];
912 /* features & properties */
914 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
916 - (BOOL)feature:(NSString *)_name {
919 - (void)setProperty:(NSString *)_name to:(id)_value {
921 - (id)property:(NSString *)_name {
927 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
928 ASSIGN(self->contentHandler, _handler);
930 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
932 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
933 ASSIGN(self->errorHandler, _handler);
935 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
937 - (id<NSObject,SaxContentHandler>)contentHandler {
938 return self->contentHandler;
940 - (id<NSObject,SaxDTDHandler>)dtdHandler {
943 - (id<NSObject,SaxErrorHandler>)errorHandler {
944 return self->errorHandler;
946 - (id<NSObject,SaxEntityResolver>)entityResolver {
952 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
954 NSLog(@"%s: parse(%@): %@", __PRETTY_FUNCTION__, _sysId, _source);
956 if (_source == nil) {
958 if (debugOn) NSLog(@"%s: not source: %@)", __PRETTY_FUNCTION__, _sysId);
962 if ([_source isKindOfClass:StrClass]) {
963 /* convert strings to UTF8 data */
964 if (_sysId == nil) _sysId = @"<string>";
966 NSLog(@"%s: parse string (len=%i))", __PRETTY_FUNCTION__,
969 [self parseString:_source];
973 if ([_source isKindOfClass:[NSURL class]]) {
974 if (_sysId == nil) _sysId = [_source absoluteString];
975 if (debugOn) NSLog(@"%s: load URL: %@", __PRETTY_FUNCTION__, _source);
976 _source = [_source resourceDataUsingCache:NO];
978 else if ([_source isKindOfClass:[NSData class]]) {
979 if (_sysId == nil) _sysId = @"<data>";
982 SaxParseException *e;
986 NSLog(@"%s: unknown source class: %@", __PRETTY_FUNCTION__,
987 NSStringFromClass([_source class]));
990 ui = [NSDictionary dictionaryWithObjectsAndKeys:
991 _source ? _source : @"<nil>", @"source",
995 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
996 reason:@"cannot handle datasource"
999 [self->errorHandler fatalError:e];
1004 NSLog(@"%s: parse data (len=%i))", __PRETTY_FUNCTION__,
1007 [self parseData:_source];
1010 - (void)parseFromSource:(id)_source {
1011 [self parseFromSource:_source systemId:nil];
1014 - (void)parseFromSystemId:(NSString *)_sysId {
1016 NSLog(@"%s: parse ID: %@", __PRETTY_FUNCTION__, _sysId);
1018 if (![_sysId hasPrefix:@"file:"]) {
1019 SaxParseException *e;
1023 if ((url = [NSURL URLWithString:_sysId])) {
1024 [self parseFromSource:url systemId:_sysId];
1029 NSLog(@"%s: could not parse ID: %@", __PRETTY_FUNCTION__, _sysId);
1031 ui = [NSDictionary dictionaryWithObjectsAndKeys:
1032 _sysId ? _sysId : @"<nil>", @"systemID",
1036 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1037 reason:@"cannot handle system-id"
1040 [self->errorHandler fatalError:e];
1044 /* cut off file:// */
1045 if ([_sysId hasPrefix:@"file://"])
1046 _sysId = [_sysId substringFromIndex:7];
1048 _sysId = [_sysId substringFromIndex:5];
1051 NSLog(@"%s: parse file: %@", __PRETTY_FUNCTION__, _sysId);
1052 [self parseFileAtPath:_sysId];
1055 @end /* ICalSaxParser */
1057 @interface iCalSaxDriver : ICalSaxParser
1060 @implementation iCalSaxDriver
1063 #include "unicode.h"