From 17349a5e426dc7acf1216a3767a22f69974cbca0 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 14 Apr 2008 21:45:08 -0400 Subject: Initial commit. --- Frameworks/LiBackend/LiFileStore.m | 1040 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1040 insertions(+) create mode 100644 Frameworks/LiBackend/LiFileStore.m (limited to 'Frameworks/LiBackend/LiFileStore.m') diff --git a/Frameworks/LiBackend/LiFileStore.m b/Frameworks/LiBackend/LiFileStore.m new file mode 100644 index 0000000..25c801f --- /dev/null +++ b/Frameworks/LiBackend/LiFileStore.m @@ -0,0 +1,1040 @@ +// +// LiFileStore.m +// Liaison +// +// Created by Brian Cully on Sat May 24 2003. +// Copyright (c) 2003 Brian Cully. All rights reserved. +// + +#import "LiFileStore.h" + +@interface LiFileStore (Private) +- (unsigned)sendNotifications; +- (void)sendAddNotificationForFileHandle: (LiFileHandle *)aFileHandle; +- (void)sendUpdateNotificationForFileHandle: (LiFileHandle *)aFileHandle + oldAttributes: (NSDictionary *)oldAttrs; +- (void)sendRemoveNotificationForFileHandle: (LiFileHandle *)aFileHandle + oldAttributes: (NSDictionary *)oldAttrs; +@end + +@implementation LiFileStore +static NSMutableDictionary *theFileStores = nil; +static unsigned int theNextID = 0; ++ (NSMutableDictionary *)fileStores +{ + if (theFileStores == nil) + theFileStores = [[NSMutableDictionary alloc] init]; + return theFileStores; +} + ++ (NSArray *)allFileStores +{ + return [[self fileStores] allValues]; +} + ++ (NSEnumerator *)fileStoreEnumerator +{ + return [[self fileStores] objectEnumerator]; +} + ++ (LiFileStore *)fileStoreWithID: (id)aStoreID +{ + return [[self fileStores] objectForKey: aStoreID]; +} + ++ (void)addStore: (LiFileStore *)aFileStore +{ + NSDictionary *userInfo; + NSNotificationCenter *defaultCenter; + + while (([self fileStoreWithID: + [NSNumber numberWithUnsignedInt: theNextID]]) != nil) + theNextID++; + + [[self fileStores] setObject: aFileStore forKey: + [NSNumber numberWithUnsignedInt: theNextID]]; + [aFileStore setStoreID: [NSNumber numberWithUnsignedInt: theNextID]]; + theNextID++; + + userInfo = [NSDictionary dictionaryWithObject: [aFileStore storeID] + forKey: LiFileStoreAdded]; + defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter postNotificationName: LiFileStoresChangedNotification + object: nil + userInfo: userInfo]; +} + ++ (void)removeStoreWithID: (id)aStoreID +{ + NSDictionary *userInfo; + NSNotificationCenter *defaultCenter; + + [[self fileStores] removeObjectForKey: aStoreID]; + + userInfo = [NSDictionary dictionaryWithObject: aStoreID + forKey: LiFileStoreRemoved]; + defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter postNotificationName: LiFileStoresChangedNotification + object: nil + userInfo: userInfo]; +} + ++ (LiFileStore *)fileStoreWithName: (NSString *)aName +{ + return [[[self alloc] initWithName: aName] autorelease]; +} + ++ (NSImage *)defaultIcon +{ + return nil; +} + +- (LiFileStore *)initWithName: (NSString *)aName +{ + self = [super init]; + + [self setName: aName]; + [self setIcon: [[self class] defaultIcon]]; + nextHandle = 1; + theFiles = [[NSMutableDictionary alloc] init]; + + [LiFileStore addStore: self]; + + return self; +} + +- (id)init +{ + NSException *myException; + + [self autorelease]; + + myException = [NSException exceptionWithName: @"LiFileStoreInitFailure" + reason: @"[[LiFileStore alloc] init] isn't supported." + userInfo: nil]; + [myException raise]; + return nil; +} + +- (void)dealloc +{ + [LiFileStore removeStoreWithID: [self storeID]]; + [theFiles release]; + [theIndexes release]; + + [super dealloc]; +} + +- (id)copyWithZone: (NSZone *)aZone +{ + LiFileStore *tmpStore; + + [LiLog logAsDebug: @"[LiFileStore copyWithZone: %@]", aZone]; + + tmpStore = [[LiFileStore allocWithZone: aZone] initWithName: [self name]]; + [tmpStore setEditable: [self isEditable]]; + [tmpStore setIcon: [self icon]]; + + // XXX + tmpStore->theFiles = theFiles; + tmpStore->theIndexes = theIndexes; + tmpStore->nextHandle = nextHandle; + + return tmpStore; +} + +- (id)initWithCoder: (NSCoder *)aCoder +{ + [LiLog logAsDebug: @"[LiFileStore initWithCoder: aCoder]"]; + + self = [self init]; + if (self != nil) { + NSData *iconData; + NSDictionary *fileDB, *indexes; + NSNumber *isEditable, *myNextHandle; + NSString *name; + + if ([aCoder allowsKeyedCoding]) { + name = [aCoder decodeObjectForKey: @"LiFSName"]; + iconData = [aCoder decodeObjectForKey: @"LiFSIconData"]; + isEditable = [aCoder decodeObjectForKey: @"LiFSIsEditable"]; + myNextHandle = [aCoder decodeObjectForKey: @"LiFSNextHandle"]; + fileDB = [aCoder decodeObjectForKey: @"LiFSFileDictionary"]; + indexes = [aCoder decodeObjectForKey: @"LiFSIndexes"]; + } else { + name = [aCoder decodeObject]; + iconData = [aCoder decodeObject]; + isEditable = [aCoder decodeObject]; + myNextHandle = [aCoder decodeObject]; + fileDB = [aCoder decodeObject]; + indexes = [aCoder decodeObject]; + } + [self setName: name]; + [self setIcon: [[[NSImage alloc] initWithData: iconData] autorelease]]; + [self setEditable: [isEditable boolValue]]; + nextHandle = [myNextHandle unsignedLongValue]; + theFiles = [[NSMutableDictionary alloc] initWithDictionary: fileDB]; + theIndexes = [[NSMutableDictionary alloc] initWithDictionary: indexes]; + } + + return self; +} + +- (void)encodeWithCoder: (NSCoder *)aCoder +{ + [LiLog logAsDebug: @"[LiFileStore encodeWithCoder: aCoder]"]; + if ([aCoder allowsKeyedCoding]) { + [aCoder encodeObject: [self name] + forKey: @"LiFSName"]; + [aCoder encodeObject: [[self icon] TIFFRepresentation] + forKey: @"LiFSIconData"]; + [aCoder encodeObject: [NSNumber numberWithBool: [self isEditable]] + forKey: @"LiFSIsEditable"]; + [aCoder encodeObject: [NSNumber numberWithUnsignedLong: nextHandle] + forKey: @"LiFSNextHandle"]; + [aCoder encodeObject: theFiles + forKey: @"LiFSFileDictionary"]; + [aCoder encodeObject: theIndexes + forKey: @"LiFSIndexes"]; + } else { + [aCoder encodeObject: [self name]]; + [aCoder encodeObject: [[self icon] TIFFRepresentation]]; + [aCoder encodeObject: [NSNumber numberWithBool: [self isEditable]]]; + [aCoder encodeObject: [NSNumber numberWithUnsignedLong: nextHandle]]; + [aCoder encodeObject: theFiles]; + [aCoder encodeObject: theIndexes]; + } +} + +- (void)addIndexForAttribute: (NSString *)anAttribute +{ + NSEnumerator *fileEnum; + NSMutableDictionary *newIndex; + id fileHandle; + + if ([self indexForAttribute: anAttribute] == nil) { + NSMutableArray *filesInValue; + + [LiLog logAsDebug: @"Indexing %@.", anAttribute]; + + filesInValue = [NSMutableArray array]; + newIndex = [NSMutableDictionary dictionary]; + fileEnum = [theFiles keyEnumerator]; + while ((fileHandle = [fileEnum nextObject]) != nil) { + id value; + + value = [[theFiles objectForKey: fileHandle] objectForKey: anAttribute]; + if (value != nil) { + if ([value isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [value objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + filesInValue = [newIndex objectForKey: subValue]; + if (filesInValue == nil) { + filesInValue = [NSMutableArray array]; + [newIndex setObject: filesInValue forKey: subValue]; + } + [filesInValue addObject: fileHandle]; + } + } else { + filesInValue = [newIndex objectForKey: value]; + if (filesInValue == nil) { + filesInValue = [NSMutableArray array]; + [newIndex setObject: filesInValue forKey: value]; + } + [filesInValue addObject: fileHandle]; + } + } + } + + if (theIndexes == nil) + theIndexes = [[NSMutableDictionary alloc] init]; + [theIndexes setObject: newIndex forKey: anAttribute]; + + [LiLog logAsDebug: @"Done indexing %@.", anAttribute]; + } +} + +- (NSMutableDictionary *)indexForAttribute: (NSString *)anAttribute +{ + return [theIndexes objectForKey: anAttribute]; +} + +- (void)removeIndexForAttribute: (NSString *)anAttribute +{ + if ([theIndexes objectForKey: anAttribute] != nil) + [theIndexes removeObjectForKey: anAttribute]; +} + +- (id)fileIDWithAttributes: (NSDictionary *)someAttributes +{ + NSEnumerator *attrEnum; + NSString *attribute; + NSMutableDictionary *fileAttributes; + NSNumber *handle; + + handle = [someAttributes objectForKey: LiFileHandleAttribute]; + if (handle == nil) { + handle = [NSNumber numberWithUnsignedLong: nextHandle]; + nextHandle++; + } else if ([theFiles objectForKey: handle] != nil) { + return handle; + } else + if (nextHandle <= [handle unsignedLongValue]) + nextHandle = [handle unsignedLongValue]+1; + + // First, create the file. + fileAttributes = [NSMutableDictionary dictionaryWithDictionary: someAttributes]; + [fileAttributes setObject: handle forKey: LiFileHandleAttribute]; + [theFiles setObject: fileAttributes forKey: handle]; + + // Then update indexes. + attrEnum = [someAttributes keyEnumerator]; + while ((attribute = [attrEnum nextObject]) != nil) { + NSMutableDictionary *index; + + index = [self indexForAttribute: attribute]; + if (index != nil) { + id myValue; + + myValue = [someAttributes objectForKey: attribute]; + if (myValue != nil) { + NSMutableArray *fileArray; + + if ([myValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [myValue objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + fileArray = [index objectForKey: subValue]; + if (fileArray == nil) { + fileArray = [NSMutableArray array]; + [index setObject: fileArray forKey: subValue]; + } + [fileArray addObject: handle]; + } + } else { + fileArray = [index objectForKey: myValue]; + if (fileArray == nil) { + fileArray = [NSMutableArray array]; + [index setObject: fileArray forKey: myValue]; + } + [fileArray addObject: handle]; + } + } + } + } + + if (handle != nil) + [self sendAddNotificationForFileHandle: [LiFileHandle fileHandleWithID: handle storeID: [self storeID]]]; + return handle; +} + +- (void)updateFileID: (id)aFileID + withAttributes: (NSDictionary *)someAttributes +{ + NSString *attribute; + NSEnumerator *attrEnum; + NSMutableDictionary *oldAttributes, *newAttributes, *tmpAttributes; + + // Call the delegate's sync method with the new attributes. + // Allow the delegate to filter out attributes it can't modify + // or modify attributes if it has to. + // It should return a dict of what was modified. + newAttributes = [NSMutableDictionary dictionaryWithDictionary: + someAttributes]; + [[self delegate] synchronizeFileHandle: [LiFileHandle fileHandleWithID: aFileID + storeID: [self storeID]] + withNewAttributes: newAttributes]; + + oldAttributes = [theFiles objectForKey: aFileID]; + tmpAttributes = [NSMutableDictionary dictionary]; + attrEnum = [newAttributes keyEnumerator]; + while ((attribute = [attrEnum nextObject]) != nil) { + NSMutableDictionary *index; + id oldValue, newValue; + + oldValue = [oldAttributes objectForKey: attribute]; + newValue = [newAttributes objectForKey: attribute]; + + // For notification. + if (oldValue != nil) + [tmpAttributes setObject: oldValue forKey: attribute]; + + index = [self indexForAttribute: attribute]; + if (index != nil) { + NSMutableArray *fileArray; + + // Remove old values from index. + if (oldValue != nil) { + if ([oldValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [oldValue objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + fileArray = [index objectForKey: subValue]; + [fileArray removeObject: aFileID]; + if ([fileArray count] == 0) { + [index removeObjectForKey: subValue]; + } + } + } else { + fileArray = [index objectForKey: oldValue]; + [fileArray removeObject: aFileID]; + if ([fileArray count] == 0) { + [index removeObjectForKey: oldValue]; + } + } + } + + // Add new values to index. + if ([newValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [newValue objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + fileArray = [index objectForKey: subValue]; + if (fileArray == nil) { + fileArray = [NSMutableArray array]; + [index setObject: fileArray forKey: subValue]; + } + [fileArray addObject: aFileID]; + } + } else { + fileArray = [index objectForKey: newValue]; + if (fileArray == nil) { + fileArray = [NSMutableArray array]; + [index setObject: fileArray forKey: newValue]; + } + [fileArray addObject: aFileID]; + } + } + + if (newValue == nil) { + [oldAttributes removeObjectForKey: attribute]; + } else + [oldAttributes setObject: newValue forKey: attribute]; + } + + // Send notification of update if there's something to say. + if ([newAttributes count] > 0) { + [self sendUpdateNotificationForFileHandle: [LiFileHandle fileHandleWithID: aFileID storeID: [self storeID]] + oldAttributes: tmpAttributes]; + } +} + +- (void)removeFileID: (id)aFileID +{ + NSDictionary *fileAttrs; + + fileAttrs = [theFiles objectForKey: aFileID]; + if (fileAttrs != nil) { + NSEnumerator *attrEnum; + NSString *attribute; + + attrEnum = [fileAttrs keyEnumerator]; + while ((attribute = [attrEnum nextObject]) != nil) { + NSMutableDictionary *index; + id myValue; + + myValue = [fileAttrs objectForKey: attribute]; + index = [self indexForAttribute: attribute]; + if (index != nil) { + NSMutableArray *fileArray; + + if ([myValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [myValue objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + fileArray = [index objectForKey: subValue]; + [fileArray removeObject: aFileID]; + if ([fileArray count] == 0) + [index removeObjectForKey: subValue]; + } + } else { + fileArray = [index objectForKey: myValue]; + [fileArray removeObject: aFileID]; + if ([fileArray count] == 0) + [index removeObjectForKey: myValue]; + } + } + } + + [fileAttrs retain]; + [theFiles removeObjectForKey: aFileID]; + + // Send notification of removal. + if ([fileAttrs count] > 0) { + [self sendRemoveNotificationForFileHandle: [LiFileHandle fileHandleWithID: aFileID storeID: [self storeID]] + oldAttributes: fileAttrs]; + } + [fileAttrs release]; + } +} + +- (NSDictionary *)attributesForFileID: (id)aFileID +{ + return [theFiles objectForKey: aFileID]; +} + +- (NSArray *)allFileIDs +{ + return [theFiles allKeys]; +} + +- (NSDictionary *)fileIDsMatchingFilter: (LiFilter *)aFilter + inList: (NSDictionary *)someFiles +{ + if (aFilter != nil) { + NSDictionary *index; + NSMutableDictionary *matchingFiles; + + matchingFiles = nil; + index = [self indexForAttribute: [aFilter attribute]]; + if (index != nil && [someFiles count] > [index count]) { + NSEnumerator *valEnum; + id value; + + valEnum = [index keyEnumerator]; + while ((value = [valEnum nextObject]) != nil) { + if ([value performSelector: [aFilter compareSelector] + withObject: [aFilter value]]) { + NSEnumerator *idEnum; + id fileID; + + if (matchingFiles == nil) + matchingFiles = [NSMutableDictionary dictionary]; + + idEnum = [[index objectForKey: value] objectEnumerator]; + while ((fileID = [idEnum nextObject]) != nil) { + [matchingFiles setObject: [self attributesForFileID: fileID] + forKey: fileID]; + } + } + } + } else { // Non-indexed + NSEnumerator *idEnum; + id fileID; + + // Go through all the files in the list. + idEnum = [someFiles keyEnumerator]; + while ((fileID = [idEnum nextObject]) != nil) { + if ([self attributes: [someFiles objectForKey: fileID] + matchFilter: aFilter]) { + if (matchingFiles == nil) + matchingFiles = [NSMutableDictionary dictionary]; + [matchingFiles setObject: [self attributesForFileID: fileID] + forKey: fileID]; + } + } + } + return matchingFiles; + } else + return someFiles; +} + +- (NSArray *)fileIDsMatchingFilter: (LiFilter *)aFilter +{ + NSArray *matchingFiles; + if (aFilter != nil) { + matchingFiles = [[self fileIDsMatchingFilter: aFilter inList: theFiles] allKeys]; + } else + matchingFiles = [self allFileIDs]; + + return matchingFiles; +} + +- (BOOL)attributes: (NSDictionary *)someAttributes + matchFilter: (LiFilter *)aFilter +{ + if (aFilter == nil) + return YES; + else if (someAttributes != nil) { + id myValue; + + myValue = [someAttributes objectForKey: [aFilter attribute]]; + if ([myValue respondsToSelector: @selector(objectEnumerator)]) { + NSEnumerator *valueEnum; + id subValue; + + valueEnum = [myValue performSelector: @selector(objectEnumerator)]; + while ((subValue = [valueEnum nextObject]) != nil) { + if ([subValue respondsToSelector: [aFilter compareSelector]]) { + if ([subValue performSelector: [aFilter compareSelector] + withObject: [aFilter value]]) { + return YES; + } + } + } + return NO; + } else { // Non-enumerable + BOOL match; + + match = NO; + if ([myValue respondsToSelector: [aFilter compareSelector]]) + if ([myValue performSelector: [aFilter compareSelector] + withObject: [aFilter value]]) + match = YES; + return match; + } + } else + return NO; +} + +#if 0 +- (BOOL)attributes: (NSDictionary *)someAttributes + match: (NSDictionary *)matchAttributes +{ + BOOL matches; + + matches = YES; + if (someAttributes != nil && [someAttributes count] > 0) { + NSEnumerator *attrEnum; + NSString *attribute; + + attrEnum = [someAttributes keyEnumerator]; + while ((attribute = [attrEnum nextObject]) != nil) { + id myValue, matchValue; + + matchValue = [someAttributes objectForKey: attribute]; + myValue = [someAttributes objectForKey: attribute]; + if (matchValue != myValue) { + if ([myValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valEnum; + id subValue; + + valEnum = [myValue objectEnumerator]; + while ((subValue = [valEnum nextObject]) != nil) { + if (subValue != matchValue) { + if ([subValue respondsToSelector: + @selector(compare:)]) { + if ([subValue performSelector: @selector(compare:) + withObject: matchValue] != 0) + matches = NO; + } else + matches = NO; + } + } + } else if ([myValue respondsToSelector: + @selector(compare:)]) { + if ([myValue performSelector: @selector(compare:) + withObject: matchValue] != 0) + matches = NO; + } else + matches = NO; + } + } + } + + return matches; +} + +- (NSMutableSet *)filesInSet: (NSMutableSet *)fileSet + matchingAttributes: (NSMutableDictionary *)someAttributes +{ + NSEnumerator *attributeEnum; + NSString *attribute, *bestAttribute; + id attributeValue; + long bestCount; + + if (someAttributes == nil || [someAttributes count] == 0) + return fileSet; + + // Find the smallest sets of potential matchers. This allows us to look + // up as few files as possible. + bestAttribute = nil; + bestCount = -1; + attributeEnum = [someAttributes keyEnumerator]; + while ((attribute = [attributeEnum nextObject]) != nil) { + if (bestAttribute == nil) + bestAttribute = attribute; + else { + NSDictionary *index; + + index = [self indexForAttribute: attribute]; + if (index != nil) { + unsigned long indexCount; + + indexCount = [index count]; + if (bestCount == -1) { + bestAttribute = attribute; + bestCount = indexCount; + } else if (bestCount > 1 && bestCount > (long)indexCount) { + bestAttribute = attribute; + bestCount = indexCount; + } + } + } + } + attribute = bestAttribute; + attributeValue = [someAttributes objectForKey: bestAttribute]; + + if (fileSet == nil) { + NSDictionary *index; + + index = [self indexForAttribute: attribute]; + if (index != nil) { + fileSet = [NSMutableSet setWithArray: [index objectForKey: attributeValue]]; + } else { + NSEnumerator *handleEnum; + id handle; + + // Go through every file in the library one by one. Shitty! + fileSet = [NSMutableSet set]; + handleEnum = [theFiles keyEnumerator]; + while ((handle = [handleEnum nextObject]) != nil) { + NSDictionary *handleAttrs; + id myValue; + + handleAttrs = [theFiles objectForKey: handle]; + myValue = [handleAttrs objectForKey: attribute]; + if (myValue != attributeValue) { + if ([myValue isKindOfClass: [NSArray class]]) { + NSEnumerator *valueEnum; + id subValue; + + valueEnum = [myValue objectEnumerator]; + while ((subValue = [valueEnum nextObject]) != nil) { + if (subValue == attributeValue) + [fileSet addObject: handle]; + else if ([subValue respondsToSelector: @selector(compare:)]) { + if ([subValue performSelector: @selector(compare:) + withObject: attributeValue] == 0) { + [fileSet addObject: handle]; + break; + } + } + } + } else if ([myValue respondsToSelector: @selector(compare:)]) + if ([myValue performSelector: @selector(compare:) + withObject: attributeValue] == 0) + [fileSet addObject: handle]; + } else + [fileSet addObject: handle]; + } + } + } else { + NSDictionary *index; + + index = [self indexForAttribute: attribute]; + if (index != nil) { + [fileSet intersectSet: [NSMutableSet setWithArray: [index objectForKey: attributeValue]]]; + } else { + NSEnumerator *handleEnum; + id handle; + + // Go through every file in the set. + handleEnum = [fileSet objectEnumerator]; + while ((handle = [handleEnum nextObject]) != nil) { + NSDictionary *handleAttrs; + id myValue; + + handleAttrs = [theFiles objectForKey: handle]; + myValue = [handleAttrs objectForKey: attribute]; + if (myValue == attributeValue) { + [fileSet removeObject: handle]; + } else if ([myValue respondsToSelector: @selector(compare:)]) { + if ([myValue performSelector: @selector(compare:) + withObject: attributeValue] == 0) + [fileSet removeObject: handle]; + } + } + } + } + + [someAttributes removeObjectForKey: bestAttribute]; + return [self filesInSet: fileSet matchingAttributes: someAttributes]; +} + +- (NSArray *)filesMatchingAttributes: (NSDictionary *)someAttributes +{ + if (someAttributes != nil && [someAttributes count] > 0) { + NSEnumerator *handleEnum; + NSMutableArray *matchingFiles; + id handle; + + matchingFiles = [NSMutableArray array]; + handleEnum = [[self filesInSet: nil + matchingAttributes: [NSMutableDictionary dictionaryWithDictionary: someAttributes]] objectEnumerator]; + while ((handle = [handleEnum nextObject]) != nil) { + LiFileHandle *fileHandle; + + fileHandle = [[LiFileHandle alloc] init]; + [fileHandle setFileStore: self]; + [fileHandle setFileHandle: handle]; + + [matchingFiles addObject: fileHandle]; + [fileHandle release]; + } + return matchingFiles; + } else + return [self allFileHandles]; +} +#endif + +- (NSArray *)allValuesForAttribute: (NSString *)anAttribute +{ + NSArray *valueArray; + NSMutableDictionary *index; + NSMutableSet *values; + + values = [NSMutableSet setWithArray: [[self delegate] defaultValuesForAttribute: anAttribute]]; + index = [self indexForAttribute: anAttribute]; + if (index != nil) { + // Use the index to find the possible values. + [values addObjectsFromArray: [index allKeys]]; + } else { + NSDictionary *fileAttrs; + NSEnumerator *fileEnum; + + // No index. Crappy. Go through every file in the library. + fileEnum = [theFiles objectEnumerator]; + while ((fileAttrs = [fileEnum nextObject]) != nil) { + id value; + + value = [fileAttrs objectForKey: anAttribute]; + if (value != nil && [values member: anAttribute] != nil) { + [values addObject: anAttribute]; + } + } + } + + valueArray = [values allObjects]; + return valueArray; +} +@synthesize theIcon; +@synthesize theDelegate; +@synthesize theFiles; +@synthesize nextHandle; +@synthesize theStoreID; +@synthesize theName; +@synthesize theIndexes; +@end + +@implementation LiFileStore (CommonAccessors) +- (void)synchronize +{ + if ([self sendNotifications] > 0) { + [[self delegate] synchronizeFileStore]; + } +} + +- (LiFileHandle *)addURL: (NSURL *)anURL +{ + return [[self delegate] addURL: anURL toFileStore: self]; +} +@end + +@implementation LiFileStore (Accessors) +- (id)storeID +{ + return theStoreID; +} + +- (void)setStoreID: (id)anID +{ + [anID retain]; + [theStoreID release]; + theStoreID = anID; +} + +- (id )delegate +{ + return theDelegate; +} + +- (void)setDelegate: (id )aDelegate +{ + [aDelegate retain]; + [theDelegate release]; + theDelegate = aDelegate; +} + +- (BOOL)isEditable +{ + return theStoreIsEditable; +} + +- (void)setEditable: (BOOL)editable +{ + theStoreIsEditable = editable; +} + +- (NSString *)name +{ + return theName; +} + +- (void)setName: (NSString *)aName +{ + NSNotificationCenter *defaultCenter; + + [aName retain]; + [theName release]; + theName = aName; + + defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter postNotificationName: LiFileStoresChangedNotification + object: nil + userInfo: nil]; +} + +- (NSImage *)icon +{ + return theIcon; +} + +- (void)setIcon: (NSImage *)anIcon +{ + NSNotificationCenter *defaultCenter; + + [anIcon retain]; + [theIcon release]; + theIcon = anIcon; + + defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter postNotificationName: LiFileStoresChangedNotification + object: nil + userInfo: nil]; +} + +- (NSMutableSet *)addedFiles +{ + if (theAddedFiles == nil) + [self setAddedFiles: [NSMutableSet set]]; + return theAddedFiles; +} + +- (void)setAddedFiles: (NSMutableSet *)aSet +{ + [aSet retain]; + [theAddedFiles release]; + theAddedFiles = aSet; +} + +- (NSMutableSet *)changedFiles +{ + if (theChangedFiles == nil) + [self setChangedFiles: [NSMutableSet set]]; + return theChangedFiles; +} + +- (void)setChangedFiles: (NSMutableSet *)aSet +{ + [aSet retain]; + [theChangedFiles release]; + theChangedFiles = aSet; +} + +- (NSMutableSet *)removedFiles +{ + if (theRemovedFiles == nil) + [self setRemovedFiles: [NSMutableSet set]]; + return theRemovedFiles; +} + +- (void)setRemovedFiles: (NSMutableSet *)aSet +{ + [aSet retain]; + [theRemovedFiles release]; + theRemovedFiles = aSet; +} +@end + +@implementation LiFileStore (Scripting) +- (NSScriptObjectSpecifier *)objectSpecifier +{ + NSScriptClassDescription *containerDescription; + NSScriptObjectSpecifier *containerRef; + unsigned index; + + index = [[LiFileStore allFileStores] indexOfObjectIdenticalTo: self]; + if (index != NSNotFound) { + containerRef = [NSApp objectSpecifier]; + containerDescription = (NSScriptClassDescription *)[NSScriptClassDescription classDescriptionForClass:[NSApp class]]; + + return [[[NSIndexSpecifier alloc] initWithContainerClassDescription:containerDescription containerSpecifier:containerRef key:@"orderedFileStores" index:index] autorelease]; + } else + return nil; +} +@end + +@implementation LiFileStore (Private) +// XXX +// Should coalesce like events into a single event before sending it +// and take the array code out of the individual file updates. +- (void)sendNotificationName: (NSString *)aNotificationName + userInfo: (NSDictionary *)userInfo +{ + NSNotificationCenter *defaultCenter; + + defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter postNotificationName: aNotificationName + object: self + userInfo: userInfo]; +} + +- (unsigned)sendNotifications +{ + NSMutableDictionary *userInfo; + unsigned changeCount; + + userInfo = [NSMutableDictionary dictionary]; + if ([[self addedFiles] count] > 0) { + [userInfo setObject: [[self addedFiles] allObjects] + forKey: LiFilesAdded]; + [self setAddedFiles: nil]; + } + if ([[self changedFiles] count] > 0) { + [userInfo setObject: [[self changedFiles] allObjects] + forKey: LiFilesChanged]; + [self setChangedFiles: nil]; + } + if ([[self removedFiles] count] > 0) { + [userInfo setObject: [[self removedFiles] allObjects] + forKey: LiFilesRemoved]; + [self setRemovedFiles: nil]; + } + + changeCount = [userInfo count]; + if (changeCount > 0) { + [self sendNotificationName: LiFileChangedNotification + userInfo: userInfo]; + } + return changeCount; +} + +- (void)sendAddNotificationForFileHandle: (LiFileHandle *)aFileHandle +{ + [[self addedFiles] addObject: aFileHandle]; +} + +- (void)sendUpdateNotificationForFileHandle: (LiFileHandle *)aFileHandle + oldAttributes: (NSDictionary *)oldAttrs +{ + if ([oldAttrs count] > 0) { + [[self changedFiles] addObject: + [NSDictionary dictionaryWithObjects: + [NSArray arrayWithObjects: aFileHandle, oldAttrs, nil] + forKeys: + [NSArray arrayWithObjects: LiFileHandleAttribute, LiFileOldAttributes, nil]]]; + } +} + +- (void)sendRemoveNotificationForFileHandle: (LiFileHandle *)aFileHandle + oldAttributes: (NSDictionary *)oldAttrs +{ + [[self removedFiles] addObject: + [NSDictionary dictionaryWithObjects: + [NSArray arrayWithObjects: aFileHandle, oldAttrs, nil] + forKeys: + [NSArray arrayWithObjects: LiFileHandleAttribute, LiFileOldAttributes, nil]]]; +} +@end \ No newline at end of file -- cgit v1.2.3