Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosContainerNode.m @ 9ab4b378

History | View | Annotate | Download (19.2 kB)

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 "PithosUtilities.h"
46
#import "PithosContainerNodeInfoController.h"
47

    
48
static NSImage *sharedIcon = nil;
49

    
50
@implementation PithosContainerNode
51
@synthesize pithosContainer, prefix;
52
@synthesize policyVersioning, policyQuota;
53

    
54
+ (void)initialize {
55
	if (self == [PithosContainerNode class])
56
        sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericHardDiskIcon)] retain];
57
}
58

    
59
#pragma mark -
60
#pragma mark Object Lifecycle
61

    
62
- (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon {
63
    if ((self = [super init])) {
64
        self.pithosContainer = aPithosContainer;
65
        prefix = nil;
66
        self.icon = anIcon;
67
        childrenUpdatedNotificationName = @"PithosContainerNodeChildrenUpdated";
68
    }
69
    return self;
70
}
71

    
72
- (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer {
73
    return [self initWithPithosContainer:aPithosContainer icon:nil];
74
}
75

    
76
- (id)initWithContainerName:(NSString *)aContainerName icon:(NSImage *)anIcon {
77
    ASIPithosContainer *container = [ASIPithosContainer container];
78
    container.name = aContainerName;
79
    return [self initWithPithosContainer:container icon:anIcon];
80
}
81

    
82
- (id)initWithContainerName:(NSString *)aContainerName {
83
    return [self initWithContainerName:aContainerName icon:nil];
84
}
85

    
86
- (void)dealloc {
87
    [containerRequest clearDelegatesAndCancel];
88
    [containerRequest release];
89
    [refreshMetadataContainerRequest clearDelegatesAndCancel];
90
    [refreshMetadataContainerRequest release];
91
    [applyMetadataContainerRequest clearDelegatesAndCancel];
92
    [applyMetadataContainerRequest release];
93
    [policyQuota release];
94
    [policyVersioning release];
95
    [childrenUpdatedNotificationName release];
96
    [prefix release];
97
    [objects release];
98
    [pithosContainer release];
99
    [super dealloc];
100
}
101

    
102
#pragma mark -
103
#pragma mark Properties
104

    
105
- (NSString *)url {
106
    if (url == nil)
107
        url = [[NSString alloc] initWithFormat:@"%@/%@%@", 
108
               (sharingAccount ? [ASIPithosRequest storageURLWithAuthUser:sharingAccount] : [ASIPithosRequest storageURL]), 
109
               pithosContainer.name, 
110
               (shared ? @"?shared" : @"")];
111
    return url;
112
}
113

    
114
- (NSArray *)children {
115
    @synchronized(self) {
116
        switch (freshness) {
117
            case PithosNodeStateFresh:
118
                break;
119
            case PithosNodeStateRefreshNeeded:
120
                freshness = PithosNodeStateRefreshing;
121
                containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
122
                                                                                             limit:0 
123
                                                                                            marker:nil 
124
                                                                                            prefix:prefix 
125
                                                                                         delimiter:@"/" 
126
                                                                                              path:nil 
127
                                                                                              meta:nil 
128
                                                                                            shared:shared 
129
                                                                                             until:nil] retain];
130
                if (sharingAccount)
131
                    [containerRequest setRequestUserFromDefaultTo:sharingAccount];
132
                containerRequest.delegate = self;
133
                containerRequest.didFinishSelector = @selector(containerRequestFinished:);
134
                containerRequest.didFailSelector = @selector(containerRequestFailed:);
135
                if (!forcedRefresh)
136
                    containerRequest.downloadCache = [ASIDownloadCache sharedCache];
137
                [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
138
                break;
139
            case PithosNodeStateRefreshing:
140
                break;
141
            case PithosNodeStateRefreshFinished:
142
                if (newChildren) {
143
                    [children release];
144
                    children = newChildren;
145
                    newChildren = nil;
146
                }
147
                freshness = PithosNodeStateFresh;
148
            default:
149
                break;
150
        }
151
        return children;
152
    }
153
}
154

    
155
- (NSString *)displayName {
156
    return [[pithosContainer.name copy] autorelease];
157
}
158

    
159
- (void)setDisplayName:(NSString *)aDisplayName {
160
}
161

    
162
- (NSImage *)icon {
163
    if (icon == nil) {
164
        if ([pithosContainer.name isEqualToString:@"pithos"])
165
            icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)] retain];
166
        else if ([pithosContainer.name isEqualToString:@"trash"])
167
            icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)] retain];
168
        else
169
            icon = [sharedIcon retain];
170
    }
171
    return icon;
172
}
173

    
174
- (void)setPithosContainer:(ASIPithosContainer *)aPithosContainer {
175
    if (![pithosContainer isEqualTo:aPithosContainer]) {
176
        [pithosContainer release];
177
        pithosContainer = [aPithosContainer retain];
178
    }
179
    if (pithosContainer.policy) {
180
        self.policyVersioning = [pithosContainer.policy objectForKey:@"versioning"];
181
        self.policyQuota = [NSNumber numberWithLongLong:[[pithosContainer.policy objectForKey:@"quota"] longLongValue]];
182
    } else {
183
        self.policyVersioning = @"manual";
184
        self.policyQuota = [NSNumber numberWithLongLong:0];
185
    }
186
}
187

    
188
#pragma mark -
189
#pragma mark ASIHTTPRequestDelegate
190

    
191
- (void)containerRequestFinished:(ASIPithosContainerRequest *)request {
192
    NSLog(@"URL: %@", [containerRequest url]);
193
    NSLog(@"cached: %d", [containerRequest didUseCachedResponse]);
194
    
195
    if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) {
196
        pithosContainer.blockHash = [containerRequest blockHash];
197
        pithosContainer.blockSize = [containerRequest blockSize];
198
    }
199
    
200
    NSArray *someObjects = [containerRequest objects];
201
    if (objects == nil) {
202
        objects = [[NSMutableArray alloc] initWithArray:someObjects];
203
    } else {
204
        [objects addObjectsFromArray:someObjects];
205
    }
206
    if ([someObjects count] < 10000) {
207
        if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
208
            // Save new children
209
            NSLog(@"using newChildren");
210
            newChildren = [[NSMutableArray alloc] init];
211
            NSArray *objectNames = [objects valueForKey:@"name"];
212
            NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
213
            BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]);
214
            for (ASIPithosObject *object in objects) {
215
                if (!isSubdirNode || 
216
                    [object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]]) {
217
                    // The check above removes false objects due to trailing slash or same prefix
218
                    if (object.subdir) {
219
                        NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
220
                        if ((sameNameObjectIndex == NSNotFound) || 
221
                            ![PithosUtilities isContentTypeDirectory:[[objects objectAtIndex:sameNameObjectIndex] contentType]]) {
222
                            PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
223
                            node.parent = self;
224
                            node.shared = shared;
225
                            node.sharingAccount = sharingAccount;
226
                            if (children) {
227
                                NSUInteger oldIndex = [children indexOfObject:node];
228
                                if (oldIndex != NSNotFound) {
229
                                    // Use the same pointer value, if possible
230
                                    node = [children objectAtIndex:oldIndex];
231
                                    node.pithosContainer = pithosContainer;
232
                                    node.pithosObject = object;
233
                                    [keptNodes addIndex:oldIndex];
234
                                }
235
                            }
236
                            if (sharingAccount)
237
                                node.pithosObject.allowedTo = [NSString stringWithString:@"read"];
238
                            [newChildren addObject:node];
239
                        }
240
                    } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
241
                        PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
242
                        node.parent = self;
243
                        node.shared = shared;
244
                        node.sharingAccount = sharingAccount;
245
                        if (children) {
246
                            NSUInteger oldIndex = [children indexOfObject:node];
247
                            if (oldIndex != NSNotFound) {
248
                                // Use the same pointer value, if possible
249
                                node = [children objectAtIndex:oldIndex];
250
                                node.pithosContainer = pithosContainer;
251
                                node.pithosObject = object;
252
                                [keptNodes addIndex:oldIndex];
253
                            }
254
                        }
255
                        [newChildren addObject:node];
256
                    } else {
257
                        PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithosContainer:pithosContainer pithosObject:object] autorelease];
258
                        node.parent = self;
259
                        node.shared = shared;
260
                        node.sharingAccount = sharingAccount;
261
                        if (children) {
262
                            NSUInteger oldIndex = [children indexOfObject:node];
263
                            if (oldIndex != NSNotFound) {
264
                                // Use the same pointer value, if possible
265
                                node = [children objectAtIndex:oldIndex];
266
                                node.pithosContainer = pithosContainer;
267
                                node.pithosObject = object;
268
                                [keptNodes addIndex:oldIndex];
269
                            }
270
                        }
271
                        [newChildren addObject:node];                                
272
                    }
273
                }
274
            }
275
            [[children objectsAtIndexes:
276
              [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
277
                if ([keptNodes containsIndex:idx])
278
                    return NO;
279
                return YES;
280
            }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
281
        }
282
        // Else cache was used and all results were fetched during this request, so existing children can be reused
283
        [containerRequest release];
284
        containerRequest = nil;
285
        [objects release];
286
        objects = nil;
287
        forcedRefresh = NO;
288
        @synchronized(self) {
289
            freshness = PithosNodeStateRefreshFinished;
290
        }
291
        // Notify observers that children are updated
292
        [[NSNotificationCenter defaultCenter] postNotificationName:childrenUpdatedNotificationName object:self];
293
    } else {
294
        [containerRequest release];
295
        // Do an additional request to fetch more objects
296
        containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithContainerName:pithosContainer.name 
297
                                                                                     limit:0 
298
                                                                                    marker:[[someObjects lastObject] name] 
299
                                                                                    prefix:prefix 
300
                                                                                 delimiter:@"/" 
301
                                                                                      path:nil 
302
                                                                                      meta:nil 
303
                                                                                    shared:shared 
304
                                                                                     until:nil] retain];
305
        if (sharingAccount)
306
            [containerRequest setRequestUserFromDefaultTo:sharingAccount];
307
        containerRequest.delegate = self;
308
        if (!forcedRefresh)
309
        containerRequest.downloadCache = [ASIDownloadCache sharedCache];
310
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
311
    }
312
}
313

    
314
- (void)containerRequestFailed:(ASIPithosContainerRequest *)request {
315
    [PithosUtilities httpRequestErrorAlertWithRequest:request];
316
    [newChildren release];
317
    newChildren = nil;
318
    [containerRequest release];
319
    containerRequest = nil;
320
    [objects release];
321
    objects = nil;
322
    forcedRefresh = NO;
323
    @synchronized(self) {
324
        freshness = PithosNodeStateRefreshNeeded;
325
    }
326
}
327

    
328
- (void)containerMetadataRequestFinished:(ASIPithosContainerRequest *)request {
329
    NSLog(@"URL: %@", [request url]);
330
    NSLog(@"cached: %d", [request didUseCachedResponse]);
331
    
332
    if ([request isEqualTo:applyMetadataContainerRequest]) {
333
        @synchronized(self) {
334
            [applyMetadataContainerRequest release];
335
            applyMetadataContainerRequest = nil;
336
        }
337
        [self refreshInfo];
338
    } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
339
        [[pithosNodeInfoController window] makeFirstResponder:nil];
340
        self.pithosContainer = [refreshMetadataContainerRequest container];
341
        @synchronized(self) {
342
            [refreshMetadataContainerRequest release];
343
            refreshMetadataContainerRequest = nil;
344
        }
345
    }
346
}
347

    
348
- (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request {
349
    if ([request isEqualTo:applyMetadataContainerRequest]) {
350
        [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest];
351
        @synchronized(self) {
352
            [applyMetadataContainerRequest release];
353
            applyMetadataContainerRequest = nil;
354
        }
355
    } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
356
        [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest];
357
        @synchronized(self) {
358
            [refreshMetadataContainerRequest release];
359
            refreshMetadataContainerRequest = nil;
360
        }
361
    }
362
}
363

    
364
#pragma mark -
365
#pragma mark Info
366

    
367
- (void)applyInfo {
368
    @synchronized(self) {
369
        if (applyMetadataContainerRequest == nil) {
370
            [[pithosNodeInfoController window] makeFirstResponder:nil];
371
            applyMetadataContainerRequest = [[ASIPithosContainerRequest updateContainerMetadataRequestWithContainerName:pithosContainer.name 
372
                                                                                                                 policy:[NSDictionary dictionaryWithObjectsAndKeys:
373
                                                                                                                         policyVersioning, @"versioning", 
374
                                                                                                                         [policyQuota stringValue], @"quota", 
375
                                                                                                                         nil] 
376
                                                                                                               metadata:pithosContainer.metadata 
377
                                                                                                                 update:NO] retain];
378
            applyMetadataContainerRequest.delegate = self;
379
            applyMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
380
            applyMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
381
            [[PithosUtilities prepareRequest:applyMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
382
        }
383
    }
384
}
385

    
386
- (void)refreshInfo {
387
    @synchronized(self) {
388
        if (refreshMetadataContainerRequest == nil) {
389
            refreshMetadataContainerRequest = [[ASIPithosContainerRequest containerMetadataRequestWithContainerName:pithosContainer.name] retain];
390
            refreshMetadataContainerRequest.delegate = self;
391
            refreshMetadataContainerRequest.didFinishSelector = @selector(containerMetadataRequestFinished:);
392
            refreshMetadataContainerRequest.didFailSelector = @selector(containerMetadataRequestFailed:);
393
            refreshMetadataContainerRequest.downloadCache = [ASIDownloadCache sharedCache];
394
            [[PithosUtilities prepareRequest:refreshMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
395
        }
396
    }
397
}
398

    
399
#pragma mark -
400
#pragma mark Actions
401

    
402
- (void)showPithosNodeInfo:(id)sender {
403
    if (!pithosNodeInfoController)
404
        pithosNodeInfoController = [[PithosContainerNodeInfoController alloc] initWithPithosNode:self];
405
    [pithosNodeInfoController showWindow:sender];
406
    [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
407
    [NSApp activateIgnoringOtherApps:YES];
408
}
409

    
410
@end