1e48d1e0adbc57021b366ddd4012c4abcf7589ae
[pithos-macos] / pithos-macos / PithosContainerNode.m
1 //
2 //  PithosContainerNode.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 #import "PithosFileUtilities.h"
46
47 static NSImage *sharedIcon = nil;
48
49 @implementation PithosContainerNode
50 @synthesize pithosContainer, prefix;
51
52 + (void)initialize {
53         if (self == [PithosContainerNode class])
54         sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericHardDiskIcon)] retain];
55 }
56
57 #pragma mark -
58 #pragma mark Object Lifecycle
59
60 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon {
61     if ((self = [super init])) {
62         self.pithosContainer = aPithosContainer;
63         prefix = nil;
64         icon = [anIcon retain];
65         childrenUpdatedNotificationName = @"PithosContainerNodeChildrenUpdated";
66     }
67     return self;
68 }
69
70 - (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer {
71     return [self initWithPithosContainer:aPithosContainer icon:nil];
72 }
73
74 - (id)initWithContainerName:(NSString *)aContainerName icon:(NSImage *)anIcon {
75     ASIPithosContainer *container = [ASIPithosContainer container];
76     container.name = aContainerName;
77     return [self initWithPithosContainer:container icon:anIcon];
78 }
79
80 - (id)initWithContainerName:(NSString *)aContainerName {
81     return [self initWithContainerName:aContainerName icon:nil];
82 }
83
84 - (void)dealloc {
85     [containerRequest clearDelegatesAndCancel];
86     [containerRequest release];
87     [childrenUpdatedNotificationName release];
88     [prefix release];
89     [objects release];
90     [pithosContainer release];
91     [super dealloc];
92 }
93
94 #pragma mark -
95 #pragma mark ASIHTTPRequestDelegate
96
97 - (void)containerRequestFinished:(ASIPithosContainerRequest *)request {
98     NSLog(@"URL: %@", [containerRequest url]);
99     NSLog(@"cached: %d", [containerRequest didUseCachedResponse]);
100     
101     if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) {
102         pithosContainer.blockHash = [containerRequest blockHash];
103         pithosContainer.blockSize = [containerRequest blockSize];
104     }
105
106     NSArray *someObjects = [containerRequest objects];
107     if (objects == nil) {
108         objects = [[NSMutableArray alloc] initWithArray:someObjects];
109     } else {
110         [objects addObjectsFromArray:someObjects];
111     }
112     if ([someObjects count] < 10000) {
113         if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
114             // Save new children
115             NSLog(@"using newChildren");
116             newChildren = [[NSMutableArray alloc] init];
117             NSArray *objectNames = [objects valueForKey:@"name"];
118             NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
119             BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]);
120             for (ASIPithosObject *object in objects) {
121                 if (!isSubdirNode || 
122                     [object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]]) {
123                     // The check above removes false objects due to trailing slash or same prefix
124                     if (object.subdir) {
125                         NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
126                         if ((sameNameObjectIndex == NSNotFound) ||
127                             ![[[objects objectAtIndex:sameNameObjectIndex] contentType] isEqualToString:@"application/directory"]) {
128                             PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
129                             node.parent = self;
130                             if (children) {
131                                 NSUInteger oldIndex = [children indexOfObject:node];
132                                 if (oldIndex != NSNotFound) {
133                                     // Use the same pointer value, if possible
134                                     node = [children objectAtIndex:oldIndex];
135                                     node.pithosContainer = pithosContainer;
136                                     node.pithosObject = object;
137                                     [keptNodes addIndex:oldIndex];
138                                 }
139                             }
140                             [newChildren addObject:node];
141                         }
142                     } else if ([object.contentType isEqualToString:@"application/directory"]) {
143                         PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
144                         node.parent = self;
145                         if (children) {
146                             NSUInteger oldIndex = [children indexOfObject:node];
147                             if (oldIndex != NSNotFound) {
148                                 // Use the same pointer value, if possible
149                                 node = [children objectAtIndex:oldIndex];
150                                 node.pithosContainer = pithosContainer;
151                                 node.pithosObject = object;
152                                 [keptNodes addIndex:oldIndex];
153                             }
154                         }
155                         [newChildren addObject:node];
156                     } else {
157                         PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
158                         node.parent = self;
159                         if (children) {
160                             NSUInteger oldIndex = [children indexOfObject:node];
161                             if (oldIndex != NSNotFound) {
162                                 // Use the same pointer value, if possible
163                                 node = [children objectAtIndex:oldIndex];
164                                 node.pithosContainer = pithosContainer;
165                                 node.pithosObject = object;
166                                 [keptNodes addIndex:oldIndex];
167                             }
168                         }
169                         [newChildren addObject:node];                                
170                     }
171                 }
172             }
173             [[children objectsAtIndexes:
174               [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
175                 if ([keptNodes containsIndex:idx])
176                     return NO;
177                 return YES;
178             }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
179         }
180         // Else cache was used and all results were fetched during this request, so existing children can be reused
181         [containerRequest release];
182         containerRequest = nil;
183         [objects release];
184         objects = nil;
185         @synchronized(self) {
186             freshness = PithosNodeStateRefreshFinished;
187         }
188         // Notify observers that children are updated
189         [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
190     } else {
191         [containerRequest release];
192         // Do an additional request to fetch more objects
193         containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
194                                                                                      limit:0 
195                                                                                     marker:[[someObjects lastObject] name] 
196                                                                                     prefix:prefix 
197                                                                                  delimiter:@"/" 
198                                                                                       path:nil 
199                                                                                       meta:nil 
200                                                                                     shared:NO 
201                                                                                      until:nil] retain];
202         containerRequest.delegate = self;
203         containerRequest.downloadCache = [ASIDownloadCache sharedCache];
204         [containerRequest startAsynchronous];
205     }
206 }
207
208 - (void)containerRequestFailed:(ASIPithosContainerRequest *)request {
209     [PithosFileUtilities httpRequestErrorAlertWithRequest:request];
210     [newChildren release];
211     newChildren = nil;
212     [containerRequest release];
213     containerRequest = nil;
214     [objects release];
215     objects = nil;
216     @synchronized(self) {
217         freshness = PithosNodeStateRefreshNeeded;
218     }
219 }
220
221 #pragma mark -
222 #pragma mark Properties
223
224 - (NSString *)url {
225     if (url == nil) 
226         url = [[NSString alloc] initWithFormat:@"%@/%@", [ASIPithosRequest storageURL], pithosContainer.name];
227     return url;
228 }
229
230 - (NSArray *)children {
231     @synchronized(self) {
232         switch (freshness) {
233             case PithosNodeStateFresh:
234                 break;
235             case PithosNodeStateRefreshNeeded:
236                 freshness = PithosNodeStateRefreshing;
237                 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
238                                                                                              limit:0 
239                                                                                             marker:nil 
240                                                                                             prefix:prefix 
241                                                                                          delimiter:@"/" 
242                                                                                               path:nil 
243                                                                                               meta:nil 
244                                                                                             shared:NO 
245                                                                                              until:nil] retain];
246                 containerRequest.delegate = self;
247                 containerRequest.didFinishSelector = @selector(containerRequestFinished:);
248                 containerRequest.didFailSelector = @selector(containerRequestFailed:);
249                 containerRequest.downloadCache = [ASIDownloadCache sharedCache];
250                 [containerRequest startAsynchronous];
251                 break;
252             case PithosNodeStateRefreshing:
253                 break;
254             case PithosNodeStateRefreshFinished:
255                 if (newChildren) {
256                     [children release];
257                     children = newChildren;
258                     newChildren = nil;
259                 }
260                 freshness = PithosNodeStateFresh;
261             default:
262                 break;
263         }
264         return children;
265     }
266 }
267
268 - (NSString *)displayName {
269     return [[pithosContainer.name copy] autorelease];
270 }
271
272 - (NSImage *)icon {
273     if (icon)
274         return icon;
275     return sharedIcon;
276 }
277
278 - (void)setIcon:(NSImage *)anIcon {
279     [icon release];
280     icon = [anIcon retain];
281 }
282
283 @end