return [[self container] valueForKey:@"uids"];
}
+/* merging */
+
+- (BOOL)doesRecord:(NSDictionary *)_rec conflictWith:(NSDictionary *)_other {
+ if (_rec == _other)
+ return NO;
+ if ([_rec isEqual:_other])
+ return NO;
+
+ return YES;
+}
+
+- (NSDictionary *)_registerConflictingRecord:(NSDictionary *)_other
+ inRecord:(NSDictionary *)_record
+{
+ NSMutableArray *conflicts;
+
+ if (_record == nil) return _other;
+ if (_other == nil) return _record;
+
+ if ((conflicts = [_record objectForKey:@"conflicts"]) == nil) {
+ NSMutableDictionary *md;
+
+ md = [[_record copy] autorelease];
+ conflicts = [NSMutableArray arrayWithCapacity:4];
+ [md setObject:conflicts forKey:@"conflicts"];
+ _record = md;
+ }
+ [conflicts addObject:_other];
+ return _record;
+}
+
/* functionality */
+- (NSArray *)fetchCoreInfosFrom:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate
+ memberFolder:(id)_folder
+{
+ SOGoAppointmentFolder *aptFolder;
+
+ if (![_folder isNotNull])
+ return nil;
+
+ aptFolder = [_folder lookupName:@"Calendar" inContext:nil acquire:NO];
+ if (![aptFolder isNotNull]) {
+ [self logWithFormat:@"ERROR: member folder no 'Calendar': %@", _folder];
+ return nil;
+ }
+
+ if (![aptFolder respondsToSelector:@selector(fetchCoreInfosFrom:to:)]) {
+ [self logWithFormat:@"ERROR: folder does not implemented required API: %@",
+ _folder];
+ return nil;
+ }
+
+ return [aptFolder fetchCoreInfosFrom:_startDate to:_endDate];
+}
+
+- (NSArray *)fetchCoreInfosFrom:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate
+ memberFolders:(NSArray *)_folders
+{
+ NSMutableArray *result;
+ NSMutableDictionary *uidToRecord;
+ unsigned i, count;
+
+ if ((count = [_folders count]) == 0)
+ return [NSArray array];
+ if (count == 1) {
+ return [self fetchCoreInfosFrom:_startDate to:_endDate
+ memberFolder:[_folders objectAtIndex:0]];
+ }
+
+ uidToRecord = [NSMutableDictionary dictionaryWithCapacity:(7 * count)];
+ result = [NSMutableArray arrayWithCapacity:(7 * count)];
+ for (i = 0; i < count; i++) {
+ id results;
+ NSDictionary *record;
+
+ results = [self fetchCoreInfosFrom:_startDate to:_endDate
+ memberFolder:[_folders objectAtIndex:i]];
+ if (![results isNotNull]) continue;
+
+ results = [results objectEnumerator];
+ while ((record = [results nextObject])) {
+ NSString *uid;
+ NSDictionary *existingRecord;
+
+ uid = [record objectForKey:@"uid"];
+ if (![uid isNotNull]) {
+ [self logWithFormat:@"WARNING: record without uid: %@", result];
+ [result addObject:record];
+ continue;
+ }
+
+ if ((existingRecord = [uidToRecord objectForKey:uid]) == nil) {
+ /* record not yet in result set */
+ [uidToRecord setObject:record forKey:uid];
+ [result addObject:record];
+ }
+ else if ([self doesRecord:existingRecord conflictWith:record]) {
+ /* record already registered and it conflicts (diff values) */
+ NSDictionary *newRecord;
+ int idx;
+
+ newRecord = [self _registerConflictingRecord:record
+ inRecord:existingRecord];
+ [uidToRecord setObject:newRecord forKey:uid];
+
+ if ((idx = [result indexOfObject:existingRecord]) != NSNotFound)
+ [result replaceObjectAtIndex:idx withObject:newRecord];
+ }
+ else {
+ /* record already registered, but values in sync, nothing to do */
+ }
+ }
+ }
+ return result;
+}
+
- (NSArray *)fetchCoreInfosFrom:(NSCalendarDate *)_startDate
to:(NSCalendarDate *)_endDate
{
-#warning TODO: implement aggregation based on the container
+ /* this is the main dispatcher method */
NSArray *folders;
if ((folders = [[self container] valueForKey:@"memberFolders"]) == nil) {
return nil;
}
- return [NSArray array];
+ return [self fetchCoreInfosFrom:_startDate to:_endDate
+ memberFolders:folders];
}
@end /* SOGoGroupAppointmentFolder */