2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
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>
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>
37 #import "iCalObject.h"
38 #import "iCalCalendar.h"
40 #import "iCalDataSource.h"
42 @interface NSObject(suppressCapitalizedKeyWarning)
43 + (void)suppressCapitalizedKeyWarning;
46 @implementation iCalDataSource
49 static id<NSObject,SaxXMLReader> parser = nil;
50 static SaxObjectDecoder *sax = nil;
52 - (void)_setupGlobals {
54 SaxXMLReaderFactory *factory =
55 [SaxXMLReaderFactory standardXMLReaderFactory];
56 parser = [[factory createXMLReaderForMimeType:@"text/calendar"] retain];
58 [self logWithFormat:@"ERROR: found no SAX driver for 'text/calendar'!"];
60 if (sax == nil && parser != nil) {
64 #if COCOA_Foundation_LIBRARY
65 /* otherwise we get warning on "X-WR-TIMEZONE" etc */
66 [NSClassFromString(@"NSKeyBinding") suppressCapitalizedKeyWarning];
69 bundle = [NSBundle bundleForClass:[self class]];
70 if ((p = [bundle pathForResource:@"NGiCal" ofType:@"xmap"]))
71 sax = [[SaxObjectDecoder alloc] initWithMappingAtPath:p];
73 sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
75 [parser setContentHandler:sax];
76 [parser setErrorHandler:sax];
81 - (id)initWithURL:(NSURL *)_url entityName:(NSString *)_ename {
83 if ((self = [super init])) {
84 self->url = [_url copy];
85 self->entityName = [_ename copy];
89 - (id)initWithPath:(NSString *)_path entityName:(NSString *)_ename {
92 lurl = [[[NSURL alloc] initFileURLWithPath:_path] autorelease];
93 return [self initWithURL:lurl entityName:_ename];
96 - (id)initWithURL:(NSURL *)_url {
97 return [self initWithURL:_url entityName:nil];
99 - (id)initWithPath:(NSString *)_path {
100 return [self initWithPath:_path entityName:nil];
103 return [self initWithURL:nil];
107 [self->fetchSpecification release];
109 [self->entityName release];
115 - (void)setFetchSpecification:(EOFetchSpecification *)_fspec {
116 if ([self->fetchSpecification isEqual:_fspec])
119 ASSIGNCOPY(self->fetchSpecification, _fspec);
121 [self postDataSourceChangedNotification];
123 - (EOFetchSpecification *)fetchSpecification {
124 return self->fetchSpecification;
129 - (id)_parseCalendar {
133 [self logWithFormat:@"ERROR: missing iCalendar parser!"];
137 [self logWithFormat:@"ERROR: missing SAX handler!"];
141 [parser parseFromSource:self->url];
142 cal = [sax rootObject];
147 - (NSArray *)objectsForEntityNamed:(NSString *)ename inCalendar:(id)_cal {
148 if ([ename isEqualToString:@"vevent"])
149 return [_cal events];
150 if ([ename isEqualToString:@"vtodo"])
152 if ([ename isEqualToString:@"vjournal"])
153 return [_cal journals];
154 if ([ename isEqualToString:@"vfreebusy"])
155 return [_cal freeBusys];
157 [self logWithFormat:@"unknown calendar entity '%@'", ename];
161 - (NSArray *)objectsFromCalendar:(id)_cal {
164 ename = [self->fetchSpecification entityName];
165 if ([ename length] == 0)
166 ename = self->entityName;
168 if ([ename length] == 0)
169 return [_cal allObjects];
171 if ([_cal isKindOfClass:[NSDictionary class]]) {
173 This happens with Mozilla Calendar which posts one vcalendar
174 entry (with method 'PUBLISH') per event.
180 if (![[(NSDictionary *)_cal objectForKey:@"tag"]
181 isEqualToString:@"iCalendar"]) {
183 @"ERROR: calendar (entity=%@) passed in as a dictionary: %@",
187 if ((calendars=[(NSDictionary *)_cal objectForKey:@"subcomponents"])==nil)
190 count = [calendars count];
191 ma = [NSMutableArray arrayWithCapacity:(count + 1)];
193 for (i = 0; i < count; i++) {
196 objects = [self objectsForEntityNamed:ename
197 inCalendar:[calendars objectAtIndex:i]];
198 if ([objects count] == 0)
201 [ma addObjectsFromArray:objects];
206 return [self objectsForEntityNamed:ename inCalendar:_cal];
209 - (NSArray *)fetchObjects {
210 NSAutoreleasePool *pool;
214 pool = [[NSAutoreleasePool alloc] init];
216 if ((calendar = [self _parseCalendar]) == nil)
219 if (self->fetchSpecification == nil) {
220 result = [[self objectsFromCalendar:calendar] shallowCopy];
232 objects = [self objectsFromCalendar:calendar];
234 /* first filter using qualifier */
236 ma = [NSMutableArray arrayWithCapacity:[objects count]];
237 q = [self->fetchSpecification qualifier];
238 e = [objects objectEnumerator];
239 while ((object = [e nextObject])) {
241 if (![(id<EOQualifierEvaluation>)q evaluateWithObject:object])
245 [ma addObject:object];
250 if ((sort = [self->fetchSpecification sortOrderings]))
251 [ma sortUsingKeyOrderArray:sort];
253 result = [ma shallowCopy];
258 return [result autorelease];
261 @end /* iCalDataSource */