Use file size and modification date to omit hash computation for unmodified files... v0.1
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Mon, 6 Feb 2012 14:13:32 +0000 (16:13 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Mon, 6 Feb 2012 14:13:32 +0000 (16:13 +0200)
pithos-macos/PithosLocalObjectState.h
pithos-macos/PithosLocalObjectState.m
pithos-macos/PithosSyncDaemon.m
pithos-macos/en.lproj/MainMenu.xib
pithos-macos/pithos-macos-Info.plist
pithos-macos/pithos_macosAppDelegate.h
pithos-macos/pithos_macosAppDelegate.m

index 42d5748..2ba45af 100644 (file)
@@ -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;
 @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
index f63a361..ad04689 100644 (file)
@@ -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 {
     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
     }
 }
 
-- (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 -
         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;
 }
     [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
index 0ca1860..5366cd6 100644 (file)
                 });
             } else {
                 directoryCreated = YES;
+                storedState.filePath = filePath;
                 storedState.isDirectory = YES;
                 [self saveLocalState];
             }
                 });
             } else {
                 fileCreated = YES;
+                storedState.filePath = filePath;
                 storedState.hash = object.objectHash;
                 storedState.tmpFilePath = nil;
                 [self saveLocalState];
         }
         // 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]];
         }
         // 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 
                                   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
         }
         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];
 
                     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];
                     // 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
                         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";
                                  currentBytes:activity.totalBytes];
             });
 
+            storedState.filePath = filePath;
             storedState.hash = object.objectHash;
             storedState.tmpFilePath = nil;
             [self saveLocalState];
index 03cd011..208e238 100644 (file)
@@ -1,17 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
        <data>
-               <int key="IBDocument.SystemTarget">1060</int>
-               <string key="IBDocument.SystemVersion">10K549</string>
+               <int key="IBDocument.SystemTarget">1070</int>
+               <string key="IBDocument.SystemVersion">11C74</string>
                <string key="IBDocument.InterfaceBuilderVersion">1938</string>
-               <string key="IBDocument.AppKitVersion">1038.36</string>
-               <string key="IBDocument.HIToolboxVersion">461.00</string>
+               <string key="IBDocument.AppKitVersion">1138.23</string>
+               <string key="IBDocument.HIToolboxVersion">567.00</string>
                <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
                        <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
                        <string key="NS.object.0">1938</string>
                </object>
                <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
                        <bool key="EncodedWithXMLCoder">YES</bool>
+                       <string>NSUserDefaultsController</string>
                        <string>NSMenu</string>
                        <string>NSMenuItem</string>
                        <string>NSCustomObject</string>
                                        </object>
                                </object>
                        </object>
+                       <object class="NSUserDefaultsController" id="796122129">
+                               <bool key="NSSharedInstance">YES</bool>
+                       </object>
                </object>
                <object class="IBObjectContainer" key="IBDocument.Objects">
                        <object class="NSMutableArray" key="connectionRecords">
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBBindingConnection" key="connection">
+                                               <string key="label">title: delegate.aboutVersion</string>
+                                               <reference key="source" ref="1057169541"/>
+                                               <reference key="destination" ref="1050"/>
+                                               <object class="NSNibBindingConnector" key="connector">
+                                                       <reference key="NSSource" ref="1057169541"/>
+                                                       <reference key="NSDestination" ref="1050"/>
+                                                       <string key="NSLabel">title: delegate.aboutVersion</string>
+                                                       <string key="NSBinding">title</string>
+                                                       <string key="NSKeyPath">delegate.aboutVersion</string>
+                                                       <int key="NSNibBindingConnectorVersion">2</int>
+                                               </object>
+                                       </object>
+                                       <int key="connectionID">580</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBBindingConnection" key="connection">
                                                <string key="label">enabled: delegate.alwaysNo</string>
                                                <reference key="source" ref="629835270"/>
                                                <reference key="destination" ref="1050"/>
                                                <reference key="parent" ref="0"/>
                                                <string key="objectName">PithosPreferencesController</string>
                                        </object>
+                                       <object class="IBObjectRecord">
+                                               <int key="objectID">576</int>
+                                               <reference key="object" ref="796122129"/>
+                                               <reference key="parent" ref="0"/>
+                                       </object>
                                </object>
                        </object>
                        <object class="NSMutableDictionary" key="flattenedProperties">
                                        <string>546.IBPluginDependency</string>
                                        <string>56.IBPluginDependency</string>
                                        <string>57.IBPluginDependency</string>
+                                       <string>576.IBPluginDependency</string>
                                        <string>58.IBPluginDependency</string>
                                        <string>72.IBPluginDependency</string>
                                        <string>73.IBPluginDependency</string>
                                        <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
                                        <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
                                        <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+                                       <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
                                </object>
                        </object>
                        <object class="NSMutableDictionary" key="unlocalizedProperties">
                                <reference key="dict.values" ref="0"/>
                        </object>
                        <nil key="sourceID"/>
-                       <int key="maxID">575</int>
+                       <int key="maxID">580</int>
                </object>
                <object class="IBClassDescriber" key="IBDocument.Classes"/>
                <int key="IBDocument.localizationMode">0</int>
index ebfb8d5..dabd092 100644 (file)
@@ -17,7 +17,7 @@
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>1.0</string>
+       <string>0.1</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleURLTypes</key>
                </dict>
        </array>
        <key>CFBundleVersion</key>
-       <string>1</string>
+       <string>20120206a</string>
        <key>LSMinimumSystemVersion</key>
        <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+       <key>LSUIElement</key>
+       <true/>
        <key>NSMainNibFile</key>
        <string>MainMenu</string>
        <key>NSPrincipalClass</key>
        <string>NSApplication</string>
-       <key>LSUIElement</key>
-       <true/>
 </dict>
 </plist>
index 9ec39ce..16ad2da 100644 (file)
@@ -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
index 282eda1..fda2640 100644 (file)
@@ -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];
 }
 
 #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 {