mirror of
https://github.com/zhigang1992/transshift.git
synced 2026-05-27 07:52:21 +08:00
475 lines
9.8 KiB
Objective-C
475 lines
9.8 KiB
Objective-C
//
|
|
// FSDirectory.m
|
|
// TransmissionRPCClient
|
|
//
|
|
// File System Directory data class
|
|
// holds file/directory tree
|
|
// using for representation on UITableView
|
|
|
|
#import "FSDirectory.h"
|
|
#import "TRFileInfo.h"
|
|
|
|
#define PATH_SPLITTER_STRING @"/"
|
|
|
|
@interface FSItem()
|
|
|
|
- (void)setNeedToRecalcStats;
|
|
|
|
@end
|
|
|
|
@implementation FSItem
|
|
|
|
{
|
|
int _filesCount;
|
|
long long _filesSize;
|
|
long long _filesDownloadedSize;
|
|
|
|
BOOL _allFilesWanted; // flag indicates that all files withing this folder is wanted
|
|
|
|
BOOL _needToCalcStats;
|
|
}
|
|
|
|
+ (FSItem *)itemWithName:(NSString *)name andType:(FSItemType)itemType
|
|
{
|
|
FSItem *item = [[FSItem alloc] init];
|
|
|
|
item.itemType = itemType;
|
|
item.name = name;
|
|
|
|
return item;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
self = [super init];
|
|
|
|
if( !self )
|
|
return self;
|
|
|
|
_level = 0;
|
|
_needToCalcStats = YES;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setInfo:(TRFileInfo *)info
|
|
{
|
|
_info = info;
|
|
_needToCalcStats = YES;
|
|
}
|
|
|
|
- (NSString *)folderSizeString
|
|
{
|
|
NSByteCountFormatter *formatter = [[NSByteCountFormatter alloc] init];
|
|
formatter.allowsNonnumericFormatting = NO;
|
|
return [formatter stringFromByteCount:self.folderSize];
|
|
}
|
|
|
|
- (NSString *)folderDownloadedString
|
|
{
|
|
NSByteCountFormatter *formatter = [[NSByteCountFormatter alloc] init];
|
|
formatter.allowsNonnumericFormatting = NO;
|
|
return [formatter stringFromByteCount:self.folderDownloadedSize];
|
|
}
|
|
|
|
- (float)folderDownloadProgress
|
|
{
|
|
[self calcStats];
|
|
|
|
if( _filesSize == 0 )
|
|
@throw [NSException exceptionWithName:@"FSItem" reason:@"folderDownloadProgress: _fileSize == 0, devide by ZERO" userInfo:nil];
|
|
|
|
return (float)((double)_filesDownloadedSize/(double)_filesSize);
|
|
}
|
|
|
|
- (NSString *)folderDownloadProgressString
|
|
{
|
|
return [NSString stringWithFormat:@"%03.2f%%", self.folderDownloadProgress * 100.0f ];
|
|
}
|
|
|
|
- (int)filesCount
|
|
{
|
|
[self calcStats];
|
|
return _filesCount;
|
|
}
|
|
|
|
- (long long)folderSize
|
|
{
|
|
[self calcStats];
|
|
return _filesSize;
|
|
}
|
|
|
|
- (long long)folderDownloadedSize
|
|
{
|
|
[self calcStats];
|
|
return _filesDownloadedSize;
|
|
}
|
|
|
|
- (void)calcStats
|
|
{
|
|
if( _needToCalcStats )
|
|
{
|
|
_filesCount = 0;
|
|
_filesSize = 0;
|
|
_filesDownloadedSize = 0;
|
|
_subfoldersCount = 0;
|
|
_allFilesWanted = YES;
|
|
_needToCalcStats = NO;
|
|
|
|
[self traverseAndCount:self];
|
|
}
|
|
}
|
|
|
|
- (void)setNeedToRecalcStats
|
|
{
|
|
_needToCalcStats = YES;
|
|
|
|
if( _items )
|
|
for( FSItem *i in _items )
|
|
[i setNeedToRecalcStats];
|
|
}
|
|
|
|
- (void)traverseAndCount:(FSItem*)item
|
|
{
|
|
if( item.items )
|
|
{
|
|
for( FSItem *i in item.items )
|
|
{
|
|
if( i.isFile )
|
|
{
|
|
_filesCount++;
|
|
|
|
if( i.info )
|
|
{
|
|
_filesSize += i.info.length;
|
|
_filesDownloadedSize += i.info.bytesComplited;
|
|
|
|
if( !i.info.wanted )
|
|
_allFilesWanted = NO;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_subfoldersCount++;
|
|
[self traverseAndCount:i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setIsAllFilesWanted:(BOOL)isAllFilesWanted
|
|
{
|
|
[self setNeedToRecalcStats];
|
|
[self traverse:self setWanted:isAllFilesWanted];
|
|
}
|
|
|
|
- (void)traverse:(FSItem*)item setWanted:(BOOL)wanted
|
|
{
|
|
if( item.items )
|
|
{
|
|
for( FSItem *i in item.items )
|
|
{
|
|
if( i.isFile )
|
|
{
|
|
if( i.info )
|
|
{
|
|
i.info.wanted = wanted;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[self traverse:i setWanted:wanted];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSArray*)fileIndexesUnwanted
|
|
{
|
|
NSMutableArray *indexes = [NSMutableArray array];
|
|
|
|
if (_items)
|
|
{
|
|
for( FSItem *i in _items )
|
|
{
|
|
if( i.isFile && !i.info.wanted )
|
|
{
|
|
[indexes addObject:@(i.index)];
|
|
}
|
|
else
|
|
{
|
|
[indexes addObjectsFromArray:i.fileIndexesUnwanted];
|
|
}
|
|
}
|
|
}
|
|
|
|
return indexes;
|
|
}
|
|
|
|
|
|
- (NSArray*)fileIndexesWanted
|
|
{
|
|
NSMutableArray *indexes = [NSMutableArray array];
|
|
|
|
if (_items)
|
|
{
|
|
for( FSItem *i in _items )
|
|
{
|
|
if( i.isFile && i.info.wanted )
|
|
{
|
|
[indexes addObject:@(i.index)];
|
|
}
|
|
else
|
|
{
|
|
[indexes addObjectsFromArray:i.fileIndexesWanted];
|
|
}
|
|
}
|
|
}
|
|
|
|
return indexes;
|
|
}
|
|
|
|
- (NSArray *)fileIndexes
|
|
{
|
|
NSMutableArray *indexes = [NSMutableArray array];
|
|
|
|
if (_items)
|
|
{
|
|
for( FSItem *i in _items )
|
|
{
|
|
if( i.isFile )
|
|
{
|
|
[indexes addObject:@(i.index)];
|
|
}
|
|
else
|
|
{
|
|
[indexes addObjectsFromArray:i.fileIndexes];
|
|
}
|
|
}
|
|
}
|
|
|
|
return indexes;
|
|
}
|
|
|
|
- (BOOL)isFolder
|
|
{
|
|
return _itemType == FSItemTypeFolder;
|
|
}
|
|
|
|
- (BOOL)isFile
|
|
{
|
|
return _itemType == FSItemTypeFile;
|
|
}
|
|
|
|
- (BOOL)isAllFilesWanted
|
|
{
|
|
[self calcStats];
|
|
return _allFilesWanted;
|
|
}
|
|
|
|
|
|
// add item to children, if it is already exists
|
|
// return existing item
|
|
|
|
- (FSItem *)addItemWithName:(NSString *)name ofType:(FSItemType)itemType
|
|
{
|
|
// lazy instanciating
|
|
if( !_items )
|
|
_items = [NSMutableArray array];
|
|
|
|
FSItem *resItem = nil;
|
|
|
|
// finding item
|
|
for( FSItem* item in _items )
|
|
{
|
|
if (item.itemType == itemType && [item.name isEqualToString:name])
|
|
{
|
|
resItem = item;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// item not found add it to the children
|
|
if( !resItem )
|
|
{
|
|
resItem = [FSItem itemWithName:name andType:itemType];
|
|
resItem.level = _level + 1;
|
|
[_items addObject:resItem];
|
|
_needToCalcStats = YES;
|
|
}
|
|
|
|
return resItem;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
NSMutableString *spaces = [NSMutableString string];
|
|
for (int i = 0; i < _level; i++)
|
|
[spaces appendString:@" "];
|
|
|
|
if( _itemType == FSItemTypeFile )
|
|
return [NSString stringWithFormat:@"%@%@!\n",spaces, _name];
|
|
|
|
NSMutableString *s = [NSMutableString stringWithFormat:@"%@/%@\n",spaces, _name];
|
|
|
|
if( !_collapsed )
|
|
for( FSItem* item in _items )
|
|
[s appendString: item.description];
|
|
|
|
return s;
|
|
}
|
|
|
|
- (void)sort
|
|
{
|
|
if( _items )
|
|
{
|
|
[_items sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
|
|
{
|
|
FSItem *item1 = (FSItem*)obj1;
|
|
FSItem *item2 = (FSItem*)obj2;
|
|
|
|
// if bouth item are folder return comparison result
|
|
if( item1.itemType == FSItemTypeFolder && item2.itemType == FSItemTypeFolder )
|
|
return [item1.name compare:item2.name];
|
|
|
|
// folders first
|
|
if ( item1.itemType == FSItemTypeFolder && item2.itemType == FSItemTypeFile )
|
|
return NSOrderedAscending;
|
|
|
|
if( item1.itemType == FSItemTypeFile && item2.itemType == FSItemTypeFolder )
|
|
return NSOrderedDescending;
|
|
|
|
// both items if files
|
|
return [item1.name compare:item2.name];
|
|
}];
|
|
|
|
for( FSItem *item in _items )
|
|
[item sort];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation FSDirectory
|
|
|
|
{
|
|
FSItem* _root;
|
|
int _curIndex;
|
|
int _findIndex;
|
|
FSItem* _foundItemAtIndex;
|
|
}
|
|
|
|
+ (FSDirectory *)directory
|
|
{
|
|
return [[FSDirectory alloc] init];
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
self = [super init];
|
|
|
|
if( self )
|
|
{
|
|
_root = [FSItem itemWithName:@"" andType:FSItemTypeFolder]; // init root element (always folder)
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (FSItem *)rootItem
|
|
{
|
|
return _root;
|
|
}
|
|
|
|
// add file to tree
|
|
// file is a path with format /root/subfolder/.../filename it
|
|
// should be from root
|
|
- (FSItem*)addFilePath:(NSString*)path withIndex:(int)index
|
|
{
|
|
// trim path
|
|
path = [path stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:PATH_SPLITTER_STRING]];
|
|
|
|
// split the path string
|
|
NSArray *arr = [path componentsSeparatedByString:PATH_SPLITTER_STRING];
|
|
|
|
if( !arr )
|
|
@throw [NSException exceptionWithName:@"FSDirectory - addFilePath"
|
|
reason:@"can not split filePath - nil array retrieved"
|
|
userInfo:nil];
|
|
|
|
// add all components to the tree (from root)
|
|
FSItem* levelItem = _root;
|
|
|
|
for( int level = 0; level < arr.count; level++ )
|
|
{
|
|
NSString *itemName = arr[level];
|
|
|
|
// last item in array is file, the others - folders
|
|
FSItemType itemType = (level == (arr.count -1) ? FSItemTypeFile : FSItemTypeFolder);
|
|
|
|
levelItem = [levelItem addItemWithName:itemName ofType:itemType];
|
|
|
|
if( itemType == FSItemTypeFile )
|
|
levelItem.index = index;
|
|
}
|
|
|
|
return levelItem;
|
|
}
|
|
|
|
- (FSItem*)itemAtIndex:(int)index
|
|
{
|
|
_curIndex = -1;
|
|
_findIndex = index;
|
|
_foundItemAtIndex = nil;
|
|
|
|
[self stepAndInrementFromItem:_root];
|
|
|
|
return _foundItemAtIndex;
|
|
}
|
|
|
|
- (void)stepAndInrementFromItem:(FSItem*)item
|
|
{
|
|
if( _foundItemAtIndex )
|
|
return;
|
|
|
|
for( FSItem *i in item.items )
|
|
{
|
|
if( ++_curIndex == _findIndex )
|
|
{
|
|
_foundItemAtIndex = i;
|
|
return;
|
|
}
|
|
|
|
if( i.items && !i.collapsed )
|
|
[self stepAndInrementFromItem:i];
|
|
}
|
|
}
|
|
|
|
- (int)count
|
|
{
|
|
_curIndex = 0;
|
|
_findIndex = -1;
|
|
_foundItemAtIndex = nil;
|
|
|
|
[self stepAndInrementFromItem:_root];
|
|
return _curIndex;
|
|
}
|
|
|
|
|
|
- (NSString *)description
|
|
{
|
|
return _root.description;
|
|
}
|
|
|
|
- (void)sort
|
|
{
|
|
[_root sort];
|
|
}
|
|
|
|
- (void)setNeedToRecalcStats
|
|
{
|
|
[_root setNeedToRecalcStats];
|
|
}
|
|
|
|
@end
|