aac59b7a6da472b27b39728e2af452ead6aabd44
[pithos-macos] / pithos-macos / PithosContainerNode.m
1 //
2 //  PithosNode.m
3 //  pithos-macos
4 //
5 // Copyright 2011 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
15 //   2. Redistributions in binary form must reproduce the above
16 //      copyright notice, this list of conditions and the following
17 //      disclaimer in the documentation and/or other materials
18 //      provided with the distribution.
19 // 
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 // 
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
37
38 #import "PithosContainerNode.h"
39 #import "PithosObjectNode.h"
40 #import "PithosSubdirNode.h"
41 #import "ASIPithosContainerRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASIDownloadCache.h"
45
46 static NSImage *sharedIcon = nil;
47
48 @implementation PithosContainerNode
49 @synthesize pithosContainer;
50
51 + (void)initialize {
52         if (self == [PithosContainerNode class])
53         sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericHardDiskIcon)] retain];
54 }
55
56 #pragma mark -
57 #pragma mark Object Lifecycle
58
59 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon {
60     if ((self = [super init])) {
61         pithosContainer = [aPithosContainer retain];
62         prefix = nil;
63         icon = [anIcon retain];
64         childrenUpdatedNotificationName = @"PithosContainerNodeChildrenUpdated";
65     }
66     return self;
67 }
68
69 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer {
70     return [self initWithPithosContainer:aPithosContainer icon:nil];
71 }
72
73 - (id)initWithContainerName:(NSString *)aContainerName icon:(NSImage *)anIcon {
74     ASIPithosContainer *container = [ASIPithosContainer container];
75     container.name = aContainerName;
76     return [self initWithPithosContainer:container icon:anIcon];
77 }
78
79 - (id)initWithContainerName:(NSString *)aContainerName {
80     return [self initWithContainerName:aContainerName icon:nil];
81 }
82
83 - (void)dealloc {
84     [containerRequest clearDelegatesAndCancel];
85     [containerRequest release];
86     [childrenUpdatedNotificationName release];
87     [prefix release];
88     [objects release];
89     [pithosContainer release];
90     [super dealloc];
91 }
92
93 #pragma mark -
94 #pragma mark ASIHTTPRequestDelegate
95
96 - (void)requestFinished:(ASIHTTPRequest *)request {
97     NSLog(@"URL: %@", [containerRequest url]);
98     NSLog(@"cached: %d", [containerRequest didUseCachedResponse]);
99
100     NSArray *someObjects = [containerRequest objects];
101     if (objects == nil) {
102         objects = [[NSMutableArray alloc] initWithArray:someObjects];
103     } else {
104         [objects addObjectsFromArray:someObjects];
105     }
106     if ([someObjects count] < 10000) {
107         if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
108             // Save new children
109             NSLog(@"using newChildren");
110             newChildren = [[NSMutableArray alloc] init];
111             for (ASIPithosObject *object in objects) {
112                 if (object.subdir) {
113                     PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
114                     if (children) {
115                         NSUInteger oldIndex = [children indexOfObject:node];
116                         if (oldIndex != NSNotFound) {
117                             // Use the same pointer value, if possible
118                             node = [children objectAtIndex:oldIndex];
119                             node.pithosContainer = pithosContainer;
120                             node.pithosObject = object;
121                         }
122                     }
123                     [newChildren addObject:node];
124                 } else if (([self class] != [PithosSubdirNode class]) || (![((PithosSubdirNode *)self).pithosObject.name isEqualToString:object.name])) {
125                     // XXX the if above removes false objects due to trailing slash
126                     // XXX this will change in the server, but it is fixed in the client for now
127                     PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
128                     if (children) {
129                         NSUInteger oldIndex = [children indexOfObject:node];
130                         if (oldIndex != NSNotFound) {
131                             // Use the same pointer value, if possible
132                             node = [children objectAtIndex:oldIndex];
133                             node.pithosContainer = pithosContainer;
134                             node.pithosObject = object;
135                         }
136                     }
137                     [newChildren addObject:node];                                
138                 }
139             }
140         }
141         // Else cache was used and all results were fetched during this request, so existing children can be reused
142         [containerRequest release];
143         containerRequest = nil;
144         [objects release];
145         objects = nil;
146         // XXX sort children based on preferences
147         @synchronized(self) {
148             freshness = PithosNodeStateRefreshFinished;
149         }
150         // Notify observers that children are updated
151         [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
152     } else {
153         [containerRequest release];
154         // Do an additional request to fetch more objects
155         containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
156                                                                                      limit:0 
157                                                                                     marker:[[someObjects lastObject] name] 
158                                                                                     prefix:prefix 
159                                                                                  delimiter:@"/" 
160                                                                                       path:nil 
161                                                                                       meta:nil 
162                                                                                     shared:NO 
163                                                                                      until:nil] retain];
164         containerRequest.delegate = self;
165         containerRequest.downloadCache = [ASIDownloadCache sharedCache];
166         [containerRequest startAsynchronous];
167     }
168 }
169
170 - (void)requestFailed:(ASIHTTPRequest *)request {
171     // XXX do something on error, cleanup
172     NSLog(@"error:%@", [containerRequest error]);
173     [newChildren release];
174     newChildren = nil;
175     [containerRequest release];
176     containerRequest = nil;
177     [objects release];
178     objects = nil;
179     @synchronized(self) {
180         freshness = PithosNodeStateRefreshNeeded;
181     }
182 }
183
184 #pragma mark -
185 #pragma mark Properties
186
187 - (NSString *)url {
188     if (url == nil) 
189         url = [[NSString alloc] initWithFormat:@"%@/%@", [ASIPithosRequest storageURL], pithosContainer.name];
190     return url;
191 }
192
193 - (NSArray *)children {
194     @synchronized(self) {
195         switch (freshness) {
196             case PithosNodeStateFresh:
197                 break;
198             case PithosNodeStateRefreshNeeded:
199                 freshness = PithosNodeStateRefreshing;
200                 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
201                                                                                              limit:0 
202                                                                                             marker:nil 
203                                                                                             prefix:prefix 
204                                                                                          delimiter:@"/" 
205                                                                                               path:nil 
206                                                                                               meta:nil 
207                                                                                             shared:NO 
208                                                                                              until:nil] retain];
209                 containerRequest.delegate = self;
210                 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
211                 [containerRequest startAsynchronous];
212                 break;
213             case PithosNodeStateRefreshing:
214                 break;
215             case PithosNodeStateRefreshFinished:
216                 if (newChildren) {
217                     [children release];
218                     children = newChildren;
219                     newChildren = nil;
220                 }
221                 freshness = PithosNodeStateFresh;
222             default:
223                 break;
224         }
225         return children;
226     }
227 }
228
229 - (NSString *)displayName {
230     return [[pithosContainer.name copy] autorelease];
231 }
232
233 - (NSImage *)icon {
234     if (icon)
235         return icon;
236     return sharedIcon;
237 }
238
239 - (void)setIcon:(NSImage *)anIcon {
240     [icon release];
241     icon = [anIcon retain];
242 }
243
244 @end