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>
30 OPEN: apply default attributes in conformance to xcal !
33 @interface ICalSaxParser(Privates)
37 - (void)logUnknownComponentKind:(icalcomponent *)_comp;
38 - (void)walkSubComponents:(icalcomponent *)_comp;
39 - (void)walkComponent:(icalcomponent *)_comp;
43 static BOOL debugOn = NO;
45 static int _UTF8ToUTF16(unsigned char **sourceStart, unsigned char *sourceEnd,
46 unichar **targetStart, const unichar *targetEnd);
48 @implementation ICalSaxParser
50 static Class StrClass = Nil;
53 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
54 StrClass = [NSString class];
56 debugOn = [ud boolForKey:@"iCalSaxDriverDebugEnabled"];
60 [self->contentHandler release];
61 [self->errorHandler release];
62 [self->attrs release];
66 /* logging of unknown elements etc */
68 - (void)logUnknownComponentKind:(icalcomponent *)_comp {
69 NSLog(@"%s: cannot process component kind: '%s'",
72 ?icalenum_component_kind_to_string(icalcomponent_isa(_comp)):"null");
75 - (void)logUnknownPropertyKind:(icalproperty *)_p {
76 NSLog(@"%s: cannot process property kind: '%s'",
78 _p ? icalenum_property_kind_to_string(icalproperty_isa(_p)) : "null");
81 - (void)logUnknownParameterKind:(icalparameter *)_pa {
82 NSLog(@"%s: cannot process parameter kind: '%s'",
84 _pa ? icalparameter_kind_to_string(icalparameter_isa(_pa)): "null");
89 - (NSString *)stringForValue:(icalvalue *)_val
90 ofProperty:(icalproperty *)_p
91 inComponent:(icalcomponent *)_comp
93 const unsigned char *s;
96 if (_val == NULL) return nil;
98 if ((s = icalvalue_as_ical_string(_val)) == NULL) {
100 NSLog(@"%s: couldn't process an iCal value (component=%s,property=%s).",
102 icalenum_component_kind_to_string(icalcomponent_isa(_comp)),
103 icalenum_property_kind_to_string(icalproperty_isa(_p)));
107 if ((len = strlen(s)) == 0)
110 /* SHOULD USE UTF-8 ?? */
111 return [StrClass stringWithCString:s];
114 - (void)walkValue:(icalvalue *)_val
115 ofProperty:(icalproperty *)_p
116 inComponent:(icalcomponent *)_comp
118 const unsigned char *s;
122 if (_val == NULL) return;
124 /* generic handling for value: pass them as SAX characters ... */
126 if ((s = icalvalue_as_ical_string(_val)) == NULL) {
128 NSLog(@"%s: couldn't process an iCal value (component=%s,property=%s).",
130 icalenum_component_kind_to_string(icalcomponent_isa(_comp)),
131 icalenum_property_kind_to_string(icalproperty_isa(_p)));
135 if ((len = strlen(s)) == 0) {
138 [self->contentHandler characters:data length:0];
142 data = ts = calloc(len + 1, sizeof(unichar)); /* GC ?! */
144 if (_UTF8ToUTF16((void *)&s, (void *)(s + len),
145 (void *)&ts, ts + (len * sizeof(unichar)))) {
147 NSLog(@"ERROR(%s:%i): couldn't convert UTF8 to UTF16 !",
148 __PRETTY_FUNCTION__, __LINE__);
151 [self->contentHandler characters:data length:((unsigned)(ts - data))];
156 - (NSString *)stringForValueParameter:(icalparameter *)_pa {
157 switch (icalparameter_get_value(_pa)) {
158 case ICAL_VALUE_BINARY: return @"binary";
159 case ICAL_VALUE_BOOLEAN: return @"boolean";
160 case ICAL_VALUE_DATE: return @"date";
161 case ICAL_VALUE_DURATION: return @"duration";
162 case ICAL_VALUE_FLOAT: return @"float";
163 case ICAL_VALUE_INTEGER: return @"integer";
164 case ICAL_VALUE_PERIOD: return @"period";
165 case ICAL_VALUE_RECUR: return @"recur";
166 case ICAL_VALUE_TEXT: return @"text";
167 case ICAL_VALUE_TIME: return @"time";
168 case ICAL_VALUE_URI: return @"uri";
169 case ICAL_VALUE_ERROR: return @"error";
170 case ICAL_VALUE_DATETIME: return @"datetime";
171 case ICAL_VALUE_UTCOFFSET: return @"utcoffset";
172 case ICAL_VALUE_CALADDRESS: return @"caladdress";
177 - (NSString *)stringForTzIdParameter:(icalparameter *)_pa {
180 if ((s = icalparameter_get_tzid(_pa)))
181 return [StrClass stringWithCString:s];
184 - (NSString *)stringForXParameter:(icalparameter *)_pa {
187 if ((s = icalparameter_get_x(_pa)))
188 return [StrClass stringWithCString:s];
191 - (NSString *)stringForCnParameter:(icalparameter *)_pa {
194 if ((s = icalparameter_get_cn(_pa)))
195 return [StrClass stringWithCString:s];
198 - (NSString *)stringForDirParameter:(icalparameter *)_pa {
201 if ((s = icalparameter_get_dir(_pa)))
202 return [StrClass stringWithCString:s];
205 - (NSString *)stringForRoleParameter:(icalparameter *)_pa {
206 switch (icalparameter_get_role(_pa)) {
207 case ICAL_ROLE_CHAIR:
209 case ICAL_ROLE_REQPARTICIPANT:
211 case ICAL_ROLE_OPTPARTICIPANT:
213 case ICAL_ROLE_NONPARTICIPANT:
223 - (NSString *)stringForPartStatParameter:(icalparameter *)_pa {
224 switch (icalparameter_get_partstat(_pa)) {
225 case ICAL_PARTSTAT_NEEDSACTION: return @"NEEDSACTION";
226 case ICAL_PARTSTAT_ACCEPTED: return @"ACCEPTED";
227 case ICAL_PARTSTAT_DECLINED: return @"DECLINED";
228 case ICAL_PARTSTAT_TENTATIVE: return @"TENTATIVE";
229 case ICAL_PARTSTAT_DELEGATED: return @"DELEGATED";
230 case ICAL_PARTSTAT_COMPLETED: return @"COMPLETED";
231 case ICAL_PARTSTAT_INPROCESS: return @"INPROCESS";
233 case ICAL_PARTSTAT_X:
234 case ICAL_PARTSTAT_NONE:
240 - (NSString *)stringForRsvpParameter:(icalparameter *)_pa {
241 switch (icalparameter_get_rsvp(_pa)) {
242 case ICAL_RSVP_TRUE: return @"true";
243 case ICAL_RSVP_FALSE: return @"false";
252 - (id<SaxAttributes>)attrsForPropertyParameters:(icalproperty *)_p {
254 2.7 Mapping Property Parameters to XML
256 The property parameters defined in the standard iCalendar format are
257 represented in the XML representation as an attribute on element
258 types. The following table specifies the attribute name
259 corresponding to each property parameter.
261 +----------------+----------------+-----------+-----------------+
262 | Property | Attribute | Attribute | Default |
263 | Parameter Name | Name | Type | Value |
264 +----------------+----------------+-----------+-----------------+
265 | ALTREP | altrep | ENTITY | IMPLIED |
266 | CN | cn | CDATA | Null String |
267 | CUTYPE | cutype | NMTOKEN | INDIVIDUAL |
268 | DELEGATED-FROM | delegated-from | CDATA | IMPLIED |
269 | DELEGATED-TO | delegated-to | CDATA | IMPLIED |
270 | DIR | dir | ENTITY | IMPLIED |
271 | ENCODING | Not Used | n/a | n/a |
272 | FMTTYPE | fmttype | CDATA | REQUIRED |
273 | FBTYPE | fbtype | NMTOKEN | BUSY |
274 | LANGUAGE | language | CDATA | IMPLIED |
275 | MEMBER | member | CDATA | IMPLIED |
276 | PARTSTAT | partstat | NMTOKEN | NEEDS-ACTION |
277 | RANGE | range | NMTOKEN | THISONLY |
278 | RELATED | related | NMTOKEN | START |
279 | RELTYPE | reltype | NMTOKEN | PARENT |
280 | ROLE | role | NMTOKEN | REQ-PARTICIPANT |
281 | RSVP | rsvp | NMTOKEN | FALSE |
282 | SENT-BY | sent-by | CDATA | IMPLIED |
283 | TZID | tzid | CDATA | IMPLIED |
284 | VALUE | value | NOTATION | See elements |
285 +----------------+----------------+-----------+-----------------+
287 The inline "ENCODING" property parameter is not needed in the XML
288 representation. Inline binary information is always included as
289 parsable character data, after first being encoded using the BASE64
290 encoding of [RFC 2045].
295 for (pa = icalproperty_get_first_parameter(_p, ICAL_ANY_PARAMETER);
297 pa = icalproperty_get_next_parameter(_p, ICAL_ANY_PARAMETER)) {
299 NSString *attrName = nil;
301 switch (icalparameter_isa(pa)) {
302 case ICAL_VALUE_PARAMETER:
304 v = [self stringForValueParameter:pa];
307 case ICAL_TZID_PARAMETER:
309 v = [self stringForTzIdParameter:pa];
312 case ICAL_X_PARAMETER:
313 attrName = [StrClass stringWithFormat:@"x-unknown-%i", s++];
314 v = [self stringForXParameter:pa];
316 case ICAL_CN_PARAMETER:
318 v = [self stringForCnParameter:pa];
320 case ICAL_DIR_PARAMETER:
322 v = [self stringForDirParameter:pa];
324 case ICAL_RSVP_PARAMETER:
326 v = [self stringForRsvpParameter:pa];
328 case ICAL_PARTSTAT_PARAMETER:
329 attrName = @"partstat";
330 v = [self stringForPartStatParameter:pa];
332 case ICAL_ROLE_PARAMETER:
334 v = [self stringForRoleParameter:pa];
338 [self logUnknownParameterKind:pa];
342 if (attrName == nil) continue;
343 if (v == nil) continue;
345 if (self->attrs == nil)
346 self->attrs = [[SaxAttributes alloc] init];
349 addAttribute:attrName uri:XMLNS_XCAL_01 rawName:attrName
357 - (void)handleErrorProperty:(icalproperty *)_p
358 ofComponent:(icalcomponent *)_comp
360 switch (icalproperty_isa(_p)) {
361 case ICAL_XLICERROR_PROPERTY:
362 NSLog(@"iCalSaxDriver: found an error property: %s",
363 icalproperty_get_xlicerror(_p));
367 NSLog(@"iCalSaxDriver: found an unknown error property !");
372 - (void)walkProperty:(icalproperty *)_p ofComponent:(icalcomponent *)_comp {
374 2.9 Mapping Component Properties to XML
376 Component properties in the standard iCalendar format provide
377 calendar information about the calendar component. The component
378 properties defined in the standard iCalendar format are represented
379 in the XML representation as element types. The following tables
380 specify the element types corresponding to each of the properties in
381 the specified property category.
383 Descriptive Component Properties
384 +----------------+-------------+-----------------------------+
385 | Component | Element | Element Content Model |
386 | Property Name | Name | |
387 +----------------+-------------+-----------------------------+
388 | ATTACH | attach | extref or b64bin |
390 | | b64bin | PCDATA |
391 | CATEGORIES | categories | Any number of item elements |
393 | CLASS | class | PCDATA |
394 | COMMENT | comment | PCDATA |
395 | DESCRIPTION | description | PCDATA |
396 | GEO | geo | lat followed by lon element |
399 | LOCATION | location | PCDATA |
400 | PERCENT | percent | PCDATA |
401 | PRIORITY | priority | PCDATA |
402 | RESOURCES | resources | Any number of item elements |
403 | STATUS | status | PCDATA |
404 | SUMMARY | summary | PCDATA |
405 +----------------+-------------+-----------------------------+
406 Date and Time Component Properties
407 +----------------+------------+-----------------------------+
408 | Component | Element | Element Content Model |
409 | Property Name | Name | |
410 +----------------+------------+-----------------------------+
411 | COMPLETED | completed | PCDATA |
412 | DTEND | dtend | PCDATA |
413 | DUE | due | PCDATA |
414 | DTSTART | dtstart | PCDATA |
415 | DURATION | duration | PCDATA |
416 | FREEBUSY | freebusy | PCDATA |
417 | TRANSP | transp | PCDATA |
418 +----------------+------------+-----------------------------+
421 Time Zone Component Properties
422 +----------------+-------------+-----------------------------+
423 | Component | Element | Element Content Model |
424 | Property Name | Name | |
425 +----------------+-------------+-----------------------------+
426 | TZID | tzid | PCDATA |
427 | TZNAME | tzname | PCDATA |
428 | TZOFFSETFROM | tzoffsetfrom| PCDATA |
429 | TZOFFSETTO | tzoffsetto | PCDATA |
430 | TZURL | tzurl | EMPTY |
431 +----------------+-------------+-----------------------------+
434 Relationship Component Properties
435 +----------------+---------------+--------------------------+
436 | Component | Element | Element Content Model |
437 | Property Name | Name | |
438 +----------------+---------------+--------------------------+
439 | ATTENDEE | attendee | PCDATA |
440 | CONTACT | contact | PCDATA |
441 | ORGANIZER | organizer | PCDATA |
442 | RECURRENCE-ID | recurrence-id | PCDATA |
443 | RELATED-TO | related-to | PCDATA |
444 | URL | url | EMPTY |
445 | UID | uid | PCDATA |
446 +----------------+---------------+--------------------------+
449 Recurrence Component Properties
450 +----------------+------------+-----------------------------+
451 | Component | Element | Element Content Model |
452 | Property Name | Name | |
453 +----------------+------------+-----------------------------+
454 | EXDATE | exdate | PCDATA |
455 | EXRULE | exrule | PCDATA |
456 | RDATE | rdate | PCDATA |
457 | RRULE | rrule | PCDATA |
458 +----------------+------------+-----------------------------+
461 Alarm Component Properties
462 +----------------+------------+-----------------------------+
463 | Component | Element | Element Content Model |
464 | Property Name | Name | |
465 +----------------+------------+-----------------------------+
466 | ACTION | action | PCDATA |
467 | REPEAT | repeat | PCDATA |
468 | TRIGGER | trigger | PCDATA |
469 +----------------+------------+-----------------------------+
472 Change Management Component Properties
473 +----------------+---------------+--------------------------+
474 | Component | Element | Element Content Model |
475 | Property Name | Name | |
476 +----------------+---------------+--------------------------+
477 | CREATED | created | PCDATA |
478 | DTSTAMP | dtstamp | PCDATA |
479 | LAST-MODIFIED | last-modified | PCDATA |
480 | SEQUENCE | sequence | PCDATA |
481 +----------------+---------------+--------------------------+
484 Miscellaneous Component Properties
485 +----------------+----------------+-------------------------+
486 | Component | Element | Element Content Model |
487 | Property Name | Name | |
488 +----------------+----------------+-------------------------+
489 | REQUEST-STATUS | request-status | PCDATA |
490 +----------------+----------------+-------------------------+
492 NSString *tagName = nil;
494 if (_p == NULL) return;
496 switch (icalproperty_isa(_p)) {
497 case ICAL_ACTION_PROPERTY: tagName = @"action"; break;
498 case ICAL_ATTENDEE_PROPERTY: tagName = @"attendee"; break;
499 case ICAL_CLASS_PROPERTY: tagName = @"class"; break;
500 case ICAL_COMMENT_PROPERTY: tagName = @"comment"; break;
501 case ICAL_COMPLETED_PROPERTY: tagName = @"completed"; break;
502 case ICAL_CONTACT_PROPERTY: tagName = @"contact"; break;
503 case ICAL_CREATED_PROPERTY: tagName = @"created"; break;
504 case ICAL_DESCRIPTION_PROPERTY: tagName = @"description"; break;
505 case ICAL_DTEND_PROPERTY: tagName = @"dtend"; break;
506 case ICAL_DTSTAMP_PROPERTY: tagName = @"dtstamp"; break;
507 case ICAL_DTSTART_PROPERTY: tagName = @"dtstart"; break;
508 case ICAL_DUE_PROPERTY: tagName = @"due"; break;
509 case ICAL_DURATION_PROPERTY: tagName = @"duration"; break;
510 case ICAL_FREEBUSY_PROPERTY: tagName = @"freebusy"; break;
511 case ICAL_EXDATE_PROPERTY: tagName = @"exdate"; break;
512 case ICAL_EXRULE_PROPERTY: tagName = @"exrule"; break;
513 case ICAL_RDATE_PROPERTY: tagName = @"rdate"; break;
514 case ICAL_RRULE_PROPERTY: tagName = @"rrule"; break;
515 case ICAL_LASTMODIFIED_PROPERTY: tagName = @"last-modified"; break;
516 case ICAL_LOCATION_PROPERTY: tagName = @"location"; break;
517 case ICAL_ORGANIZER_PROPERTY: tagName = @"organizer"; break;
518 case ICAL_PRIORITY_PROPERTY: tagName = @"priority"; break;
519 case ICAL_REPEAT_PROPERTY: tagName = @"repeat"; break;
520 case ICAL_RELATEDTO_PROPERTY: tagName = @"related-to"; break;
521 case ICAL_RECURRENCEID_PROPERTY: tagName = @"recurrence-id"; break;
522 case ICAL_REQUESTSTATUS_PROPERTY: tagName = @"request-status"; break;
523 case ICAL_SEQUENCE_PROPERTY: tagName = @"sequence"; break;
524 case ICAL_STATUS_PROPERTY: tagName = @"status"; break;
525 case ICAL_SUMMARY_PROPERTY: tagName = @"summary"; break;
526 case ICAL_TRANSP_PROPERTY: tagName = @"transp"; break;
527 case ICAL_TRIGGER_PROPERTY: tagName = @"trigger"; break;
528 case ICAL_TZID_PROPERTY: tagName = @"tzid"; break;
529 case ICAL_TZNAME_PROPERTY: tagName = @"tzname"; break;
530 case ICAL_TZOFFSETFROM_PROPERTY: tagName = @"tzoffsetfrom"; break;
531 case ICAL_TZOFFSETTO_PROPERTY: tagName = @"tzoffsetto"; break;
532 case ICAL_TZURL_PROPERTY: tagName = @"tzurl"; break;
533 case ICAL_UID_PROPERTY: tagName = @"uid"; break;
534 case ICAL_URL_PROPERTY: tagName = @"url"; break;
535 case ICAL_PERCENTCOMPLETE_PROPERTY:
536 tagName = @"percent-complete";
539 /* special handling in xcal !! check ! */
541 case ICAL_GEO_PROPERTY:
542 tagName = @"geo"; /* lat followed by lon element */
547 case ICAL_RESOURCES_PROPERTY:
548 /* content model: Any number of item elements */
549 tagName = @"resources";
551 case ICAL_CATEGORIES_PROPERTY:
552 /* content model: Any number of item elements */
553 tagName = @"categories";
556 case ICAL_ATTACH_PROPERTY:
557 tagName = @"attach"; /* content model: extref || b64bin */
558 // tagName = @"extref"; /* content model: empty */
559 // tagName = @"b64bin"; /* content model: pcdata */
562 /* attribute properties */
563 case ICAL_PRODID_PROPERTY:
564 case ICAL_METHOD_PROPERTY:
565 case ICAL_VERSION_PROPERTY:
566 case ICAL_CALSCALE_PROPERTY:
569 /* extension properties */
570 case ICAL_XLICERROR_PROPERTY:
571 [self handleErrorProperty:_p ofComponent:_comp];
574 case ICAL_X_PROPERTY:
575 tagName = [StrClass stringWithCString:icalproperty_get_x_name(_p)];
579 [self logUnknownPropertyKind:_p];
584 [self->contentHandler
586 namespace:XMLNS_XCAL_01
588 attributes:[self attrsForPropertyParameters:_p]];
591 switch (icalproperty_isa(_p)) {
592 case ICAL_RRULE_PROPERTY: {
593 /* this should unparse the rrule parameters as-is */
594 NSLog(@"iCalSaxDriver: recurrence rules are not properly handled yet");
599 /* generic handling for value: pass them as SAX characters ... */
600 [self walkValue:icalproperty_get_value(_p)
601 ofProperty:_p inComponent:_comp];
604 [self->contentHandler
606 namespace:XMLNS_XCAL_01
610 - (id<SaxAttributes>)attrPropertiesOfComponent:(icalcomponent *)_comp {
613 for (p = icalcomponent_get_first_property(_comp,ICAL_ANY_PROPERTY);
615 p = icalcomponent_get_next_property(_comp,ICAL_ANY_PROPERTY)) {
617 NSString *attrName = nil;
619 switch (icalproperty_isa(p)) {
620 case ICAL_PRODID_PROPERTY: attrName = @"prodid"; break;
621 case ICAL_METHOD_PROPERTY: attrName = @"method"; break;
622 case ICAL_VERSION_PROPERTY: attrName = @"version"; break;
623 case ICAL_CALSCALE_PROPERTY: attrName = @"calscale"; break;
628 if (attrName == nil) continue;
630 v = [self stringForValue:icalproperty_get_value(p)
634 if (v == nil) continue;
636 if (self->attrs == nil)
637 self->attrs = [[SaxAttributes alloc] init];
640 addAttribute:attrName uri:XMLNS_XCAL_01 rawName:attrName
647 - (void)walkPropertiesOfComponent:(icalcomponent *)_comp {
650 for (p = icalcomponent_get_first_property(_comp,ICAL_ANY_PROPERTY);
652 p = icalcomponent_get_next_property(_comp,ICAL_ANY_PROPERTY)) {
653 [self walkProperty:p ofComponent:_comp];
657 - (void)walkSubComponents:(icalcomponent *)_comp {
660 for(c = icalcomponent_get_first_component(_comp, ICAL_ANY_COMPONENT);
662 c = icalcomponent_get_next_component(_comp, ICAL_ANY_COMPONENT)) {
663 [self walkComponent:c];
667 - (void)walkComponent:(icalcomponent *)_comp {
669 Component Structuring Properties
670 +----------------+------------+-------------------------------+
671 | Component | Element | Element Content Model |
672 | Property Name | Name | |
673 +----------------+------------+-------------------------------+
674 | Multiple iCal- | iCalendar | One or more iCal elements |
675 | endar objects | | |
676 | VCALENDAR | vcalendar | calcomp parameter entity |
677 | VEVENT | vevent | vevent.opt1 and vevent.optm |
678 | | | parameter entity and valarm |
680 | VTODO | vtodo | vtodo.opt1 and vtodo.optm |
681 | | | parameter entity and valarm |
683 | VJOURNAL | vjournal | vjournal.opt1 and |
684 | | | vjournal.optm parameter |
686 | VFREEBUSY | vfreebusy | vfreebusy.opt1 and |
687 | | | vfreebusy.optm parameter |
689 | VTIMEZONE | vtimezone | vtimezone.man, |
690 | | | vtimezone.opt1, |
691 | | | vtimezone.mann parameter |
693 | STANDARD | standard | standard.man or standard.optm |
695 | DAYLIGHT | daylight | daylight.man or daylight.optm |
697 | VALARM | valarm | valarm.audio, valarm.display, |
698 | | | valarm.email and |
699 | | | valarm.procedure entity |
700 +----------------+------------+-------------------------------+
702 NSString *tagName = nil;
704 switch (icalcomponent_isa(_comp)) {
705 case ICAL_VCALENDAR_COMPONENT:
706 tagName = @"vcalendar";
708 case ICAL_VFREEBUSY_COMPONENT:
709 tagName = @"vfreebusy";
711 case ICAL_VEVENT_COMPONENT:
714 case ICAL_VTODO_COMPONENT:
717 case ICAL_VJOURNAL_COMPONENT:
718 tagName = @"vjournal";
720 case ICAL_VTIMEZONE_COMPONENT:
721 tagName = @"vtimezone";
723 case ICAL_VALARM_COMPONENT:
727 case ICAL_XSTANDARD_COMPONENT:
728 tagName = @"standard"; // in xcal as "standard" component ?
730 case ICAL_XDAYLIGHT_COMPONENT:
731 tagName = @"daylight"; // in xcal as "daylight" component ?
734 case ICAL_XROOT_COMPONENT:
735 /* root component for a file with multiple components */
736 tagName = @"iCalendar";
740 [self logUnknownComponentKind:_comp];
746 calscale - cdata - implied
747 method - nmtoken - publish
748 version - cdata - required
749 prodid - cdata - implied
751 vcalendar: language - cdata - implied
755 [self->contentHandler
757 namespace:XMLNS_XCAL_01
759 attributes:[self attrPropertiesOfComponent:_comp]];
762 [self walkPropertiesOfComponent:_comp];
764 [self walkSubComponents:_comp];
766 [self->contentHandler
768 namespace:XMLNS_XCAL_01
772 - (void)walkRootComponent:(icalcomponent *)_comp {
773 [self->contentHandler startDocument];
774 [self->contentHandler startPrefixMapping:@"" uri:XMLNS_XCAL_01];
776 [self walkComponent:_comp];
778 [self->contentHandler endPrefixMapping:@""];
779 [self->contentHandler endDocument];
784 - (void)_reportICalErrno:(icalerrorenum)_errcode {
787 /* well, no error ... */
790 case ICAL_BADARG_ERROR:
791 case ICAL_NEWFAILED_ERROR:
792 case ICAL_ALLOCATION_ERROR:
793 case ICAL_MALFORMEDDATA_ERROR:
794 case ICAL_PARSE_ERROR:
795 case ICAL_INTERNAL_ERROR:
796 /* Like assert --internal consist. prob */
797 case ICAL_FILE_ERROR:
798 case ICAL_USAGE_ERROR:
799 case ICAL_UNIMPLEMENTED_ERROR:
800 case ICAL_UNKNOWN_ERROR:
801 /* Used for problems in input to icalerror_strerror()*/
804 SaxParseException *e;
808 errstr = icalerror_strerror(_errcode);
810 NSLog(@"%s: generic ical parsing error(code=%i): %s",
811 __PRETTY_FUNCTION__, _errcode, errstr);
813 s = [[StrClass alloc] initWithFormat:@"generic libical error %i: %s",
815 e = (id)[SaxParseException exceptionWithName:@"SaxParseException"
819 [self->errorHandler fatalError:e];
825 - (BOOL)parseString:(NSString *)_string {
830 NSLog(@"%s: parse string (len=%i)", __PRETTY_FUNCTION__,
833 if (_string == nil) {
834 if (debugOn) NSLog(@"%s: got no string ...", __PRETTY_FUNCTION__);
837 if ((str = [_string icalCString]) == NULL) {
838 if (debugOn) NSLog(@"%s: got no icalCString ...", __PRETTY_FUNCTION__);
842 // printf("STR: '%s'(%i)\n", str, strlen(str));
844 if ((c = icalparser_parse_string(str)) == NULL) {
845 /* parsing failed ... */
847 NSLog(@"%s: libical parsing failed ...", __PRETTY_FUNCTION__);
848 [self _reportICalErrno:icalerrno];
852 [self walkRootComponent:c];
856 - (BOOL)parseData:(NSData *)_data {
861 if (_data == nil) return NO;
863 len = [_data length];
864 str = malloc(len + 10);
865 [_data getBytes:str length:len];
868 if ((c = icalparser_parse_string(str)) == NULL)
869 /* parsing failed ... */
872 [self walkRootComponent:c];
877 - (void)parseFileAtPath:(NSString *)_path encoding:(NSStringEncoding)_enc {
878 NSAutoreleasePool *pool;
883 NSLog(@"%s: parse file: %@", __PRETTY_FUNCTION__, _path);
885 pool = [[NSAutoreleasePool alloc] init];
887 data = [[NSData alloc] initWithContentsOfMappedFile:_path];
889 /* throw Sax Exception ! */
891 NSLog(@"%s: could not map data: %@", __PRETTY_FUNCTION__, _path);
895 s = [[[StrClass alloc] initWithData:data encoding:_enc] autorelease];
898 [self parseString:s];
902 - (void)parseFileAtPath:(NSString *)_path {
903 [self parseFileAtPath:_path encoding:NSUTF8StringEncoding];
908 /* features & properties */
910 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
912 - (BOOL)feature:(NSString *)_name {
915 - (void)setProperty:(NSString *)_name to:(id)_value {
917 - (id)property:(NSString *)_name {
923 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
924 ASSIGN(self->contentHandler, _handler);
926 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
928 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
929 ASSIGN(self->errorHandler, _handler);
931 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
933 - (id<NSObject,SaxContentHandler>)contentHandler {
934 return self->contentHandler;
936 - (id<NSObject,SaxDTDHandler>)dtdHandler {
939 - (id<NSObject,SaxErrorHandler>)errorHandler {
940 return self->errorHandler;
942 - (id<NSObject,SaxEntityResolver>)entityResolver {
948 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
950 NSLog(@"%s: parse(%@): %@", __PRETTY_FUNCTION__, _sysId, _source);
952 if (_source == nil) {
954 if (debugOn) NSLog(@"%s: not source: %@)", __PRETTY_FUNCTION__, _sysId);
958 if ([_source isKindOfClass:StrClass]) {
959 /* convert strings to UTF8 data */
960 if (_sysId == nil) _sysId = @"<string>";
962 NSLog(@"%s: parse string (len=%i))", __PRETTY_FUNCTION__,
965 [self parseString:_source];
969 if ([_source isKindOfClass:[NSURL class]]) {
970 if (_sysId == nil) _sysId = [_source absoluteString];
971 if (debugOn) NSLog(@"%s: load URL: %@", __PRETTY_FUNCTION__, _source);
972 _source = [_source resourceDataUsingCache:NO];
974 else if ([_source isKindOfClass:[NSData class]]) {
975 if (_sysId == nil) _sysId = @"<data>";
978 SaxParseException *e;
982 NSLog(@"%s: unknown source class: %@", __PRETTY_FUNCTION__,
983 NSStringFromClass([_source class]));
986 ui = [NSDictionary dictionaryWithObjectsAndKeys:
987 _source ? _source : @"<nil>", @"source",
991 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
992 reason:@"cannot handle datasource"
995 [self->errorHandler fatalError:e];
1000 NSLog(@"%s: parse data (len=%i))", __PRETTY_FUNCTION__,
1003 [self parseData:_source];
1006 - (void)parseFromSource:(id)_source {
1007 [self parseFromSource:_source systemId:nil];
1010 - (void)parseFromSystemId:(NSString *)_sysId {
1012 NSLog(@"%s: parse ID: %@", __PRETTY_FUNCTION__, _sysId);
1014 if (![_sysId hasPrefix:@"file:"]) {
1015 SaxParseException *e;
1019 if ((url = [NSURL URLWithString:_sysId])) {
1020 [self parseFromSource:url systemId:_sysId];
1025 NSLog(@"%s: could not parse ID: %@", __PRETTY_FUNCTION__, _sysId);
1027 ui = [NSDictionary dictionaryWithObjectsAndKeys:
1028 _sysId ? _sysId : @"<nil>", @"systemID",
1032 e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
1033 reason:@"cannot handle system-id"
1036 [self->errorHandler fatalError:e];
1040 /* cut off file:// */
1041 if ([_sysId hasPrefix:@"file://"])
1042 _sysId = [_sysId substringFromIndex:7];
1044 _sysId = [_sysId substringFromIndex:5];
1047 NSLog(@"%s: parse file: %@", __PRETTY_FUNCTION__, _sysId);
1048 [self parseFileAtPath:_sysId];
1051 @end /* ICalSaxParser */
1053 @interface iCalSaxDriver : ICalSaxParser
1056 @implementation iCalSaxDriver
1059 #include "unicode.h"