/* Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOPE. SOPE is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. SOPE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "iCalRecurrenceRule.h" #include "NSCalendarDate+ICal.h" #include "common.h" /* freq = rrFreq; until = rrUntil; count = rrCount; interval = rrInterval; bysecond = rrBySecondList; byminute = rrByMinuteList; byhour = rrByHourList; byday = rrByDayList; bymonthday = rrByMonthDayList; byyearday = rrByYearDayList; byweekno = rrByWeekNumberList; bymonth = rrByMonthList; bysetpos = rrBySetPosList; wkst = rrWeekStart; */ @interface iCalRecurrenceRule (PrivateAPI) - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day; - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay; - (NSString *)freq; - (NSString *)wkst; - (NSString *)byDayList; - (void)_processRule; - (void)setRrule:(NSString *)_rrule; @end @implementation iCalRecurrenceRule + (void)initialize { static BOOL didInit = NO; if (didInit) return; didInit = YES; } + (id)recurrenceRuleWithICalRepresentation:(NSString *)_iCalRep { iCalRecurrenceRule *r; r = [[[self alloc] init] autorelease]; [r setRrule:_iCalRep]; return r; } - (id)init { self = [super init]; if (self) { self->byDay.weekStart = iCalWeekDayMonday; self->interval = 1; } return self; } - (void)dealloc { [self->untilDate release]; [self->rrule release]; [super dealloc]; } /* Accessors */ - (void)setFrequency:(iCalRecurrenceFrequency)_frequency { self->frequency = _frequency; } - (iCalRecurrenceFrequency)frequency { return self->frequency; } - (void)setRepeatCount:(unsigned)_repeatCount { self->repeatCount = _repeatCount; } - (unsigned)repeatCount { return self->repeatCount; } - (void)setUntilDate:(NSCalendarDate *)_untilDate { ASSIGN(self->untilDate, _untilDate); } - (NSCalendarDate *)untilDate { return self->untilDate; } - (void)setRepeatInterval:(int)_repeatInterval { self->interval = _repeatInterval; } - (int)repeatInterval { return self->interval; } - (void)setWeekStart:(iCalWeekDay)_weekStart { self->byDay.weekStart = _weekStart; } - (iCalWeekDay)weekStart { return self->byDay.weekStart; } - (void)setByDayMask:(unsigned)_mask { self->byDay.mask = _mask; } - (unsigned)byDayMask { return self->byDay.mask; } - (BOOL)isInfinite { return (self->repeatCount != 0 || self->untilDate) ? NO : YES; } /* Private */ - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day { _day = [_day uppercaseString]; if ([_day isEqualToString:@"MO"]) return iCalWeekDayMonday; else if ([_day isEqualToString:@"TU"]) return iCalWeekDayTuesday; else if ([_day isEqualToString:@"WE"]) return iCalWeekDayWednesday; else if ([_day isEqualToString:@"TH"]) return iCalWeekDayThursday; else if ([_day isEqualToString:@"FR"]) return iCalWeekDayFriday; else if ([_day isEqualToString:@"SA"]) return iCalWeekDaySaturday; else if ([_day isEqualToString:@"SU"]) return iCalWeekDaySunday; else [NSException raise:NSGenericException format:@"Incorrect weekDay '%@' specified!", _day]; return iCalWeekDayMonday; /* keep compiler happy */ } - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay { switch (_weekDay) { case iCalWeekDayMonday: return @"MO"; case iCalWeekDayTuesday: return @"TU"; case iCalWeekDayWednesday: return @"WE"; case iCalWeekDayThursday: return @"TH"; case iCalWeekDayFriday: return @"FR"; case iCalWeekDaySaturday: return @"SA"; case iCalWeekDaySunday: return @"SU"; default: return @"MO"; } } - (NSString *)freq { switch (self->frequency) { case iCalRecurrenceFrequenceWeekly: return @"WEEKLY"; case iCalRecurrenceFrequenceMonthly: return @"MONTHLY"; case iCalRecurrenceFrequenceDaily: return @"DAILY"; case iCalRecurrenceFrequenceYearly: return @"YEARLY"; case iCalRecurrenceFrequenceHourly: return @"HOURLY"; case iCalRecurrenceFrequenceMinutely: return @"MINUTELY"; case iCalRecurrenceFrequenceSecondly: return @"SECONDLY"; default: return @"UNDEFINED?"; } } - (NSString *)wkst { return [self iCalRepresentationForWeekDay:self->byDay.weekStart]; } /* TODO: Each BYDAY value can also be preceded by a positive (+n) or negative (-n) integer. If present, this indicates the nth occurrence of the specific day within the MONTHLY or YEARLY RRULE. For example, within a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday within the month, whereas -1MO represents the last Monday of the month. If an integer modifier is not present, it means all days of this type within the specified frequency. For example, within a MONTHLY rule, MO represents all Mondays within the month. */ - (NSString *)byDayList { NSMutableString *s; unsigned i, mask, day; BOOL needsComma; s = [NSMutableString stringWithCapacity:20]; needsComma = NO; mask = self->byDay.mask; day = iCalWeekDayMonday; for (i = 0; i < 7; i++) { if (mask & day) { if (needsComma) [s appendString:@","]; [s appendString:[self iCalRepresentationForWeekDay:day]]; needsComma = YES; } day = (day << 1); } return s; } /* Rule */ - (void)setRrule:(NSString *)_rrule { ASSIGN(self->rrule, _rrule); [self _processRule]; } /* Processing existing rrule */ - (void)_processRule { NSArray *props; unsigned i, count; props = [self->rrule componentsSeparatedByString:@";"]; count = [props count]; for (i = 0; i < count; i++) { NSString *prop, *key, *value; NSRange r; prop = [props objectAtIndex:i]; r = [prop rangeOfString:@"="]; if (r.length) { key = [prop substringToIndex:r.location]; value = [prop substringFromIndex:NSMaxRange(r)]; } else { key = prop; value = nil; } [self takeValue:value forKey:[key lowercaseString]]; } } /* properties */ - (void)setFreq:(NSString *)_freq { _freq = [_freq uppercaseString]; if ([_freq isEqualToString:@"WEEKLY"]) self->frequency = iCalRecurrenceFrequenceWeekly; else if ([_freq isEqualToString:@"MONTHLY"]) self->frequency = iCalRecurrenceFrequenceMonthly; else if ([_freq isEqualToString:@"DAILY"]) self->frequency = iCalRecurrenceFrequenceDaily; else if ([_freq isEqualToString:@"YEARLY"]) self->frequency = iCalRecurrenceFrequenceYearly; else if ([_freq isEqualToString:@"HOURLY"]) self->frequency = iCalRecurrenceFrequenceHourly; else if ([_freq isEqualToString:@"MINUTELY"]) self->frequency = iCalRecurrenceFrequenceMinutely; else if ([_freq isEqualToString:@"SECONDLY"]) self->frequency = iCalRecurrenceFrequenceSecondly; else [NSException raise:NSGenericException format:@"Incorrect frequency '%@' specified!", _freq]; } - (void)setInterval:(NSString *)_interval { self->interval = [_interval intValue]; } - (void)setCount:(NSString *)_count { self->repeatCount = [_count unsignedIntValue]; } - (void)setUntil:(NSString *)_until { NSCalendarDate *date; date = [NSCalendarDate calendarDateWithICalRepresentation:_until]; ASSIGN(self->untilDate, date); } - (void)setWkst:(NSString *)_weekStart { self->byDay.weekStart = [self weekDayFromICalRepresentation:_weekStart]; } - (void)setByday:(NSString *)_byDayList { NSArray *days; unsigned i, count; self->byDay.mask = 0; days = [_byDayList componentsSeparatedByString:@","]; count = [days count]; for (i = 0; i < count; i++) { NSString *iCalDay; iCalWeekDay day; iCalDay = [days objectAtIndex:i]; day = [self weekDayFromICalRepresentation:iCalDay]; self->byDay.mask |= day; } } /* key/value coding */ - (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key { NSLog(@"Don't know how to process '%@'!", _key); } /* Description */ - (NSString *)iCalRepresentation { NSMutableString *s; s = [NSMutableString stringWithCapacity:80]; [s appendString:@"FREQ="]; [s appendString:[self freq]]; if ([self repeatInterval] != 1) { [s appendFormat:@";INTERVAL=%d", [self repeatInterval]]; } if (![self isInfinite]) { if ([self repeatCount] > 0) { [s appendFormat:@";COUNT=%d", [self repeatCount]]; } else { [s appendString:@";UNTIL="]; [s appendString:[[self untilDate] icalString]]; } } if (self->byDay.weekStart != iCalWeekDayMonday) { [s appendString:@";WKST="]; [s appendString:[self iCalRepresentationForWeekDay:self->byDay.weekStart]]; } if (self->byDay.mask != 0) { [s appendString:@";BYDAY="]; [s appendString:[self byDayList]]; } return s; } @end