2 Copyright (C) 2004-2005 SKYRIX Software AG
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 "iCalRecurrenceRule.h"
23 #include "NSCalendarDate+ICal.h"
30 interval = rrInterval;
31 bysecond = rrBySecondList;
32 byminute = rrByMinuteList;
33 byhour = rrByHourList;
35 bymonthday = rrByMonthDayList;
36 byyearday = rrByYearDayList;
37 byweekno = rrByWeekNumberList;
38 bymonth = rrByMonthList;
39 bysetpos = rrBySetPosList;
43 // TODO: private API in the header file?!
44 @interface iCalRecurrenceRule (PrivateAPI)
46 - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day;
47 - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay;
50 - (NSString *)byDayList;
53 - (void)setRrule:(NSString *)_rrule; // TODO: weird name?
57 @implementation iCalRecurrenceRule
59 + (id)recurrenceRuleWithICalRepresentation:(NSString *)_iCalRep {
60 return [[[self alloc] initWithString:_iCalRep] autorelease];
63 - (id)init { /* designated initializer */
64 if ((self = [super init]) != nil) {
65 self->byDay.weekStart = iCalWeekDayMonday;
71 - (id)initWithString:(NSString *)_str {
72 if ((self = [self init]) != nil) {
79 [self->untilDate release];
80 [self->rrule release];
87 - (void)setFrequency:(iCalRecurrenceFrequency)_frequency {
88 self->frequency = _frequency;
90 - (iCalRecurrenceFrequency)frequency {
91 return self->frequency;
94 - (void)setRepeatCount:(unsigned)_repeatCount {
95 self->repeatCount = _repeatCount;
97 - (unsigned)repeatCount {
98 return self->repeatCount;
101 - (void)setUntilDate:(NSCalendarDate *)_untilDate {
102 ASSIGN(self->untilDate, _untilDate);
104 - (NSCalendarDate *)untilDate {
105 return self->untilDate;
108 - (void)setRepeatInterval:(int)_repeatInterval {
109 self->interval = _repeatInterval;
111 - (int)repeatInterval {
112 return self->interval;
115 - (void)setWeekStart:(iCalWeekDay)_weekStart {
116 self->byDay.weekStart = _weekStart;
118 - (iCalWeekDay)weekStart {
119 return self->byDay.weekStart;
122 - (void)setByDayMask:(unsigned)_mask {
123 self->byDay.mask = _mask;
125 - (unsigned)byDayMask {
126 return self->byDay.mask;
130 return (self->repeatCount != 0 || self->untilDate) ? NO : YES;
136 - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day {
137 if ([_day length] > 1) {
141 c0 = [_day characterAtIndex:0];
142 if (c0 == 'm' || c0 == 'M') return iCalWeekDayMonday;
143 if (c0 == 'w' || c0 == 'W') return iCalWeekDayWednesday;
144 if (c0 == 'f' || c0 == 'F') return iCalWeekDayFriday;
146 c1 = [_day characterAtIndex:1];
147 if (c0 == 't' || c0 == 't') {
148 if (c1 == 'u' || c1 == 'U') return iCalWeekDayTuesday;
149 if (c1 == 'h' || c1 == 'H') return iCalWeekDayThursday;
151 if (c0 == 's' || c0 == 'S') {
152 if (c1 == 'a' || c1 == 'A') return iCalWeekDaySaturday;
153 if (c1 == 'u' || c1 == 'U') return iCalWeekDaySunday;
157 // TODO: do not raise but rather return an error value?
158 [NSException raise:NSGenericException
159 format:@"Incorrect weekDay '%@' specified!", _day];
160 return iCalWeekDayMonday; /* keep compiler happy */
163 - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay {
165 case iCalWeekDayMonday: return @"MO";
166 case iCalWeekDayTuesday: return @"TU";
167 case iCalWeekDayWednesday: return @"WE";
168 case iCalWeekDayThursday: return @"TH";
169 case iCalWeekDayFriday: return @"FR";
170 case iCalWeekDaySaturday: return @"SA";
171 case iCalWeekDaySunday: return @"SU";
172 default: return @"MO"; // TODO: return error?
177 switch (self->frequency) {
178 case iCalRecurrenceFrequenceWeekly: return @"WEEKLY";
179 case iCalRecurrenceFrequenceMonthly: return @"MONTHLY";
180 case iCalRecurrenceFrequenceDaily: return @"DAILY";
181 case iCalRecurrenceFrequenceYearly: return @"YEARLY";
182 case iCalRecurrenceFrequenceHourly: return @"HOURLY";
183 case iCalRecurrenceFrequenceMinutely: return @"MINUTELY";
184 case iCalRecurrenceFrequenceSecondly: return @"SECONDLY";
186 return @"UNDEFINED?";
191 return [self iCalRepresentationForWeekDay:self->byDay.weekStart];
196 Each BYDAY value can also be preceded by a positive (+n) or negative
197 (-n) integer. If present, this indicates the nth occurrence of the
198 specific day within the MONTHLY or YEARLY RRULE. For example, within
199 a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
200 within the month, whereas -1MO represents the last Monday of the
201 month. If an integer modifier is not present, it means all days of
202 this type within the specified frequency. For example, within a
203 MONTHLY rule, MO represents all Mondays within the month.
205 - (NSString *)byDayList {
207 unsigned i, mask, day;
210 s = [NSMutableString stringWithCapacity:20];
212 mask = self->byDay.mask;
213 day = iCalWeekDayMonday;
215 for (i = 0; i < 7; i++) {
218 [s appendString:@","];
219 [s appendString:[self iCalRepresentationForWeekDay:day]];
229 - (void)setRrule:(NSString *)_rrule {
230 ASSIGNCOPY(self->rrule, _rrule);
234 /* Processing existing rrule */
236 - (void)_processRule {
240 props = [self->rrule componentsSeparatedByString:@";"];
241 for (i = 0, count = [props count]; i < count; i++) {
242 NSString *prop, *key, *value;
245 prop = [props objectAtIndex:i];
246 r = [prop rangeOfString:@"="];
248 key = [prop substringToIndex:r.location];
249 value = [prop substringFromIndex:NSMaxRange(r)];
255 [self takeValue:value forKey:[key lowercaseString]];
262 - (void)setFreq:(NSString *)_freq {
263 _freq = [_freq uppercaseString];
264 if ([_freq isEqualToString:@"WEEKLY"])
265 self->frequency = iCalRecurrenceFrequenceWeekly;
266 else if ([_freq isEqualToString:@"MONTHLY"])
267 self->frequency = iCalRecurrenceFrequenceMonthly;
268 else if ([_freq isEqualToString:@"DAILY"])
269 self->frequency = iCalRecurrenceFrequenceDaily;
270 else if ([_freq isEqualToString:@"YEARLY"])
271 self->frequency = iCalRecurrenceFrequenceYearly;
272 else if ([_freq isEqualToString:@"HOURLY"])
273 self->frequency = iCalRecurrenceFrequenceHourly;
274 else if ([_freq isEqualToString:@"MINUTELY"])
275 self->frequency = iCalRecurrenceFrequenceMinutely;
276 else if ([_freq isEqualToString:@"SECONDLY"])
277 self->frequency = iCalRecurrenceFrequenceSecondly;
279 [NSException raise:NSGenericException
280 format:@"Incorrect frequency '%@' specified!", _freq];
284 - (void)setInterval:(NSString *)_interval {
285 self->interval = [_interval intValue];
287 - (void)setCount:(NSString *)_count {
288 self->repeatCount = [_count unsignedIntValue];
290 - (void)setUntil:(NSString *)_until {
291 NSCalendarDate *date;
293 date = [NSCalendarDate calendarDateWithICalRepresentation:_until];
294 ASSIGN(self->untilDate, date);
297 - (void)setWkst:(NSString *)_weekStart {
298 self->byDay.weekStart = [self weekDayFromICalRepresentation:_weekStart];
301 - (void)setByday:(NSString *)_byDayList {
305 self->byDay.mask = 0;
306 days = [_byDayList componentsSeparatedByString:@","];
307 for (i = 0, count = [days count]; i < count; i++) {
311 iCalDay = [days objectAtIndex:i];
312 day = [self weekDayFromICalRepresentation:iCalDay];
313 self->byDay.mask |= day;
317 /* key/value coding */
319 - (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
320 [self warnWithFormat:@"Don't know how to process '%@'!", _key];
326 - (NSString *)iCalRepresentation {
329 s = [NSMutableString stringWithCapacity:80];
331 [s appendString:@"FREQ="];
332 [s appendString:[self freq]];
334 if ([self repeatInterval] != 1)
335 [s appendFormat:@";INTERVAL=%d", [self repeatInterval]];
337 if (![self isInfinite]) {
338 if ([self repeatCount] > 0) {
339 [s appendFormat:@";COUNT=%d", [self repeatCount]];
342 [s appendString:@";UNTIL="];
343 [s appendString:[[self untilDate] icalString]];
346 if (self->byDay.weekStart != iCalWeekDayMonday) {
347 [s appendString:@";WKST="];
348 [s appendString:[self iCalRepresentationForWeekDay:self->byDay.weekStart]];
350 if (self->byDay.mask != 0) {
351 [s appendString:@";BYDAY="];
352 [s appendString:[self byDayList]];
357 @end /* iCalRecurrenceRule */