From: Miltiadis Vasilakis Date: Mon, 6 Feb 2012 14:13:32 +0000 (+0200) Subject: Use file size and modification date to omit hash computation for unmodified files... X-Git-Tag: v0.1 X-Git-Url: https://code.grnet.gr/git/pithos-macos/commitdiff_plain/refs/tags/v0.1 Use file size and modification date to omit hash computation for unmodified files during sync. Show version in menu. --- diff --git a/pithos-macos/PithosLocalObjectState.h b/pithos-macos/PithosLocalObjectState.h index 42d5748..2ba45af 100644 --- a/pithos-macos/PithosLocalObjectState.h +++ b/pithos-macos/PithosLocalObjectState.h @@ -42,8 +42,8 @@ BOOL isDirectory; NSString *hash; NSString *tmpFilePath; - - BOOL exists; + NSDate *fileModificationDate; + NSNumber *fileSize; } - (id)initWithFile:(NSString *)aFilePath blockHash:(NSString *)blockHash blockSize:(NSUInteger)blockSize; @@ -57,7 +57,10 @@ @property (nonatomic, assign) BOOL isDirectory; @property (nonatomic, retain) NSString *hash; @property (nonatomic, retain) NSString *tmpFilePath; +@property (nonatomic, retain) NSDate *fileModificationDate; +@property (nonatomic, retain) NSNumber *fileSize; -@property (nonatomic, readonly) BOOL exists; +- (BOOL)exists; +- (BOOL)isModified; @end diff --git a/pithos-macos/PithosLocalObjectState.m b/pithos-macos/PithosLocalObjectState.m index f63a361..ad04689 100644 --- a/pithos-macos/PithosLocalObjectState.m +++ b/pithos-macos/PithosLocalObjectState.m @@ -40,7 +40,7 @@ #import "HashMapHash.h" @implementation PithosLocalObjectState -@synthesize filePath, isDirectory, exists, hash, tmpFilePath; +@synthesize filePath, isDirectory, hash, tmpFilePath, fileModificationDate, fileSize; #pragma mark - #pragma mark Object Lifecycle @@ -49,7 +49,7 @@ if ((self = [self init])) { self.filePath = aFilePath; if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory] && !isDirectory) { - self.hash = [HashMapHash calculateHashMapHash:[HashMapHash calculateObjectHashMap:aFilePath + self.hash = [HashMapHash calculateHashMapHash:[HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize]]; } @@ -80,6 +80,8 @@ } - (void)dealloc { + [fileSize release]; + [fileModificationDate release]; self.tmpFilePath = nil; [filePath release]; [hash release]; @@ -87,8 +89,8 @@ } - (NSString *)description { - return [NSString stringWithFormat:@"hash: %@, filePath: %@, isDirectory: %d", - hash, filePath, isDirectory]; + return [NSString stringWithFormat:@"hash: %@, filePath: %@, isDirectory: %d, fileModificationDate: %@, fileSize: %@", + hash, filePath, isDirectory, fileModificationDate, fileSize]; } - (BOOL)isEqualToState:(PithosLocalObjectState *)aState { @@ -98,10 +100,10 @@ else if (aState.isDirectory) // Object is not a directory, while the other is return NO; - else if (!self.exists) + else if (![self exists]) // Object doesn't exist, check the other - return (!aState.exists); - else if (!aState.exists) + return (![aState exists]); + else if (![aState exists]) // Object exists, while the other doesn't return NO; else @@ -137,16 +139,33 @@ } } -- (BOOL)exists { - return (isDirectory || hash); -} - - (void)setHash:(NSString *)aHash { [hash release]; if ([aHash length] == 64) hash = [aHash retain]; else hash = nil; + if (filePath) { + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; + self.fileModificationDate = [attributes objectForKey:NSFileModificationDate]; + self.fileSize = [attributes objectForKey:NSFileSize]; + } else { + self.fileModificationDate = nil; + self.fileSize = nil; + } +} + +#pragma mark - +#pragma mark Methods + +- (BOOL)exists { + return (isDirectory || hash); +} + +- (BOOL)isModified { + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; + return (![fileSize isEqualToNumber:[attributes objectForKey:NSFileSize]] || + ![fileModificationDate isEqualToDate:[attributes objectForKey:NSFileModificationDate]]); } #pragma mark - @@ -158,6 +177,8 @@ self.filePath = [decoder decodeObjectForKey:@"filePath"]; self.isDirectory = [decoder decodeBoolForKey:@"isDirectory"]; self.tmpFilePath = [decoder decodeObjectForKey:@"tmpFilePath"]; + self.fileModificationDate = [decoder decodeObjectForKey:@"fileModificationDate"]; + self.fileSize = [decoder decodeObjectForKey:@"fileSize"]; } return self; } @@ -167,6 +188,8 @@ [encoder encodeObject:filePath forKey:@"filePath"]; [encoder encodeBool:isDirectory forKey:@"isDirectory"]; [encoder encodeObject:tmpFilePath forKey:@"tmpFilePath"]; + [encoder encodeObject:fileModificationDate forKey:@"fileModificationDate"]; + [encoder encodeObject:fileSize forKey:@"fileSize"]; } @end diff --git a/pithos-macos/PithosSyncDaemon.m b/pithos-macos/PithosSyncDaemon.m index 0ca1860..5366cd6 100644 --- a/pithos-macos/PithosSyncDaemon.m +++ b/pithos-macos/PithosSyncDaemon.m @@ -618,6 +618,7 @@ }); } else { directoryCreated = YES; + storedState.filePath = filePath; storedState.isDirectory = YES; [self saveLocalState]; } @@ -660,6 +661,7 @@ }); } else { fileCreated = YES; + storedState.filePath = filePath; storedState.hash = object.objectHash; storedState.tmpFilePath = nil; [self saveLocalState]; @@ -692,6 +694,9 @@ } // Check first if a local copy exists if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { + storedState.filePath = filePath; + storedState.hash = object.objectHash; + [self saveLocalState]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility startAndEndActivityWithType:PithosActivityOther message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]]; @@ -756,13 +761,15 @@ } // Check first if a local copy exists if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { + storedState.filePath = filePath; + storedState.hash = object.objectHash; + // Delete incomplete temp download + storedState.tmpFilePath = nil; + [self saveLocalState]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility startAndEndActivityWithType:PithosActivityOther message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]]; }); - // Delete incomplete temp download - error = nil; - storedState.tmpFilePath = nil; } else { [self increaseSyncOperationCount]; ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithPithos:pithos @@ -845,7 +852,7 @@ NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", nil]; [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } else if (!currentState.exists) { + } else if (![currentState exists]) { // Delete remote object if (fileExists) { // Local object created in the meantime, just mark the sync cycle as incomplete, but do delete the server object @@ -1037,14 +1044,21 @@ } self.currentLocalObjectStates = [NSMutableDictionary dictionaryWithCapacity:[subPaths count]]; for (NSString *objectName in subPaths) { - if (![storedLocalObjectStates objectForKey:objectName]) { - [storedLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName]; - } + PithosLocalObjectState *storedLocalObjectState = [storedLocalObjectStates objectForKey:objectName]; NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName]; - [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath - blockHash:blockHash - blockSize:blockSize] - forKey:filePath]; + if (!storedLocalObjectState || [storedLocalObjectState isModified]) { + // New or modified existing local object, compute current state + if (!storedLocalObjectState) + // For new local object, also create empty stored state + [storedLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName]; + [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath + blockHash:blockHash + blockSize:blockSize] + forKey:filePath]; + } else { + // Local object hasn't changed, set stored state also to current + [currentLocalObjectStates setObject:storedLocalObjectState forKey:filePath]; + } } [self saveLocalState]; @@ -1094,7 +1108,7 @@ object.contentType = remoteObject.contentType; object.objectHash = remoteObject.objectHash; [self updateLocalStateWithObject:object localFilePath:filePath]; - } else if (!remoteObject && !currentLocalObjectState.exists) { + } else if (!remoteObject && ![currentLocalObjectState exists]) { // Server state hasn't changed // If the object doesn't exist neither in the server or locally, it should be removed from the stored local objects [storedLocalObjectStates removeObjectForKey:objectName]; @@ -1111,14 +1125,17 @@ // Server state has also changed if (remoteObjectState.isDirectory && currentLocalObjectState.isDirectory) { // Both did the same change (directory) + storedLocalObjectState.filePath = filePath; storedLocalObjectState.isDirectory = YES; [self saveLocalState]; } else if ([remoteObjectState isEqualToState:currentLocalObjectState]) { // Both did the same change (object edit or delete) - if (!remoteObjectState.exists) + if (![remoteObjectState exists]) { [storedLocalObjectStates removeObjectForKey:object.name]; - else + } else { + storedLocalObjectState.filePath = filePath; storedLocalObjectState.hash = remoteObjectState.hash; + } [self saveLocalState]; } else { // Conflict, we ask the user which change to keep @@ -1126,12 +1143,12 @@ NSString *firstButtonText; NSString *secondButtonText; - if (!remoteObjectState.exists) { + if (![remoteObjectState exists]) { // Remote object has been deleted informativeText = [NSString stringWithFormat:@"'%@' has been modified locally, while it has been deleted from server.", object.name ]; firstButtonText = @"Delete local file"; secondButtonText = @"Upload file to server"; - } else if (!currentLocalObjectState.exists) { + } else if (![currentLocalObjectState exists]) { informativeText = [NSString stringWithFormat:@"'%@' has been modified on the server, while it has been deleted locally.", object.name]; firstButtonText = @"Download file from server"; secondButtonText = @"Delete file on server"; @@ -1359,6 +1376,7 @@ currentBytes:activity.totalBytes]; }); + storedState.filePath = filePath; storedState.hash = object.objectHash; storedState.tmpFilePath = nil; [self saveLocalState]; diff --git a/pithos-macos/en.lproj/MainMenu.xib b/pithos-macos/en.lproj/MainMenu.xib index 03cd011..208e238 100644 --- a/pithos-macos/en.lproj/MainMenu.xib +++ b/pithos-macos/en.lproj/MainMenu.xib @@ -1,17 +1,18 @@ - 1060 - 10K549 + 1070 + 11C74 1938 - 1038.36 - 461.00 + 1138.23 + 567.00 com.apple.InterfaceBuilder.CocoaPlugin 1938 YES + NSUserDefaultsController NSMenu NSMenuItem NSCustomObject @@ -1417,6 +1418,9 @@ + + YES + @@ -2159,6 +2163,22 @@ + title: delegate.aboutVersion + + + + + + title: delegate.aboutVersion + title + delegate.aboutVersion + 2 + + + 580 + + + enabled: delegate.alwaysNo @@ -3346,6 +3366,11 @@ PithosPreferencesController + + 576 + + + @@ -3502,6 +3527,7 @@ 546.IBPluginDependency 56.IBPluginDependency 57.IBPluginDependency + 576.IBPluginDependency 58.IBPluginDependency 72.IBPluginDependency 73.IBPluginDependency @@ -3681,6 +3707,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -3695,7 +3722,7 @@ - 575 + 580 0 diff --git a/pithos-macos/pithos-macos-Info.plist b/pithos-macos/pithos-macos-Info.plist index ebfb8d5..dabd092 100644 --- a/pithos-macos/pithos-macos-Info.plist +++ b/pithos-macos/pithos-macos-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 0.1 CFBundleSignature ???? CFBundleURLTypes @@ -32,14 +32,14 @@ CFBundleVersion - 1 + 20120206a LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} + LSUIElement + NSMainNibFile MainMenu NSPrincipalClass NSApplication - LSUIElement - diff --git a/pithos-macos/pithos_macosAppDelegate.h b/pithos-macos/pithos_macosAppDelegate.h index 9ec39ce..16ad2da 100644 --- a/pithos-macos/pithos_macosAppDelegate.h +++ b/pithos-macos/pithos_macosAppDelegate.h @@ -53,6 +53,8 @@ IBOutlet NSMenu *statusMenu; NSStatusItem *statusItem; + NSString *aboutVersion; + NSUserDefaults *userDefaults; } @@ -69,5 +71,6 @@ @property (nonatomic, retain) PithosBrowserController *pithosBrowserController; @property (nonatomic, retain) PithosSyncDaemon *pithosSyncDaemon; @property (nonatomic, assign) BOOL alwaysNo; +@property (nonatomic, readonly) NSString *aboutVersion; @end diff --git a/pithos-macos/pithos_macosAppDelegate.m b/pithos-macos/pithos_macosAppDelegate.m index 282eda1..fda2640 100644 --- a/pithos-macos/pithos_macosAppDelegate.m +++ b/pithos-macos/pithos_macosAppDelegate.m @@ -44,7 +44,7 @@ #import "ASIDownloadCache.h" @implementation pithos_macosAppDelegate -@synthesize pithos, pithosBrowserController, pithosSyncDaemon, alwaysNo; +@synthesize pithos, pithosBrowserController, pithosSyncDaemon, alwaysNo, aboutVersion; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { userDefaults = [[NSUserDefaults standardUserDefaults] retain]; @@ -194,6 +194,15 @@ } #pragma mark - +#pragma Properties + +- (NSString *)aboutVersion { + return [NSString stringWithFormat:@"About Pithos+ %@ (%@)", + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"], + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]; +} + +#pragma mark - #pragma Actions - (IBAction)showPithosBrowser:(id)sender {