From 605c89329b792ac10149f9f501d8f7cfc6be9b2e Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Sun, 14 Aug 2011 14:38:37 +0300 Subject: [PATCH 1/1] Improved refresh mechanism. Other minor fixes and changes. --- pithos-macos/PithosAccountNode.h | 6 +- pithos-macos/PithosAccountNode.m | 87 ++++++++++++++---------- pithos-macos/PithosBrowserController.m | 29 ++++++-- pithos-macos/PithosContainerNode.h | 12 ++-- pithos-macos/PithosContainerNode.m | 117 ++++++++++++++++++++------------ pithos-macos/PithosNode.h | 13 +++- pithos-macos/PithosNode.m | 22 +++--- pithos-macos/PithosObjectNode.h | 9 ++- pithos-macos/PithosObjectNode.m | 4 ++ pithos-macos/PithosSubdirNode.h | 6 +- pithos-macos/PithosSubdirNode.m | 5 +- 11 files changed, 199 insertions(+), 111 deletions(-) diff --git a/pithos-macos/PithosAccountNode.h b/pithos-macos/PithosAccountNode.h index cfd6541..7700332 100644 --- a/pithos-macos/PithosAccountNode.h +++ b/pithos-macos/PithosAccountNode.h @@ -36,11 +36,11 @@ // or implied, of GRNET S.A. #import "PithosNode.h" +@class ASIPithosAccountRequest; @interface PithosAccountNode : PithosNode { NSMutableArray *containers; - - BOOL refreshing; + ASIPithosAccountRequest *accountRequest; } -@end +@end \ No newline at end of file diff --git a/pithos-macos/PithosAccountNode.m b/pithos-macos/PithosAccountNode.m index 973d7a1..168bd0a 100644 --- a/pithos-macos/PithosAccountNode.m +++ b/pithos-macos/PithosAccountNode.m @@ -46,14 +46,9 @@ #pragma mark - #pragma mark Object Lifecycle -- (id)init { - if ((self = [super init])) { - refreshing = NO; - } - return self; -} - - (void)dealloc { + [accountRequest clearDelegatesAndCancel]; + [accountRequest release]; [containers release]; [super dealloc]; } @@ -61,7 +56,7 @@ #pragma mark - #pragma mark ASIHTTPRequestDelegate -- (void)requestFinished:(ASIPithosAccountRequest *)accountRequest { +- (void)requestFinished:(ASIHTTPRequest *)request { NSLog(@"URL: %@", [accountRequest url]); NSLog(@"cached: %d", [accountRequest didUseCachedResponse]); @@ -75,46 +70,56 @@ if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) { // Save new children NSLog(@"using newChildren"); - NSMutableArray *newChildren = [NSMutableArray array]; + newChildren = [NSMutableArray array]; for (ASIPithosContainer *container in containers) { PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithosContainer:container] autorelease]; if (children) { NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) + if (oldIndex != NSNotFound) { // Use the same pointer value, if possible node = [children objectAtIndex:oldIndex]; + node.pithosContainer = container; + } } [newChildren addObject:node]; } - [children release]; - children = [newChildren retain]; } - // Else cache was used and all results were fetched during this request, so previousChildren can be reused + // Else cache was used and all results were fetched during this request, so existing children can be reused + [accountRequest release]; + accountRequest = nil; [containers release]; containers = nil; // XXX sort children based on preferences - refreshing = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshFinished; + } // Notify observers that children are updated [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAccountNodeChildrenUpdated" object:self]; } else { + [accountRequest release]; // Do an additional request to fetch more objects - ASIPithosAccountRequest *newAccountRequest = [ASIPithosAccountRequest listContainersRequestWithLimit:0 - marker:[[someContainers lastObject] name] - shared:NO - until:nil]; - newAccountRequest.delegate = self; - newAccountRequest.downloadCache = [ASIDownloadCache sharedCache]; - [newAccountRequest startAsynchronous]; + accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0 + marker:[[someContainers lastObject] name] + shared:NO + until:nil] retain]; + accountRequest.delegate = self; + accountRequest.downloadCache = [ASIDownloadCache sharedCache]; + [accountRequest startAsynchronous]; } } -- (void)requestFailed:(ASIPithosAccountRequest *)accountRequest { +- (void)requestFailed:(ASIHTTPRequest *)request { // XXX do something on error, cleanup NSLog(@"error:%@", [accountRequest error]); + [newChildren release]; + newChildren = nil; + [accountRequest release]; + accountRequest = nil; [containers release]; containers = nil; - childrenDirty = YES; - refreshing = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshNeeded; + } } #pragma mark - @@ -127,22 +132,34 @@ } - (NSArray *)children { - if (childrenDirty) { - @synchronized (self) { - if (!refreshing) { - refreshing = YES; - childrenDirty = NO; - ASIPithosAccountRequest *accountRequest = [ASIPithosAccountRequest listContainersRequestWithLimit:0 - marker:nil - shared:NO - until:nil]; + @synchronized(self) { + switch (freshness) { + case PithosNodeStateFresh: + break; + case PithosNodeStateRefreshNeeded: + freshness = PithosNodeStateRefreshing; + accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0 + marker:nil + shared:NO + until:nil] retain]; accountRequest.delegate = self; accountRequest.downloadCache = [ASIDownloadCache sharedCache]; [accountRequest startAsynchronous]; - } + break; + case PithosNodeStateRefreshing: + break; + case PithosNodeStateRefreshFinished: + if (newChildren) { + [children release]; + children = newChildren; + newChildren = nil; + } + freshness = PithosNodeStateFresh; + default: + break; } + return children; } - return children; } - (NSString *)displayName { diff --git a/pithos-macos/PithosBrowserController.m b/pithos-macos/PithosBrowserController.m index 6720a92..d76e31f 100644 --- a/pithos-macos/PithosBrowserController.m +++ b/pithos-macos/PithosBrowserController.m @@ -195,16 +195,29 @@ #pragma Observers - (void)pithosNodeChildrenUpdated:(NSNotification *)notification { - if ([[browser parentForItemsInColumn:[browser lastColumn]] isEqualTo:[notification object]]) - [browser reloadColumn:[browser lastColumn]]; + PithosNode *node = (PithosNode *)[notification object]; + NSInteger lastColumn = [browser lastColumn]; + for (NSInteger column = lastColumn; column >= 0; column--) { + if ([[browser parentForItemsInColumn:column] isEqualTo:node]) { + [browser reloadColumn:column]; + if ((column == lastColumn - 1) && ([[browser parentForItemsInColumn:lastColumn] isLeafItem])) { + // This reloads the preview column + [browser setLastColumn:column]; + [browser addColumn]; + } + return; + } + } } #pragma mark - #pragma Actions - (IBAction)refresh:(id)sender { - [[browser parentForItemsInColumn:[browser lastColumn]] invalidateChildren]; - [browser reloadColumn:[browser lastColumn]]; + for (NSInteger column = [browser lastColumn]; column >= 0; column--) { + [(PithosNode *)[browser parentForItemsInColumn:column] invalidateChildren]; + } + [browser validateVisibleColumns]; } #pragma mark - @@ -318,15 +331,19 @@ // return suggestedWidth; //} +- (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column { + return NO; +} + #pragma mark - #pragma NSSplitViewDelegate - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex { - return 100; + return 120; } - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex { - return 260; + return 220; } #pragma mark - diff --git a/pithos-macos/PithosContainerNode.h b/pithos-macos/PithosContainerNode.h index a3591b8..ccc88f6 100644 --- a/pithos-macos/PithosContainerNode.h +++ b/pithos-macos/PithosContainerNode.h @@ -36,17 +36,15 @@ // or implied, of GRNET S.A. #import "PithosNode.h" -#import "ASIPithosContainer.h" +@class ASIPithosContainer; +@class ASIPithosContainerRequest; @interface PithosContainerNode : PithosNode { ASIPithosContainer *pithosContainer; NSMutableArray *objects; - - BOOL refreshing; - + ASIPithosContainerRequest *containerRequest; NSString *prefix; - NSString *childrenUpdatedNotificationName; } @@ -55,4 +53,6 @@ - (id)initWithContainerName:(NSString *)aContainerName; - (id)initWithContainerName:(NSString *)aContainerName icon:(NSImage *)anIcon; -@end +@property(retain) ASIPithosContainer *pithosContainer; + +@end \ No newline at end of file diff --git a/pithos-macos/PithosContainerNode.m b/pithos-macos/PithosContainerNode.m index 989ca1a..8e1679c 100644 --- a/pithos-macos/PithosContainerNode.m +++ b/pithos-macos/PithosContainerNode.m @@ -39,12 +39,14 @@ #import "PithosObjectNode.h" #import "PithosSubdirNode.h" #import "ASIPithosContainerRequest.h" +#import "ASIPithosContainer.h" #import "ASIPithosObject.h" #import "ASIDownloadCache.h" static NSImage *sharedIcon = nil; @implementation PithosContainerNode +@synthesize pithosContainer; + (void)initialize { if (self == [PithosContainerNode class]) @@ -57,7 +59,6 @@ static NSImage *sharedIcon = nil; - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon { if ((self = [super init])) { pithosContainer = [aPithosContainer retain]; - refreshing = NO; prefix = nil; icon = [anIcon retain]; childrenUpdatedNotificationName = @"PithosContainerNodeChildrenUpdated"; @@ -80,6 +81,8 @@ static NSImage *sharedIcon = nil; } - (void)dealloc { + [containerRequest clearDelegatesAndCancel]; + [containerRequest release]; [childrenUpdatedNotificationName release]; [prefix release]; [objects release]; @@ -90,7 +93,7 @@ static NSImage *sharedIcon = nil; #pragma mark - #pragma mark ASIHTTPRequestDelegate -- (void)requestFinished:(ASIPithosContainerRequest *)containerRequest { +- (void)requestFinished:(ASIHTTPRequest *)request { NSLog(@"URL: %@", [containerRequest url]); NSLog(@"cached: %d", [containerRequest didUseCachedResponse]); @@ -104,62 +107,78 @@ static NSImage *sharedIcon = nil; if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) { // Save new children NSLog(@"using newChildren"); - NSMutableArray *newChildren = [NSMutableArray array]; + newChildren = [[NSMutableArray alloc] init]; for (ASIPithosObject *object in objects) { if (object.subdir) { PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease]; if (children) { NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) + if (oldIndex != NSNotFound) { // Use the same pointer value, if possible node = [children objectAtIndex:oldIndex]; + node.pithosContainer = pithosContainer; + node.pithosObject = object; + } } - [newChildren addObject:node]; - } else { + [newChildren addObject:node]; + } else if (([self class] != [PithosSubdirNode class]) || (![((PithosSubdirNode *)self).pithosObject.name isEqualToString:object.name])) { + // XXX the if above removes false objects due to trailing slash + // XXX this will change in the server, but it is fixed in the client for now PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease]; if (children) { NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) + if (oldIndex != NSNotFound) { // Use the same pointer value, if possible node = [children objectAtIndex:oldIndex]; + node.pithosContainer = pithosContainer; + node.pithosObject = object; + } } [newChildren addObject:node]; } } - [children release]; - children = [newChildren retain]; } - // Else cache was used and all results were fetched during this request, so previousChildren can be reused + // Else cache was used and all results were fetched during this request, so existing children can be reused + [containerRequest release]; + containerRequest = nil; [objects release]; objects = nil; - // XXX sort then based on preferences - refreshing = NO; + // XXX sort children based on preferences + @synchronized(self) { + freshness = PithosNodeStateRefreshFinished; + } // Notify observers that children are updated [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self]; } else { + [containerRequest release]; // Do an additional request to fetch more objects - ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name - limit:0 - marker:[[someObjects lastObject] name] - prefix:prefix - delimiter:@"/" - path:nil - meta:nil - shared:NO - until:nil]; - newContainerRequest.delegate = self; - newContainerRequest.downloadCache = [ASIDownloadCache sharedCache]; - [newContainerRequest startAsynchronous]; + containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name + limit:0 + marker:[[someObjects lastObject] name] + prefix:prefix + delimiter:@"/" + path:nil + meta:nil + shared:NO + until:nil] retain]; + containerRequest.delegate = self; + containerRequest.downloadCache = [ASIDownloadCache sharedCache]; + [containerRequest startAsynchronous]; } } -- (void)requestFailed:(ASIPithosContainerRequest *)containerRequest { +- (void)requestFailed:(ASIHTTPRequest *)request { // XXX do something on error, cleanup NSLog(@"error:%@", [containerRequest error]); + [newChildren release]; + newChildren = nil; + [containerRequest release]; + containerRequest = nil; [objects release]; objects = nil; - childrenDirty = YES; - refreshing = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshNeeded; + } } #pragma mark - @@ -172,27 +191,39 @@ static NSImage *sharedIcon = nil; } - (NSArray *)children { - if (childrenDirty) { - @synchronized (self) { - if (!refreshing) { - refreshing = YES; - childrenDirty = NO; - ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name - limit:0 - marker:nil - prefix:prefix - delimiter:@"/" - path:nil - meta:nil - shared:NO - until:nil]; + @synchronized(self) { + switch (freshness) { + case PithosNodeStateFresh: + break; + case PithosNodeStateRefreshNeeded: + freshness = PithosNodeStateRefreshing; + containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name + limit:0 + marker:nil + prefix:prefix + delimiter:@"/" + path:nil + meta:nil + shared:NO + until:nil] retain]; containerRequest.delegate = self; containerRequest.downloadCache = [ASIDownloadCache sharedCache]; [containerRequest startAsynchronous]; - } + break; + case PithosNodeStateRefreshing: + break; + case PithosNodeStateRefreshFinished: + if (newChildren) { + [children release]; + children = newChildren; + newChildren = nil; + } + freshness = PithosNodeStateFresh; + default: + break; } + return children; } - return children; } - (NSString *)displayName { diff --git a/pithos-macos/PithosNode.h b/pithos-macos/PithosNode.h index 8889561..089a5d7 100644 --- a/pithos-macos/PithosNode.h +++ b/pithos-macos/PithosNode.h @@ -35,12 +35,18 @@ // interpreted as representing official policies, either expressed // or implied, of GRNET S.A. +#define PithosNodeStateFresh 0 +#define PithosNodeStateRefreshNeeded 1 +#define PithosNodeStateRefreshing 2 +#define PithosNodeStateRefreshFinished 3 + @interface PithosNode : NSObject { + NSInteger freshness; + NSString *url; NSMutableArray *children; - BOOL childrenDirty; - NSMutableArray *previousChildren; + NSMutableArray *newChildren; NSString *displayName; BOOL isLeafItem; @@ -60,5 +66,6 @@ @property(readonly) NSString *version; - (void)invalidateChildren; +- (void)invalidateChildrenRecursive; -@end +@end \ No newline at end of file diff --git a/pithos-macos/PithosNode.m b/pithos-macos/PithosNode.m index bc1cc4e..42c0567 100644 --- a/pithos-macos/PithosNode.m +++ b/pithos-macos/PithosNode.m @@ -46,7 +46,7 @@ - (id)init { if ((self == [super init])) { - childrenDirty = YES; + freshness = PithosNodeStateRefreshNeeded; isLeafItem = NO; } return self; @@ -55,17 +55,14 @@ - (void)dealloc { [icon release]; [displayName release]; - [previousChildren release]; + [newChildren release]; [children release]; [url release]; [super dealloc]; } - (BOOL)isEqual:(id)anObject { - if ([anObject isKindOfClass:[self class]]) { - return [((PithosNode *)anObject).url isEqual:self.url]; - } - return NO; + return ([anObject isKindOfClass:[self class]] && [((PithosNode *)anObject).url isEqual:self.url]); } - (NSUInteger)hash { @@ -73,9 +70,16 @@ } - (void)invalidateChildren { - childrenDirty = YES; - for (PithosNode *child in children) { - [child invalidateChildren]; + if (freshness == PithosNodeStateFresh) + freshness = PithosNodeStateRefreshNeeded; +} + +- (void)invalidateChildrenRecursive { + if (freshness == PithosNodeStateFresh) { + for (PithosNode *child in children) { + [child invalidateChildrenRecursive]; + } + freshness = PithosNodeStateRefreshNeeded; } } diff --git a/pithos-macos/PithosObjectNode.h b/pithos-macos/PithosObjectNode.h index f51f68d..d868d1a 100644 --- a/pithos-macos/PithosObjectNode.h +++ b/pithos-macos/PithosObjectNode.h @@ -36,8 +36,8 @@ // or implied, of GRNET S.A. #import "PithosNode.h" -#import "ASIPithosContainer.h" -#import "ASIPithosObject.h" +@class ASIPithosContainer; +@class ASIPithosObject; @interface PithosObjectNode : PithosNode { ASIPithosContainer *pithosContainer; @@ -46,4 +46,7 @@ - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer pithosObject:(ASIPithosObject *)aPithosObject; -@end +@property (retain) ASIPithosContainer *pithosContainer; +@property (retain) ASIPithosObject *pithosObject; + +@end \ No newline at end of file diff --git a/pithos-macos/PithosObjectNode.m b/pithos-macos/PithosObjectNode.m index 8cd4774..71df703 100644 --- a/pithos-macos/PithosObjectNode.m +++ b/pithos-macos/PithosObjectNode.m @@ -37,8 +37,11 @@ #import "PithosObjectNode.h" #import "ASIPithosRequest.h" +#import "ASIPithosContainer.h" +#import "ASIPithosObject.h" @implementation PithosObjectNode +@synthesize pithosContainer, pithosObject; #pragma mark - #pragma mark Object Lifecycle @@ -73,6 +76,7 @@ - (NSString *)displayName { if (displayName == nil) { + // XXX check if there are problems with . or other special characters displayName = [pithosObject.name lastPathComponent]; if([pithosObject.name hasSuffix:@"/"]) displayName = [displayName stringByAppendingString:@"/"]; diff --git a/pithos-macos/PithosSubdirNode.h b/pithos-macos/PithosSubdirNode.h index 89f7ef9..e62f6e3 100644 --- a/pithos-macos/PithosSubdirNode.h +++ b/pithos-macos/PithosSubdirNode.h @@ -36,7 +36,7 @@ // or implied, of GRNET S.A. #import "PithosContainerNode.h" -#import "ASIPithosObject.h" +@class ASIPithosObject; @interface PithosSubdirNode : PithosContainerNode { ASIPithosObject *pithosObject; @@ -44,4 +44,6 @@ - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer pithosObject:(ASIPithosObject *)aPithosObject; -@end +@property (retain) ASIPithosObject *pithosObject; + +@end \ No newline at end of file diff --git a/pithos-macos/PithosSubdirNode.m b/pithos-macos/PithosSubdirNode.m index 94bcec1..b96d7ff 100644 --- a/pithos-macos/PithosSubdirNode.m +++ b/pithos-macos/PithosSubdirNode.m @@ -37,10 +37,13 @@ #import "PithosSubdirNode.h" #import "ASIPithosRequest.h" +#import "ASIPithosContainer.h" +#import "ASIPithosObject.h" static NSImage *sharedIcon = nil; @implementation PithosSubdirNode +@synthesize pithosObject; + (void)initialize { if (self == [PithosSubdirNode class]) @@ -54,7 +57,6 @@ static NSImage *sharedIcon = nil; if ((self = [super init])) { pithosContainer = [aPithosContainer retain]; pithosObject = [aPithosObject retain]; - refreshing = NO; prefix = [[pithosObject.name substringToIndex:([pithosObject.name length] - 1)] retain]; childrenUpdatedNotificationName = @"PithosSubdirNodeChildrenUpdated"; } @@ -76,6 +78,7 @@ static NSImage *sharedIcon = nil; } - (NSString *)displayName { + // XXX check if there are problems with . or other special characters return [pithosObject.name lastPathComponent]; } -- 1.7.10.4