]> err.no Git - sope/blob - sope-ical/NGiCal/iCalRecurrenceRule.m
7c09525fdb2c23aff6bae64b52323d39cae033bf
[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 - (void)setByDayMask:(unsigned)_mask {
131   self->byDay.mask = _mask;
132 }
133 - (unsigned)byDayMask {
134   return self->byDay.mask;
135 }
136
137 - (BOOL)isInfinite {
138   return (self->repeatCount != 0 || self->untilDate) ? NO : YES;
139 }
140
141
142 /* Private */
143
144 - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day {
145   _day = [_day uppercaseString];
146   if ([_day isEqualToString:@"MO"])
147     return iCalWeekDayMonday;
148   else if ([_day isEqualToString:@"TU"])
149     return iCalWeekDayTuesday;
150   else if ([_day isEqualToString:@"WE"])
151     return iCalWeekDayWednesday;
152   else if ([_day isEqualToString:@"TH"])
153     return iCalWeekDayThursday;
154   else if ([_day isEqualToString:@"FR"])
155     return iCalWeekDayFriday;
156   else if ([_day isEqualToString:@"SA"])
157     return iCalWeekDaySaturday;
158   else if ([_day isEqualToString:@"SU"])
159     return iCalWeekDaySunday;
160   else
161     [NSException raise:NSGenericException
162                  format:@"Incorrect weekDay '%@' specified!", _day];
163   return iCalWeekDayMonday; /* keep compiler happy */
164 }
165
166 - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay {
167   switch (_weekDay) {
168     case iCalWeekDayMonday:
169       return @"MO";
170     case iCalWeekDayTuesday:
171       return @"TU";
172     case iCalWeekDayWednesday:
173       return @"WE";
174     case iCalWeekDayThursday:
175       return @"TH";
176     case iCalWeekDayFriday:
177       return @"FR";
178     case iCalWeekDaySaturday:
179       return @"SA";
180     case iCalWeekDaySunday:
181       return @"SU";
182     default:
183       return @"MO";
184   }
185 }
186
187 - (NSString *)freq {
188   switch (self->frequency) {
189     case iCalRecurrenceFrequenceWeekly:
190       return @"WEEKLY";
191     case iCalRecurrenceFrequenceMonthly:
192       return @"MONTHLY";
193     case iCalRecurrenceFrequenceDaily:
194       return @"DAILY";
195     case iCalRecurrenceFrequenceYearly:
196       return @"YEARLY";
197     case iCalRecurrenceFrequenceHourly:
198       return @"HOURLY";
199     case iCalRecurrenceFrequenceMinutely:
200       return @"MINUTELY";
201     case iCalRecurrenceFrequenceSecondly:
202       return @"SECONDLY";
203     default:
204       return @"UNDEFINED?";
205   }
206 }
207
208 - (NSString *)wkst {
209   return [self iCalRepresentationForWeekDay:self->byDay.weekStart];
210 }
211
212 /*
213  TODO:
214  Each BYDAY value can also be preceded by a positive (+n) or negative
215  (-n) integer. If present, this indicates the nth occurrence of the
216  specific day within the MONTHLY or YEARLY RRULE. For example, within
217  a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
218  within the month, whereas -1MO represents the last Monday of the
219  month. If an integer modifier is not present, it means all days of
220  this type within the specified frequency. For example, within a
221  MONTHLY rule, MO represents all Mondays within the month.
222 */
223 - (NSString *)byDayList {
224   NSMutableString *s;
225   unsigned        i, mask, day;
226   BOOL            needsComma;
227
228   s          = [NSMutableString stringWithCapacity:20];
229   needsComma = NO;
230   mask       = self->byDay.mask;
231   day        = iCalWeekDayMonday;
232   for (i = 0; i < 7; i++) {
233     if (mask & day) {
234       if (needsComma)
235         [s appendString:@","];
236       [s appendString:[self iCalRepresentationForWeekDay:day]];
237       needsComma = YES;
238     }
239     day = (day << 1);
240   }
241   return s;
242 }
243
244 /* Rule */
245
246 - (void)setRrule:(NSString *)_rrule {
247   ASSIGN(self->rrule, _rrule);
248   [self _processRule];
249 }
250
251 /* Processing existing rrule */
252
253 - (void)_processRule {
254   NSArray  *props;
255   unsigned i, count;
256   
257   props = [self->rrule componentsSeparatedByString:@";"];
258   count = [props count];
259   for (i = 0; i < count; i++) {
260     NSString *prop, *key, *value;
261     NSRange  r;
262     
263     prop = [props objectAtIndex:i];
264     r    = [prop rangeOfString:@"="];
265     if (r.length) {
266       key   = [prop substringToIndex:r.location];
267       value = [prop substringFromIndex:NSMaxRange(r)];
268     }
269     else {
270       key   = prop;
271       value = nil;
272     }
273     [self takeValue:value forKey:[key lowercaseString]];
274   }
275 }
276
277
278 /* properties */
279
280 - (void)setFreq:(NSString *)_freq {
281   _freq = [_freq uppercaseString];
282   if ([_freq isEqualToString:@"WEEKLY"])
283     self->frequency = iCalRecurrenceFrequenceWeekly;
284   else if ([_freq isEqualToString:@"MONTHLY"])
285     self->frequency = iCalRecurrenceFrequenceMonthly;
286   else if ([_freq isEqualToString:@"DAILY"])
287     self->frequency = iCalRecurrenceFrequenceDaily;
288   else if ([_freq isEqualToString:@"YEARLY"])
289     self->frequency = iCalRecurrenceFrequenceYearly;
290   else if ([_freq isEqualToString:@"HOURLY"])
291     self->frequency = iCalRecurrenceFrequenceHourly;
292   else if ([_freq isEqualToString:@"MINUTELY"])
293     self->frequency = iCalRecurrenceFrequenceMinutely;
294   else if ([_freq isEqualToString:@"SECONDLY"])
295     self->frequency = iCalRecurrenceFrequenceSecondly;
296   else
297     [NSException raise:NSGenericException
298                  format:@"Incorrect frequency '%@' specified!", _freq];
299 }
300
301 - (void)setInterval:(NSString *)_interval {
302   self->interval = [_interval intValue];
303 }
304 - (void)setCount:(NSString *)_count {
305   self->repeatCount = [_count unsignedIntValue];
306 }
307 - (void)setUntil:(NSString *)_until {
308   iCalDateHolder *dh;
309   NSCalendarDate *date;
310
311   dh = [[iCalDateHolder alloc] init];
312   [dh setString:_until];
313   date = [dh awakeAfterUsingSaxDecoder:nil];
314   ASSIGN(self->untilDate, date);
315   [dh release];
316 }
317
318 - (void)setWkst:(NSString *)_weekStart {
319   self->byDay.weekStart = [self weekDayFromICalRepresentation:_weekStart];
320 }
321
322 - (void)setByday:(NSString *)_byDayList {
323   NSArray  *days;
324   unsigned i, count;
325
326   self->byDay.mask = 0;
327   days  = [_byDayList componentsSeparatedByString:@","];
328   count = [days count];
329   for (i = 0; i < count; i++) {
330     NSString    *iCalDay;
331     iCalWeekDay day;
332     
333     iCalDay = [days objectAtIndex:i];
334     day     = [self weekDayFromICalRepresentation:iCalDay];
335     self->byDay.mask |= day;
336   }
337 }
338
339 /* key/value coding */
340
341 - (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
342   [self warnWithFormat:@"Don't know how to process '%@'!", _key];
343 }
344
345
346 /* Description */
347
348 - (NSString *)iCalRepresentation {
349   NSMutableString *s;
350   
351   s = [NSMutableString stringWithCapacity:80];
352   [s appendString:@"FREQ="];
353   [s appendString:[self freq]];
354   if ([self repeatInterval] != 1) {
355     [s appendFormat:@";INTERVAL=%d", [self repeatInterval]];
356   }
357   if (![self isInfinite]) {
358     if ([self repeatCount] > 0) {
359       [s appendFormat:@";COUNT=%d", [self repeatCount]];
360     }
361     else {
362       [s appendString:@";UNTIL="];
363       [s appendString:[[self untilDate] icalString]];
364     }
365   }
366   if (self->byDay.weekStart != iCalWeekDayMonday) {
367     [s appendString:@";WKST="];
368     [s appendString:[self iCalRepresentationForWeekDay:self->byDay.weekStart]];
369   }
370   if (self->byDay.mask != 0) {
371     [s appendString:@";BYDAY="];
372     [s appendString:[self byDayList]];
373   }
374   return s;
375 }
376
377
378 @end