Fix crash when popover is dismissed while a view is presented modally.
[pithos-ios] / Classes / Folder.m
index d02abe3..527a455 100755 (executable)
 #import "NSObject+NSCoding.h"
 #import "PithosUtilities.h"
 
-
 @implementation Folder
 
-@synthesize name, parent, folders, objects, metadata, sharing, contentType;
+@synthesize folderObject, parent, objects, folders, objectsAndFoldersCount;
+@synthesize name, sharing, contentType, lastModifiedString, metadata;
+
+#pragma mark - Object lifecycle
+
+- (id)initWithObject:(StorageObject *)anObject {
+    if ((self = [super init])) {
+        self.folderObject = anObject;
+        self.objects = [NSMutableDictionary dictionary];
+        self.folders = [NSMutableDictionary dictionary];
+    }
+    return self;
+}
+
+- (id)init {
+    StorageObject *anObject = [[[StorageObject alloc] init] autorelease];
+    return [self initWithObject:anObject];
+}
+
++ (id)folderWithObject:(StorageObject *)anObject {
+    return [[[self alloc] initWithObject:anObject] autorelease];
+}
 
 + (id)folder {
-       Folder *folder = [[[self alloc] init] autorelease];
-       folder.folders = [[NSMutableDictionary alloc] init];
-       folder.objects = [[NSMutableDictionary alloc] init];
-       return folder;
+    return [[[self alloc] init] autorelease];
 }
 
-#pragma mark -
-#pragma mark Serialization
+- (NSComparisonResult)compare:(Folder *)aFolder {
+    return [self.name caseInsensitiveCompare:aFolder.name];
+}
 
-- (id)init {
-    if (self = [super init]) {
-        self.folders = [[NSMutableDictionary alloc] init];
-        self.objects = [[NSMutableDictionary alloc] init];
+#pragma mark - Serialization
+
+- (id)initWithCoder:(NSCoder *)coder {
+    if ((self = [super init])) {
+        [self autoDecode:coder];
     }
     return self;
 }
 
 - (void)encodeWithCoder:(NSCoder *)coder {
     [self autoEncodeWithCoder:coder];
-    /*
-    [coder encodeObject:name forKey:@"name"];
-    [coder encodeObject:parent forKey:@"parent"];
-    [coder encodeObject:folders forKey:@"folders"];
-    [coder encodeObject:objects forKey:@"objects"];
-     */
 }
 
-- (id)initWithCoder:(NSCoder *)coder {
-    if (self = [super init]) {
-        [self autoDecode:coder];
-        /*
-        name = [[coder decodeObjectForKey:@"name"] retain];
-        parent = [[coder decodeObjectForKey:@"parent"] retain];
-        folders = [[coder decodeObjectForKey:@"folders"] retain];
-        objects = [[coder decodeObjectForKey:@"objects"] retain];
-         */
-    }
-    return self;
+#pragma mark - Memory management
+
+-(void)dealloc {
+    [folderObject release];
+       [parent release];
+       [folders release];
+       [objects release];
+    [sortedContents release];
+       [super dealloc];
+}
+
+#pragma mark - Properties
+
+- (void)setName:(NSString *)aName {
+    self.folderObject.name = aName;
+}
+
+- (NSString *)name {
+    return self.folderObject.name;
+}
+
+- (void)setSharing:(NSString *)aSharing {
+    self.folderObject.sharing = aSharing;
+}
+
+- (NSString *)sharing {
+    return self.folderObject.sharing;
+}
+
+- (void)setContentType:(NSString *)aContentType {
+    self.folderObject.contentType = aContentType;
+}
+
+- (NSString *)contentType {
+    return self.folderObject.contentType;
 }
 
+- (void)setMetadata:(NSMutableDictionary *)aMetadata {
+    self.folderObject.metadata = aMetadata;
+}
+
+- (NSMutableDictionary *)metadata {
+    return self.folderObject.metadata;
+}
+
+- (void)setLastModifiedString:(NSString *)aLastModifiedString {
+    self.folderObject.lastModifiedString = aLastModifiedString;
+}
+
+- (NSString *)lastModifiedString {
+    return self.folderObject.lastModifiedString;
+}
 
 - (void)setObjects:(NSMutableDictionary *)objs {
-    if (self.objects != objs) {
-        [self.objects release];
-        
-        NSMutableDictionary *folderedFiles = [[NSMutableDictionary alloc] init];
-        NSMutableDictionary *files = [[NSMutableDictionary alloc] init];
-        folders = [[NSMutableDictionary alloc] init];
+    if (![objects isEqualToDictionary:objs]) {
+        [objects release];
+        NSMutableDictionary *folderedFiles = [NSMutableDictionary dictionary];
+        NSMutableDictionary *files = [NSMutableDictionary dictionary];
+        self.folders = [[NSMutableDictionary alloc] init];
 
         for (NSString *key in objs) {
             StorageObject *object = [objs objectForKey:key];
                 object.name = [object.name substringToIndex:(object.name.length - 1)];
             }
 
-            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"/" options:NSRegularExpressionCaseInsensitive error:nil];            
-            NSInteger matches = [regex numberOfMatchesInString:object.name options:0 range:NSMakeRange(0, [object.name length])];
-
-            if (matches > 0) {
+            NSRange slashRange = [object.name rangeOfString:@"/"];
+            if (slashRange.location != NSNotFound) {
                 // build up the folder structure
-                NSMutableArray *components = [NSMutableArray arrayWithArray:[object.name componentsSeparatedByString:@"/"]];
-                NSString *folderName = [components objectAtIndex:0];
-                
-                object.name = [components lastObject];
+                NSString *folderName = [object.name substringToIndex:slashRange.location];
+                object.name = [object.name substringFromIndex:(slashRange.location + 1)];
                 if (objectHasTrailingSlash)
                     object.name = [object.name stringByAppendingString:@"/"];
                 
-                for (int i = [components count] - 2; i > 0; i--) {
-                    object.name = [NSString stringWithFormat:@"%@/%@", [components objectAtIndex:i], object.name];
-                }
-                
                 if (![folderedFiles objectForKey:folderName]) {
-                    [folderedFiles setObject:[[NSMutableDictionary alloc] init] forKey:folderName];
+                    [folderedFiles setObject:[NSMutableDictionary dictionary] forKey:folderName];
                 }
                 
                 NSMutableDictionary *folderFiles = [folderedFiles objectForKey:folderName];
                 [folderFiles setObject:object forKey:object.name]; 
             } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
-                Folder *folder = [[Folder alloc] init];
+                Folder *folder = [Folder folderWithObject:object];
                 if (objectHasTrailingSlash)
-                    folder.name = [NSString stringWithFormat:@"%@/", object.name];
-                else
-                    folder.name = object.name;
+                    folder.name = [folder.name stringByAppendingString:@"/"];
                 folder.parent = self;
-                folder.metadata = object.metadata;    
-                folder.sharing = object.sharing;
-                folder.contentType = object.contentType;
                 [self.folders setObject:folder forKey:folder.name];
-                [folder release];
             } else {
                 // put the files in this folder 
                 if (objectHasTrailingSlash)
         }
         
         // take the foldered files and recursively build the rest of the folder structure
-        NSArray *keys = [folderedFiles allKeys];
-        for (int i = 0; i < [keys count]; i++) {
-            NSString *folderName = [keys objectAtIndex:i];
-            NSMutableDictionary *folderFiles = [folderedFiles objectForKey:folderName];
-            Folder *folder = [[Folder alloc] init];
-            folder.name = folderName;
-            folder.parent = self;
-            folder.objects = folderFiles;
+        for (NSString *folderName in [folderedFiles allKeys]) {
+            Folder *folder;
             StorageObject *object = [objs objectForKey:folderName];
-            if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
-                folder.metadata = object.metadata;
-                folder.sharing = object.sharing;
-                folder.contentType = object.contentType;
+            if (object && [PithosUtilities isContentTypeDirectory:object.contentType]) {
+                folder = [Folder folderWithObject:object];
             } else {
+                folder = [Folder folder];
+                folder.name = folderName;
                 folder.metadata = [NSMutableDictionary dictionary];
             }
+            folder.parent = self;
+            folder.objects = [folderedFiles objectForKey:folderName];
             [self.folders setObject:folder forKey:folder.name];
-            [folder release];
         }
         
-        [folderedFiles release];
-        objects = files;
+        objects = [files retain];
+        [sortedContents release];
+        sortedContents = nil;
     }
 }
 
 - (NSArray *)sortedContents {
-    NSMutableArray *contents = [[NSMutableArray alloc] initWithArray:[self.folders allValues]];
-    
-    [contents addObjectsFromArray:[self.objects allValues]];
-    if (!sortedContents || ![sortedContents isEqualToArray:contents]) {
+    if (!sortedContents) {
+        NSMutableArray *contents = [[NSMutableArray alloc] initWithArray:[self.folders allValues]];
+        [contents addObjectsFromArray:[self.objects allValues]];
         sortedContents = [[NSArray alloc] initWithArray:[contents sortedArrayUsingSelector:@selector(compare:)]];
     }
-    [contents release];
     return sortedContents;
 }
 
-- (NSComparisonResult)compare:(Folder *)aFolder {
-    return [self.name caseInsensitiveCompare:aFolder.name];
+- (NSUInteger)objectsAndFoldersCount {
+    return (self.objects.count + self.folders.count);
+}
+
+#pragma mark - Actions
+
+- (NSArray *)sortedContentsUsingFilter:(NSString *)filter andScope:(NSString *)scope {
+    if (!filter || !filter.length)
+        return self.sortedContents;
+    return [self.sortedContents objectsAtIndexes:
+            [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.sortedContents.count)] indexesPassingTest:^(NSUInteger idx, BOOL *stop) {
+        id item = [self.sortedContents objectAtIndex:idx];
+        if ([scope isEqualToString:@"name"] || [scope isEqualToString:@"all"]) {
+            NSString *itemName = [item name];
+            if (itemName) {
+                NSUInteger nameLocation = [itemName rangeOfString:filter
+                                                          options:NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch].location;
+                if (nameLocation != NSNotFound)
+                    return YES;
+            }
+        }
+        if ([scope isEqualToString:@"date"] || [scope isEqualToString:@"all"]) {
+            NSString *itemDate = [item lastModifiedString];
+            if (itemDate) {
+                NSUInteger dateLocation = [itemDate rangeOfString:filter
+                                                          options:NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch].location;
+                if (dateLocation != NSNotFound)
+                    return YES;
+            }
+        }
+        if ([scope isEqualToString:@"type"] || [scope isEqualToString:@"all"]) {
+            NSString *itemType = [item contentType];
+            if (itemType) {
+                NSUInteger typeLocation = [itemType rangeOfString:filter
+                                                          options:NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch].location;
+                if (typeLocation != NSNotFound)
+                    return YES;
+            }
+        }
+        // Consider searching additional metadata when the scope is all
+        return NO;
+    }]];
+}
+
+- (NSArray *)sortedContentsWithNameThatStartsWith:(NSString *)filter {
+    if (!filter || !filter.length)
+        return self.sortedContents;
+    return [self.sortedContents objectsAtIndexes:
+            [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.sortedContents.count)] indexesPassingTest:^(NSUInteger idx, BOOL *stop) {
+        BOOL hasPrefix = [(NSString *)[[self.sortedContents objectAtIndex:idx] name] hasPrefix:filter];
+        if (hasPrefix)
+            return YES;
+        NSComparisonResult comparisonResult = [(NSString *)[[self.sortedContents objectAtIndex:idx] name] compare:filter];
+        if (comparisonResult == NSOrderedDescending)
+            *stop = YES;
+        return NO;
+    }]];
 }
 
 - (NSString *)fullPath {
-    NSString *result = self.name;
-    if (parent) {
-        result = [NSString stringWithFormat:@"%@/%@", [parent fullPath], self.name];
+    if (self.parent) {
+        return [[self.parent fullPath] stringByAppendingFormat:@"/%@", self.name];
+    } else if (self.name) {
+        return self.name;
+    } else {
+        return @"";
     }
-    if (!result) {
-        result = @"";
-    }
-    return result;
 }
 
--(void)dealloc {
-       [name release];
-       [parent release];
-       [folders release];
-    [sharing release];
-       [objects release];
+- (void)addFolder:(Folder *)aFolder {
+    [self.folders setObject:aFolder forKey:aFolder.name];
     [sortedContents release];
-    [contentType release];
-       [super dealloc];
+    sortedContents = nil;
+}
+
+- (void)addObject:(StorageObject *)anObject {
+    [self.objects setObject:anObject forKey:anObject.name];
+    [sortedContents release];
+    sortedContents = nil;
+}
+
+- (void)removeFolder:(Folder *)aFolder {
+    if ([self.folders objectForKey:aFolder.name]) {
+        [self.folders removeObjectForKey:aFolder.name];
+        [sortedContents release];
+        sortedContents = nil;
+    }
+}
+
+- (void)removeObject:(StorageObject *)anObject {
+    if ([self.objects objectForKey:anObject.name]) {
+        [self.objects removeObjectForKey:anObject.name];
+        [sortedContents release];
+        sortedContents = nil;
+    }
 }
 
 @end