]> err.no Git - sope/blob - sope-core/NGExtensions/NGCalendarDateRange.m
renamed packages as discussed in the developer list
[sope] / sope-core / NGExtensions / NGCalendarDateRange.m
1 /*
2   Copyright (C) 2004 Marcus Mueller
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "NGCalendarDateRange.h"
23 #include <NGExtensions/NSCalendarDate+misc.h>
24 #include <NGExtensions/NSNull+misc.h>
25 #include "common.h"
26
27 @implementation NGCalendarDateRange
28
29 + (id)calendarDateRangeWithStartDate:(NSCalendarDate *)start
30   endDate:(NSCalendarDate *)end
31 {
32     return [[[self alloc] initWithStartDate:start endDate:end] autorelease];
33 }
34
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!");
38   
39   if ((self = [super init])) {
40     if ([start compare:end] == NSOrderedAscending) {
41       self->startDate = [start copy];
42       self->endDate   = [end   copy];
43     }
44     else {
45       self->startDate = [end   copy];
46       self->endDate   = [start copy];
47     }
48   }
49   return self;
50 }
51
52 /* NSCopying */
53
54 - (id)copyWithZone:(NSZone *)zone {
55   /* object is immutable */
56   return [self retain];
57 }
58
59 /* accessors */
60
61 - (NSCalendarDate *)startDate {
62   return self->startDate;
63 }
64
65 - (NSCalendarDate *)endDate {
66   return self->endDate;
67 }
68
69 - (NGCalendarDateRange *)intersectionDateRange:(NGCalendarDateRange *)other {
70   NSCalendarDate *a, *b, *c, *d;
71     
72   if ([self compare:other] == NSOrderedAscending) {
73     a = self->startDate;
74     b = self->endDate;
75     c = [other startDate];
76     d = [other endDate];
77   }
78   else {
79     a = [other startDate];
80     b = [other endDate];
81     c = self->startDate;
82     d = self->endDate;
83   }
84   if ([b compare:c] == NSOrderedAscending)
85     return nil; // no intersection
86   if ([b compare:d] == NSOrderedAscending)
87     return [NGCalendarDateRange calendarDateRangeWithStartDate:c endDate:b];
88   
89   return [NGCalendarDateRange calendarDateRangeWithStartDate:c endDate:d];
90 }
91
92 - (BOOL)doesIntersectWithDateRange:(NGCalendarDateRange *)_other {
93   // TODO: improve
94   if (_other == nil) return NO;
95   return [self intersectionDateRange:_other] != nil ? YES : NO;
96 }
97
98 - (NGCalendarDateRange *)unionDateRange:(NGCalendarDateRange *)other {
99   NSCalendarDate *a, *b, *c, *d;
100     
101   if ([self compare:other] == NSOrderedAscending) {
102     a = self->startDate;
103     b = self->endDate;
104     c = [other startDate];
105     d = [other endDate];
106   }
107   else {
108     a = [other startDate];
109     b = [other endDate];
110     c = self->startDate;
111     d = self->endDate;
112   }
113   if ([b compare:d] == NSOrderedAscending)
114     return [NGCalendarDateRange calendarDateRangeWithStartDate:a endDate:d];
115   
116   return [NGCalendarDateRange calendarDateRangeWithStartDate:a endDate:b];
117 }
118
119 - (BOOL)containsDate:(NSCalendarDate *)date {
120   return ([self->startDate earlierDate:date] == self->startDate && 
121           [self->endDate laterDate:date] == self->endDate) ? YES : NO;
122 }
123
124 /* comparison */
125
126 - (BOOL)isEqual:(id)other {
127   if (other == nil)
128     return NO;
129   if (other == self)
130     return YES;
131   
132   if ([other isKindOfClass:self->isa] == NO)
133     return NO;
134   
135   return ([self->startDate isEqual:[other startDate]] && 
136           [self->endDate isEqual:[other endDate]]) ? YES : NO;
137 }
138
139 - (unsigned)hash {
140   return [self->startDate hash] ^ [self->endDate hash];
141 }
142
143 - (NSComparisonResult)compare:(NGCalendarDateRange *)other {
144   return [self->startDate compare:[other startDate]];
145 }
146
147 /* description */
148
149 - (NSString *)description {
150   NSMutableString *description;
151     
152   description = [NSMutableString stringWithCapacity:64];
153
154   [description appendFormat:@"<%@[0x%x]: startDate:%@ endDate: ", 
155                  NSStringFromClass(self->isa), self, self->startDate];
156   
157   if ([self->startDate isEqual:self->endDate])
158     [description appendString:@"== startDate"];
159   else
160     [description appendFormat:@"%@", self->endDate];
161   [description appendString:@">"];
162   return description;
163 }
164
165 @end /* NGCalendarDateRange */
166
167 @implementation NSArray(NGCalendarDateRanges)
168
169 - (NSArray *)arrayByCreatingDateRangesFromObjectsWithStartDateKey:(NSString *)s
170   andEndDateKey:(NSString *)e
171 {
172   NSMutableArray *ma;
173   unsigned i, count;
174   
175   count = [self count];
176   ma    = [NSMutableArray arrayWithCapacity:count];
177   for (i = 0; i < count; i++) {
178     NGCalendarDateRange *daterange;
179     NSCalendarDate *start, *end;
180     id object;
181     
182     object = [self objectAtIndex:i];
183     start  = [object valueForKey:s];
184     end    = [object valueForKey:e];
185     
186     /* skip invalid data */
187     if (![start isNotNull]) continue;
188     if (![end   isNotNull]) continue;
189     
190     daterange =
191       [[NGCalendarDateRange alloc] initWithStartDate:start endDate:end];
192     if (daterange) [ma addObject:daterange];
193     [daterange release];
194   }
195   return ma;
196 }
197
198 - (BOOL)dateRangeArrayContainsDate:(NSCalendarDate *)_date {
199   unsigned i, count;
200   
201   if (_date == nil) 
202     return NO;
203   if ((count = [self count]) == 0)
204     return NO;
205
206   for (i = 0; i < count; i++) {
207     if ([[self objectAtIndex:i] containsDate:_date])
208       return YES;
209   }
210   return NO;
211 }
212 - (unsigned)indexOfFirstIntersectingDateRange:(NGCalendarDateRange *)_range {
213   unsigned i, count;
214   
215   if (_range == nil)
216     return NO;
217   
218   if ((count = [self count]) == 0)
219     return NSNotFound;
220
221   for (i = 0; i < count; i++) {
222     if ([[self objectAtIndex:i] doesIntersectWithDateRange:_range])
223       return i;
224   }
225   return NSNotFound;
226 }
227
228 - (NSArray *)arrayByCompactingContainedDateRanges {
229   // TODO: this is a candidate for unit testing ...
230   // TODO: pretty "slow" algorithm, improve
231   NSMutableArray *ma;
232   unsigned i, count;
233   
234   count = [self count];
235   if (count < 2)
236     return [[self copy] autorelease];
237   
238   ma = [NSMutableArray arrayWithCapacity:count];
239   [ma addObject:[self objectAtIndex:0]]; /* add first range */
240   
241   for (i = 1; i < count; i++) {
242     NGCalendarDateRange *rangeToAdd;
243     NGCalendarDateRange *availRange;
244     NGCalendarDateRange *newRange;
245     unsigned idx;
246     
247     rangeToAdd = [self objectAtIndex:i];
248     idx = [ma indexOfFirstIntersectingDateRange:rangeToAdd];
249     
250     if (idx == NSNotFound) {
251       /* range not yet covered in array */
252       [ma addObject:rangeToAdd];
253       continue;
254     }
255     
256     /* union old range and replace the entry */
257     
258     availRange = [ma objectAtIndex:idx];
259     newRange   = [availRange unionDateRange:rangeToAdd];
260     
261     [ma replaceObjectAtIndex:idx withObject:newRange];
262   }
263   /* Note: we might want to join ranges up to some "closeness" (eg 1s)? */
264   return [ma sortedArrayUsingSelector:@selector(compare:)];
265 }
266
267 @end /* NSArray(NGCalendarDateRanges) */