]> err.no Git - sope/blob - sope-ical/NGiCal/iCalRecurrenceRule.m
fixed a header
[sope] / sope-ical / NGiCal / iCalRecurrenceRule.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
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 "iCalRecurrenceRule.h"
23 #include "iCalDateHolder.h"
24 #include "NSCalendarDate+ICal.h"
25 #include "common.h"
26
27 /*
28  freq       = rrFreq;
29  until      = rrUntil;
30  count      = rrCount;
31  interval   = rrInterval;
32  bysecond   = rrBySecondList;
33  byminute   = rrByMinuteList;
34  byhour     = rrByHourList;
35  byday      = rrByDayList;
36  bymonthday = rrByMonthDayList;
37  byyearday  = rrByYearDayList;
38  byweekno   = rrByWeekNumberList;
39  bymonth    = rrByMonthList;
40  bysetpos   = rrBySetPosList;
41  wkst       = rrWeekStart;
42  */
43
44 @interface iCalDateHolder (PrivateAPI)
45 - (void)setString:(NSString *)_value;
46 - (id)awakeAfterUsingSaxDecoder:(id)_decoder;
47 @end
48
49 @interface iCalRecurrenceRule (PrivateAPI)
50 - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day;
51 - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay;
52 - (NSString *)freq;
53 - (NSString *)wkst;
54 - (NSString *)byDayList;
55
56 - (void)_processRule;
57 - (void)setRrule:(NSString *)_rrule;
58 @end
59
60 @implementation iCalRecurrenceRule
61
62 + (void)initialize {
63   static BOOL didInit = NO;
64   
65   if (didInit) return;
66   didInit = YES;
67 }
68
69 + (id)recurrenceRuleWithICalRepresentation:(NSString *)_iCalRep {
70   iCalRecurrenceRule *r;
71   
72   r = [[[self alloc] init] autorelease];
73   [r setRrule:_iCalRep];
74   return r;
75 }
76
77 - (id)init {
78   self = [super init];
79   if (self) {
80     self->byDay.weekStart = iCalWeekDayMonday;
81     self->interval        = 1;
82   }
83   return self;
84 }
85
86 - (void)dealloc {
87   [self->untilDate release];
88   [self->rrule     release];
89   [super dealloc];
90 }
91
92
93 /* Accessors */
94
95 - (void)setFrequency:(iCalRecurrenceFrequency)_frequency {
96   self->frequency = _frequency;
97 }
98 - (iCalRecurrenceFrequency)frequency {
99   return self->frequency;
100 }
101
102 - (void)setRepeatCount:(unsigned)_repeatCount {
103   self->repeatCount = _repeatCount;
104 }
105 - (unsigned)repeatCount {
106   return self->repeatCount;
107 }
108
109 - (void)setUntilDate:(NSCalendarDate *)_untilDate {
110   ASSIGN(self->untilDate, _untilDate);
111 }
112 - (NSCalendarDate *)untilDate {
113   return self->untilDate;
114 }
115
116 - (void)setRepeatInterval:(int)_repeatInterval {
117   self->interval = _repeatInterval;
118 }
119 - (int)repeatInterval {
120   return self->interval;
121 }
122
123 - (void)setWeekStart:(iCalWeekDay)_weekStart {
124   self->byDay.weekStart = _weekStart;
125 }
126 - (iCalWeekDay)weekStart {
127   return self->byDay.weekStart;
128 }
129
130 - (BOOL)isInfinite {
131   return (self->repeatCount != 0 || self->untilDate) ? NO : YES;
132 }
133
134
135 /* Private */
136
137 - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day {
138   _day = [_day uppercaseString];
139   if ([_day isEqualToString:@"MO"])
140     return iCalWeekDayMonday;
141   else if ([_day isEqualToString:@"TU"])
142     return iCalWeekDayTuesday;
143   else if ([_day isEqualToString:@"WE"])
144     return iCalWeekDayWednesday;
145   else if ([_day isEqualToString:@"TH"])
146     return iCalWeekDayThursday;
147   else if ([_day isEqualToString:@"FR"])
148     return iCalWeekDayFriday;
149   else if ([_day isEqualToString:@"SA"])
150     return iCalWeekDaySaturday;
151   else if ([_day isEqualToString:@"SU"])
152     return iCalWeekDaySunday;
153   else
154     [NSException raise:NSGenericException
155                  format:@"Incorrect weekDay '%@' specified!", _day];
156   return iCalWeekDayMonday; /* keep compiler happy */
157 }
158
159 - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay {
160   switch (self->byDay.weekStart) {
161     case iCalWeekDayMonday:
162       return @"MO";
163     case iCalWeekDayTuesday:
164       return @"TU";
165     case iCalWeekDayWednesday:
166       return @"WE";
167     case iCalWeekDayThursday:
168       return @"TH";
169     case iCalWeekDayFriday:
170       return @"FR";
171     case iCalWeekDaySaturday:
172       return @"SA";
173     case iCalWeekDaySunday:
174       return @"SU";
175     default:
176       return @"MO";
177   }
178 }
179
180 - (NSString *)freq {
181   switch (self->frequency) {
182     case iCalRecurrenceFrequenceWeekly:
183       return @"WEEKLY";
184     case iCalRecurrenceFrequenceMonthly:
185       return @"MONTHLY";
186     case iCalRecurrenceFrequenceDaily:
187       return @"DAILY";
188     case iCalRecurrenceFrequenceYearly:
189       return @"YEARLY";
190     case iCalRecurrenceFrequenceHourly:
191       return @"HOURLY";
192     case iCalRecurrenceFrequenceMinutely:
193       return @"MINUTELY";
194     case iCalRecurrenceFrequenceSecondly:
195       return @"SECONDLY";
196     default:
197       return @"UNDEFINED?";
198   }
199 }
200
201 - (NSString *)wkst {
202   return [self iCalRepresentationForWeekDay:self->byDay.weekStart];
203 }
204
205 /*
206  TODO:
207  Each BYDAY value can also be preceded by a positive (+n) or negative
208  (-n) integer. If present, this indicates the nth occurrence of the
209  specific day within the MONTHLY or YEARLY RRULE. For example, within
210  a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
211  within the month, whereas -1MO represents the last Monday of the
212  month. If an integer modifier is not present, it means all days of
213  this type within the specified frequency. For example, within a
214  MONTHLY rule, MO represents all Mondays within the month.
215 */
216 - (NSString *)byDayList {
217   NSMutableString *s;
218   unsigned        i, day;
219   BOOL            needsComma;
220
221   s          = [NSMutableString stringWithCapacity:20];
222   needsComma = NO;
223   day        = iCalWeekDayMonday;
224
225   for (i = 0; i < 7; i++) {
226     if (self->byDay.mask && day) {
227       if (needsComma)
228         [s appendString:@","];
229       [s appendString:[self iCalRepresentationForWeekDay:day]];
230       needsComma = YES;
231     }
232     day = day << 1;
233   }
234   return s;
235 }
236
237 /* Rule */
238
239 - (void)setRrule:(NSString *)_rrule {
240   ASSIGN(self->rrule, _rrule);
241   [self _processRule];
242 }
243
244 /* Processing existing rrule */
245
246 - (void)_processRule {
247   NSArray  *props;
248   unsigned i, count;
249   
250   props = [self->rrule componentsSeparatedByString:@";"];
251   count = [props count];
252   for (i = 0; i < count; i++) {
253     NSString *prop, *key, *value;
254     NSRange  r;
255     
256     prop = [props objectAtIndex:i];
257     r    = [prop rangeOfString:@"="];
258     if (r.length) {
259       key   = [prop substringToIndex:r.location];
260       value = [prop substringFromIndex:NSMaxRange(r)];
261     }
262     else {
263       key   = prop;
264       value = nil;
265     }
266     [self takeValue:value forKey:[key lowercaseString]];
267   }
268 }
269
270
271 /* properties */
272
273 - (void)setFreq:(NSString *)_freq {
274   _freq = [_freq uppercaseString];
275   if ([_freq isEqualToString:@"WEEKLY"])
276     self->frequency = iCalRecurrenceFrequenceWeekly;
277   else if ([_freq isEqualToString:@"MONTHLY"])
278     self->frequency = iCalRecurrenceFrequenceMonthly;
279   else if ([_freq isEqualToString:@"DAILY"])
280     self->frequency = iCalRecurrenceFrequenceDaily;
281   else if ([_freq isEqualToString:@"YEARLY"])
282     self->frequency = iCalRecurrenceFrequenceYearly;
283   else if ([_freq isEqualToString:@"HOURLY"])
284     self->frequency = iCalRecurrenceFrequenceHourly;
285   else if ([_freq isEqualToString:@"MINUTELY"])
286     self->frequency = iCalRecurrenceFrequenceMinutely;
287   else if ([_freq isEqualToString:@"SECONDLY"])
288     self->frequency = iCalRecurrenceFrequenceSecondly;
289   else
290     [NSException raise:NSGenericException
291                  format:@"Incorrect frequency '%@' specified!", _freq];
292 }
293
294 - (void)setInterval:(NSString *)_interval {
295   self->interval = [_interval intValue];
296 }
297 - (void)setCount:(NSString *)_count {
298   self->repeatCount = [_count unsignedIntValue];
299 }
300 - (void)setUntil:(NSString *)_until {
301   iCalDateHolder *dh;
302   NSCalendarDate *date;
303
304   dh = [[iCalDateHolder alloc] init];
305   [dh setString:_until];
306   date = [dh awakeAfterUsingSaxDecoder:nil];
307   ASSIGN(self->untilDate, date);
308   [dh release];
309 }
310
311 - (void)setWkst:(NSString *)_weekStart {
312   self->byDay.weekStart = [self weekDayFromICalRepresentation:_weekStart];
313 }
314
315 - (void)setByday:(NSString *)_byDayList {
316   NSArray  *days;
317   unsigned i, count;
318
319   self->byDay.mask = 0;
320   days  = [_byDayList componentsSeparatedByString:@","];
321   count = [days count];
322   for (i = 0; i < count; i++) {
323     NSString    *iCalDay;
324     iCalWeekDay day;
325     
326     iCalDay = [days objectAtIndex:i];
327     day     = [self weekDayFromICalRepresentation:iCalDay];
328     self->byDay.mask |= day;
329   }
330 }
331
332 /* key/value coding */
333
334 - (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
335   [self warnWithFormat:@"Don't know how to process '%@'!", _key];
336 }
337
338
339 /* Description */
340
341 - (NSString *)iCalRepresentation {
342   NSMutableString *s;
343   
344   s = [NSMutableString stringWithCapacity:80];
345   [s appendString:@"FREQ="];
346   [s appendString:[self freq]];
347   if ([self repeatInterval] != 1) {
348     [s appendFormat:@";INTERVAL=%d", [self repeatInterval]];
349   }
350   if (![self isInfinite]) {
351     if ([self repeatCount] > 0) {
352       [s appendFormat:@";COUNT=%d", [self repeatCount]];
353     }
354     else {
355       [s appendString:@";UNTIL="];
356       [s appendString:[[self untilDate] icalString]];
357     }
358   }
359   if (self->byDay.weekStart != iCalWeekDayMonday) {
360     [s appendString:@";WKST="];
361     [s appendString:[self iCalRepresentationForWeekDay:self->byDay.weekStart]];
362   }
363   if (self->byDay.mask != 0) {
364     [s appendString:@";BYDAY="];
365     [s appendString:[self byDayList]];
366   }
367   return s;
368 }
369
370
371 @end