]> err.no Git - scalable-opengroupware.org/blob - SOPE/NGCards/iCalDataSource.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1178 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SOPE / NGCards / iCalDataSource.m
1 /*
2   Copyright (C) 2000-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 #import <Foundation/NSArray.h>
23 #import <Foundation/NSAutoreleasePool.h>
24 #import <Foundation/NSBundle.h>
25 #import <Foundation/NSObject.h>
26 #import <Foundation/NSURL.h>
27
28 #import <EOControl/EOClassDescription.h>
29 #import <EOControl/EOFetchSpecification.h>
30 #import <EOControl/EOQualifier.h>
31 #import <EOControl/EOSortOrdering.h>
32 #import <NGExtensions/EODataSource+NGExtensions.h>
33 #import <NGExtensions/NSObject+Logs.h>
34 #import <SaxObjC/SaxObjectDecoder.h>
35 #import <SaxObjC/SaxXMLReaderFactory.h>
36
37 #import "iCalObject.h"
38 #import "iCalCalendar.h"
39
40 #import "iCalDataSource.h"
41
42 @interface NSObject(suppressCapitalizedKeyWarning)
43 + (void)suppressCapitalizedKeyWarning;
44 @end
45
46 @implementation iCalDataSource
47
48 // THREAD
49 static id<NSObject,SaxXMLReader> parser = nil;
50 static SaxObjectDecoder *sax = nil;
51
52 - (void)_setupGlobals {
53   if (parser == nil ) {
54     SaxXMLReaderFactory *factory = 
55       [SaxXMLReaderFactory standardXMLReaderFactory];
56     parser = [[factory createXMLReaderForMimeType:@"text/calendar"] retain];
57     if (parser == nil)
58       [self logWithFormat:@"ERROR: found no SAX driver for 'text/calendar'!"];
59   }
60   if (sax == nil && parser != nil) {
61     NSBundle *bundle;
62     NSString *p;
63     
64 #if COCOA_Foundation_LIBRARY
65     /* otherwise we get warning on "X-WR-TIMEZONE" etc */
66     [NSClassFromString(@"NSKeyBinding") suppressCapitalizedKeyWarning];
67 #endif
68     
69     bundle = [NSBundle bundleForClass:[self class]];
70     if ((p = [bundle pathForResource:@"NGiCal" ofType:@"xmap"]))
71       sax = [[SaxObjectDecoder alloc] initWithMappingAtPath:p];
72     else
73       sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
74     
75     [parser setContentHandler:sax];
76     [parser setErrorHandler:sax];
77   }
78 }
79
80
81 - (id)initWithURL:(NSURL *)_url entityName:(NSString *)_ename {
82   [self _setupGlobals];
83   if ((self = [super init])) {
84     self->url        = [_url copy];
85     self->entityName = [_ename copy];
86   }
87   return self;
88 }
89 - (id)initWithPath:(NSString *)_path entityName:(NSString *)_ename {
90   NSURL *lurl;
91   
92   lurl = [[[NSURL alloc] initFileURLWithPath:_path] autorelease];
93   return [self initWithURL:lurl entityName:_ename];
94 }
95
96 - (id)initWithURL:(NSURL *)_url {
97   return [self initWithURL:_url entityName:nil];
98 }
99 - (id)initWithPath:(NSString *)_path {
100   return [self initWithPath:_path entityName:nil];
101 }
102 - (id)init {
103   return [self initWithURL:nil];
104 }
105
106 - (void)dealloc {
107   [self->fetchSpecification release];
108   [self->url                release];
109   [self->entityName         release];
110   [super dealloc];
111 }
112
113 /* accessors */
114
115 - (void)setFetchSpecification:(EOFetchSpecification *)_fspec {
116   if ([self->fetchSpecification isEqual:_fspec])
117     return;
118   
119   ASSIGNCOPY(self->fetchSpecification, _fspec);
120
121   [self postDataSourceChangedNotification];
122 }
123 - (EOFetchSpecification *)fetchSpecification {
124   return self->fetchSpecification;
125 }
126
127 /* fetching */
128
129 - (id)_parseCalendar {
130   id cal;
131   
132   if (parser == nil) {
133     [self logWithFormat:@"ERROR: missing iCalendar parser!"];
134     return nil;
135   }
136   if (sax == nil) {
137     [self logWithFormat:@"ERROR: missing SAX handler!"];
138     return nil;
139   }
140   
141   [parser parseFromSource:self->url];
142   cal = [sax rootObject];
143   
144   return cal;
145 }
146
147 - (NSArray *)objectsForEntityNamed:(NSString *)ename inCalendar:(id)_cal {
148   if ([ename isEqualToString:@"vevent"])
149     return [_cal events];
150   if ([ename isEqualToString:@"vtodo"])
151     return [_cal todos];
152   if ([ename isEqualToString:@"vjournal"])
153     return [_cal journals];
154   if ([ename isEqualToString:@"vfreebusy"])
155     return [_cal freeBusys];
156   
157   [self logWithFormat:@"unknown calendar entity '%@'", ename];
158   return nil;
159 }
160
161 - (NSArray *)objectsFromCalendar:(id)_cal {
162   NSString *ename;
163   
164   ename = [self->fetchSpecification entityName];
165   if ([ename length] == 0)
166     ename = self->entityName;
167
168   if ([ename length] == 0)
169     return [_cal allObjects];
170   
171   if ([_cal isKindOfClass:[NSDictionary class]]) {
172     /*
173       This happens with Mozilla Calendar which posts one vcalendar
174       entry (with method 'PUBLISH') per event.
175     */
176     NSMutableArray *ma;
177     NSArray  *calendars;
178     unsigned i, count;
179     
180     if (![[(NSDictionary *)_cal objectForKey:@"tag"] 
181            isEqualToString:@"iCalendar"]) {
182       [self logWithFormat:
183               @"ERROR: calendar (entity=%@) passed in as a dictionary: %@", 
184               _cal];
185     }
186     
187     if ((calendars=[(NSDictionary *)_cal objectForKey:@"subcomponents"])==nil)
188       return nil;
189
190     count = [calendars count];
191     ma = [NSMutableArray arrayWithCapacity:(count + 1)];
192     
193     for (i = 0; i < count; i++) {
194       NSArray *objects;
195       
196       objects = [self objectsForEntityNamed:ename 
197                       inCalendar:[calendars objectAtIndex:i]];
198       if ([objects count] == 0)
199         continue;
200       
201       [ma addObjectsFromArray:objects];
202     }
203     return ma;
204   }
205   
206   return [self objectsForEntityNamed:ename inCalendar:_cal];
207 }
208
209 - (NSArray *)fetchObjects {
210   NSAutoreleasePool *pool;
211   NSArray *result;
212   id calendar;
213
214   pool = [[NSAutoreleasePool alloc] init];
215   
216   if ((calendar = [self _parseCalendar]) == nil)
217     return nil;
218   
219   if (self->fetchSpecification == nil) {
220     result = [[self objectsFromCalendar:calendar] shallowCopy];
221   }
222   else {
223     NSMutableArray *ma;
224     NSEnumerator   *e;
225     EOQualifier *q;
226     NSArray     *sort;
227     NSArray     *objects;
228     iCalObject  *object;
229     
230     /* get objects */
231     
232     objects = [self objectsFromCalendar:calendar];
233     
234     /* first filter using qualifier */
235     
236     ma = [NSMutableArray arrayWithCapacity:[objects count]];
237     q  = [self->fetchSpecification qualifier];
238     e  = [objects objectEnumerator];
239     while ((object = [e nextObject])) {
240       if (q) {
241         if (![(id<EOQualifierEvaluation>)q evaluateWithObject:object])
242           continue;
243       }
244       
245       [ma addObject:object];
246     }
247     
248     /* now sort */
249     
250     if ((sort = [self->fetchSpecification sortOrderings]))
251       [ma sortUsingKeyOrderArray:sort];
252
253     result = [ma shallowCopy];
254   }
255   
256   [pool release];
257   
258   return [result autorelease];
259 }
260
261 @end /* iCalDataSource */