2 Copyright (C) 2004 Marcus Mueller
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 "NGCalendarDateRange.h"
23 #include <NGExtensions/NSCalendarDate+misc.h>
24 #include <NGExtensions/NSNull+misc.h>
27 @implementation NGCalendarDateRange
29 + (id)calendarDateRangeWithStartDate:(NSCalendarDate *)start
30 endDate:(NSCalendarDate *)end
32 return [[[self alloc] initWithStartDate:start endDate:end] autorelease];
35 - (id)initWithStartDate:(NSCalendarDate *)start endDate:(NSCalendarDate *)end {
36 NSAssert(start != nil, @"startDate MUST NOT be nil!");
37 NSAssert(end != nil, @"endDate MUST NOT be nil!");
39 if ((self = [super init])) {
40 if ([start compare:end] == NSOrderedAscending) {
41 self->startDate = [start copy];
42 self->endDate = [end copy];
45 self->startDate = [end copy];
46 self->endDate = [start copy];
54 - (id)copyWithZone:(NSZone *)zone {
55 /* object is immutable */
61 - (NSCalendarDate *)startDate {
62 return self->startDate;
65 - (NSCalendarDate *)endDate {
69 - (NGCalendarDateRange *)intersectionDateRange:(NGCalendarDateRange *)other {
70 NSCalendarDate *a, *b, *c, *d;
72 if ([self compare:other] == NSOrderedAscending) {
75 c = [other startDate];
79 a = [other startDate];
85 if ([b compare:c] == NSOrderedAscending)
86 return nil; // no intersection
88 if ([b compare:d] == NSOrderedAscending) {
89 // c !< b && b !< d -> [c;b[
90 if([c compare:b] == NSOrderedSame)
91 return nil; // no real range, thus return nil!
93 return [NGCalendarDateRange calendarDateRangeWithStartDate:c endDate:b];
95 if([c compare:d] == NSOrderedSame)
96 return nil; // no real range, thus return nil!
98 return [NGCalendarDateRange calendarDateRangeWithStartDate:c endDate:d];
101 - (BOOL)doesIntersectWithDateRange:(NGCalendarDateRange *)_other {
103 if (_other == nil) return NO;
104 return [self intersectionDateRange:_other] != nil ? YES : NO;
107 - (NGCalendarDateRange *)unionDateRange:(NGCalendarDateRange *)other {
108 NSCalendarDate *a, *b, *c, *d;
110 if ([self compare:other] == NSOrderedAscending) {
113 c = [other startDate];
117 a = [other startDate];
122 if ([b compare:d] == NSOrderedAscending)
123 return [NGCalendarDateRange calendarDateRangeWithStartDate:a endDate:d];
125 return [NGCalendarDateRange calendarDateRangeWithStartDate:a endDate:b];
128 - (BOOL)containsDate:(NSCalendarDate *)date {
129 return ([self->startDate earlierDate:date] == self->startDate &&
130 [self->endDate compare:date] == NSOrderedDescending) ? YES : NO;
133 - (BOOL)containsDateRange:(NGCalendarDateRange *)_range {
134 NSComparisonResult result;
136 result = [self->startDate compare:[_range startDate]];
137 if (!((result == NSOrderedSame) || (result == NSOrderedAscending)))
139 result = [self->endDate compare:[_range endDate]];
140 if (result == NSOrderedAscending)
148 - (BOOL)isEqual:(id)other {
154 if ([other isKindOfClass:self->isa] == NO)
157 return ([self->startDate isEqual:[other startDate]] &&
158 [self->endDate isEqual:[other endDate]]) ? YES : NO;
162 return [self->startDate hash] ^ [self->endDate hash];
165 - (NSComparisonResult)compare:(NGCalendarDateRange *)other {
166 return [self->startDate compare:[other startDate]];
171 - (NSString *)description {
172 NSMutableString *description;
174 description = [NSMutableString stringWithCapacity:64];
176 [description appendFormat:@"<%@[0x%x]: startDate:%@ endDate: ",
177 NSStringFromClass(self->isa), self, self->startDate];
179 if ([self->startDate isEqual:self->endDate])
180 [description appendString:@"== startDate"];
182 [description appendFormat:@"%@", self->endDate];
183 [description appendString:@">"];
187 @end /* NGCalendarDateRange */
189 @implementation NSArray(NGCalendarDateRanges)
191 - (NSArray *)arrayByCreatingDateRangesFromObjectsWithStartDateKey:(NSString *)s
192 andEndDateKey:(NSString *)e
197 count = [self count];
198 ma = [NSMutableArray arrayWithCapacity:count];
199 for (i = 0; i < count; i++) {
200 NGCalendarDateRange *daterange;
201 NSCalendarDate *start, *end;
204 object = [self objectAtIndex:i];
205 start = [object valueForKey:s];
206 end = [object valueForKey:e];
208 /* skip invalid data */
209 if (![start isNotNull]) continue;
210 if (![end isNotNull]) continue;
213 [[NGCalendarDateRange alloc] initWithStartDate:start endDate:end];
214 if (daterange) [ma addObject:daterange];
220 - (BOOL)dateRangeArrayContainsDate:(NSCalendarDate *)_date {
225 if ((count = [self count]) == 0)
228 for (i = 0; i < count; i++) {
229 if ([[self objectAtIndex:i] containsDate:_date])
234 - (unsigned)indexOfFirstIntersectingDateRange:(NGCalendarDateRange *)_range {
240 if ((count = [self count]) == 0)
243 for (i = 0; i < count; i++) {
244 if ([[self objectAtIndex:i] doesIntersectWithDateRange:_range])
250 - (NSArray *)arrayByCompactingContainedDateRanges {
251 // TODO: this is a candidate for unit testing ...
252 // TODO: pretty "slow" algorithm, improve
256 count = [self count];
258 return [[self copy] autorelease];
260 ma = [NSMutableArray arrayWithCapacity:count];
261 [ma addObject:[self objectAtIndex:0]]; /* add first range */
263 for (i = 1; i < count; i++) {
264 NGCalendarDateRange *rangeToAdd;
265 NGCalendarDateRange *availRange;
266 NGCalendarDateRange *newRange;
269 rangeToAdd = [self objectAtIndex:i];
270 idx = [ma indexOfFirstIntersectingDateRange:rangeToAdd];
272 if (idx == NSNotFound) {
273 /* range not yet covered in array */
274 [ma addObject:rangeToAdd];
278 /* union old range and replace the entry */
280 availRange = [ma objectAtIndex:idx];
281 newRange = [availRange unionDateRange:rangeToAdd];
283 [ma replaceObjectAtIndex:idx withObject:newRange];
285 /* Note: we might want to join ranges up to some "closeness" (eg 1s)? */
286 return [ma sortedArrayUsingSelector:@selector(compare:)];
289 @end /* NSArray(NGCalendarDateRanges) */