// // FileSystemMonitor.m // iVersion // // Created by Ben Reeves on 02/11/2009. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "iVersion.h" @implementation FileSystemMonitor @synthesize delegate; @synthesize manager; @synthesize internalDict; -(id)init { if (self = [super init]) { self.internalDict = [NSMutableDictionary dictionaryWithCapacity:10]; manager = [NSFileManager defaultManager]; firstUpdate = TRUE; } return self; } //Minimum time to sleep -(float)internalCheckInterval { return 1.0; } -(NSNumber*)sizetToNumber:(size_t)val { return [NSNumber numberWithUnsignedLongLong:val]; } -(size_t)numberToSizet:(NSNumber*)val { return [val unsignedLongLongValue]; } -(void)addPathTodict:(NSString*)path dict:(NSMutableDictionary*)dict size:(size_t)size { NSNumber * onumber = [self sizetToNumber:size]; [dict setObject:onumber forKey:path]; } -(void)addPathTodict:(NSString*)path { [self addPathTodict:path dict:internalDict size:0]; } -(NSMutableDictionary*)internalCheckPath:(NSString*)path oldDict:(NSMutableDictionary*)oldDict { NSMutableDictionary * newDict = [[[NSMutableDictionary alloc] init] autorelease]; NSError * error = nil; NSDictionary * attributes = [manager attributesOfItemAtPath:path error:&error]; NSNumber * oldDictObject = [oldDict objectForKey:path]; if (error) { printf(" error could not monitor %s\n", [path UTF8String]); return nil; } if (firstUpdate == FALSE && oldDictObject == nil) { if ([delegate respondsToSelector:@selector(nodeWasCreatedAt:)]) [delegate nodeWasCreatedAt:path]; } if ([[attributes fileType] isEqualToString:NSFileTypeRegular]) { size_t oldFilesize = [self numberToSizet:oldDictObject]; size_t newFileSize = [attributes fileSize]; if (oldFilesize != newFileSize) { size_t difference = newFileSize - oldFilesize; if (firstUpdate == FALSE) { if ([delegate respondsToSelector:@selector(fileSizeDidchange: bytes:)]) [delegate fileSizeDidchange:path bytes:difference]; } } } else if ([[attributes fileType] isEqualToString:NSFileTypeDirectory]) { NSArray * contents = [manager contentsOfDirectoryAtPath:path error:nil]; for (NSString * subpath in contents) { NSString * fullPath = [path stringByAppendingPathComponent:subpath]; //Not Seen [newDict addEntriesFromDictionary:[self internalCheckPath:fullPath oldDict:oldDict]]; } } [oldDict removeObjectForKey:path]; [self addPathTodict:path dict:newDict size:[attributes fileSize]]; return newDict; } -(void)updateInternal { //Take Out of loop to avoid potential stack overflow NSAutoreleasePool * pool = nil; NSTimeInterval startTime; NSTimeInterval endTime; NSTimeInterval executionTime; NSArray * paths = nil; NSMutableDictionary * newDict = nil; NSMutableDictionary * oldDict = nil; while (1) { pool = [[NSAutoreleasePool alloc] init]; if (internalDict == nil || [internalDict count] == 0) break; startTime = [[NSDate date] timeIntervalSince1970]; paths = [internalDict allKeys]; for (NSString * path in paths) { oldDict = [internalDict objectForKey:path]; if ([oldDict isKindOfClass:[NSString class]]) { firstUpdate = TRUE; NSDictionary * attributes = [manager attributesOfItemAtPath:path error:nil]; oldDict = [[[NSMutableDictionary alloc] init] autorelease]; [self addPathTodict:path dict:oldDict size:[attributes fileSize]]; } newDict = [self internalCheckPath:path oldDict:oldDict]; firstUpdate = FALSE; /* If entries still left then they must have been deleted */ if ([oldDict count] > 0) { for (NSString * deletedpath in oldDict) { if (firstUpdate == FALSE) { if ([delegate respondsToSelector:@selector(nodeWasDeletedAt:)]) [delegate nodeWasDeletedAt:deletedpath]; } } } [internalDict setObject:newDict forKey:path]; } endTime = [[NSDate date] timeIntervalSince1970]; executionTime = endTime - startTime; printf(" Execution time %f\n", executionTime); [NSThread sleepForTimeInterval:[self internalCheckInterval]-executionTime]; [pool release]; } } -(void)stopMonitor { self.internalDict = nil; } -(void)stopMonitor:(NSString*)path { [internalDict removeObjectForKey:path]; } -(void)monitorPath:(NSString*)path { [internalDict setObject:@"" forKey:path]; if (firstUpdate == TRUE) { [self performSelectorInBackground:@selector(updateInternal) withObject:nil]; } } -(void)dealloc { [self stopMonitor]; [internalDict release]; [delegate release]; [super dealloc]; } @end