// // Folder.m // OpenStack // // Created by Mike Mayo on 12/7/10. // The OpenStack project is provided under the Apache 2.0 license. // #import "Folder.h" #import "StorageObject.h" #import "NSObject+NSCoding.h" #import "PithosUtilities.h" @implementation Folder @synthesize folderObject, parent, objects, folders, objectsAndFoldersCount; @synthesize name, sharing, contentType, lastModifiedString, metadata; @synthesize sortedContentsByName, sortedContentsByDate; #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 { return [[[self alloc] init] autorelease]; } - (NSComparisonResult)compareByName:(id)item { return [self.name caseInsensitiveCompare:[item name]]; } - (NSComparisonResult)compareByDate:(id)item { return [self.lastModifiedString caseInsensitiveCompare:[item lastModifiedString]]; } #pragma mark - Serialization - (id)initWithCoder:(NSCoder *)coder { if ((self = [super init])) { [self autoDecode:coder]; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { [self autoEncodeWithCoder:coder]; } #pragma mark - Memory management -(void)dealloc { [folderObject release]; [parent release]; [folders release]; [objects release]; [sortedContentsByName release]; [sortedContentsByDate 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 { if (!self.folderObject.lastModifiedString) { NSString *computedLastModifiedString = @""; for (Folder *folder in [self.folders allValues]) { if ([computedLastModifiedString compare:folder.lastModifiedString] == NSOrderedAscending) { computedLastModifiedString = folder.lastModifiedString; } } for (StorageObject *object in [self.objects allValues]) { if ([computedLastModifiedString compare:object.lastModifiedString] == NSOrderedAscending) { computedLastModifiedString = object.lastModifiedString; } } self.folderObject.lastModifiedString = computedLastModifiedString; } return self.folderObject.lastModifiedString; } - (void)setObjects:(NSMutableDictionary *)objs { 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]; BOOL objectHasTrailingSlash = NO; if ([object.name hasSuffix:@"/"]) { objectHasTrailingSlash = YES; object.name = [object.name substringToIndex:(object.name.length - 1)]; } NSRange slashRange = [object.name rangeOfString:@"/"]; if (slashRange.location != NSNotFound) { // build up the folder structure NSString *folderName = [object.name substringToIndex:slashRange.location]; object.name = [object.name substringFromIndex:(slashRange.location + 1)]; if (objectHasTrailingSlash) object.name = [object.name stringByAppendingString:@"/"]; if (![folderedFiles objectForKey: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 folderWithObject:object]; if (objectHasTrailingSlash) folder.name = [folder.name stringByAppendingString:@"/"]; folder.parent = self; [self.folders setObject:folder forKey:folder.name]; } else { // put the files in this folder if (objectHasTrailingSlash) object.name = [object.name stringByAppendingString:@"/"]; [files setObject:object forKey:object.name]; } } // take the foldered files and recursively build the rest of the folder structure for (NSString *folderName in [folderedFiles allKeys]) { Folder *folder; StorageObject *object = [objs objectForKey:folderName]; 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]; } objects = [files retain]; [sortedContentsByName release]; sortedContentsByName = nil; [sortedContentsByDate release]; sortedContentsByDate = nil; } } - (NSArray *)sortedContentsByName { if (!sortedContentsByName) { NSMutableArray *contents = [[NSMutableArray alloc] initWithArray:[self.folders allValues]]; [contents addObjectsFromArray:[self.objects allValues]]; sortedContentsByName = [[NSArray alloc] initWithArray:[contents sortedArrayUsingSelector:@selector(compareByName:)]]; } return sortedContentsByName; } - (NSArray *)sortedContentsByDate { if (!sortedContentsByDate) { NSMutableArray *contents = [[NSMutableArray alloc] initWithArray:[self.folders allValues]]; [contents addObjectsFromArray:[self.objects allValues]]; sortedContentsByDate = [[NSArray alloc] initWithArray:[contents sortedArrayUsingSelector:@selector(compareByDate:)]]; } return sortedContentsByDate; } - (NSUInteger)objectsAndFoldersCount { return (self.objects.count + self.folders.count); } #pragma mark - Actions - (NSArray *)sortedContentsUsingFilter:(NSString *)filter scope:(NSString *)scope sortType:(NSString *)sortType sortDirection:(NSString *)sortDirection { NSArray *sortedContents = nil; if ([sortType isEqualToString:@"date"]) { sortedContents = self.sortedContentsByDate; } else { sortedContents = self.sortedContentsByName; } if ([sortDirection isEqualToString:@"Z-A"]) { sortedContents = [[sortedContents reverseObjectEnumerator] allObjects]; } if (!filter || !filter.length) { return sortedContents; } return [sortedContents objectsAtIndexes: [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sortedContents.count)] indexesPassingTest:^(NSUInteger idx, BOOL *stop) { id item = [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; }]]; } - (NSString *)fullPath { if (self.parent) { return [[self.parent fullPath] stringByAppendingFormat:@"/%@", self.name]; } else if (self.name) { return self.name; } else { return @""; } } - (void)addFolder:(Folder *)aFolder { [self.folders setObject:aFolder forKey:aFolder.name]; [sortedContentsByName release]; sortedContentsByName = nil; [sortedContentsByDate release]; sortedContentsByDate = nil; } - (void)addObject:(StorageObject *)anObject { [self.objects setObject:anObject forKey:anObject.name]; [sortedContentsByName release]; sortedContentsByName = nil; [sortedContentsByDate release]; sortedContentsByDate = nil; } - (void)removeFolder:(Folder *)aFolder { if ([self.folders objectForKey:aFolder.name]) { [self.folders removeObjectForKey:aFolder.name]; [sortedContentsByName release]; sortedContentsByName = nil; [sortedContentsByDate release]; sortedContentsByDate = nil; } } - (void)removeObject:(StorageObject *)anObject { if ([self.objects objectForKey:anObject.name]) { [self.objects removeObjectForKey:anObject.name]; [sortedContentsByName release]; sortedContentsByName = nil; [sortedContentsByDate release]; sortedContentsByDate = nil; } } @end