summaryrefslogtreecommitdiffstats
path: root/Frameworks/LiBackend
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2008-04-14 21:45:08 -0400
committerBrian Cully <github.20.shmit@spamgourmet.com>2008-04-14 21:45:08 -0400
commit17349a5e426dc7acf1216a3767a22f69974cbca0 (patch)
tree20029d02f07ab6257cccec36d34fb312f796e1d1 /Frameworks/LiBackend
downloadliaison-17349a5e426dc7acf1216a3767a22f69974cbca0.tar.gz
liaison-17349a5e426dc7acf1216a3767a22f69974cbca0.zip
Initial commit.
Diffstat (limited to 'Frameworks/LiBackend')
-rw-r--r--Frameworks/LiBackend/English.lproj/LiBackend.scriptTerminology79
-rw-r--r--Frameworks/LiBackend/Info.plist26
-rw-r--r--Frameworks/LiBackend/LiBackend.h17
-rw-r--r--Frameworks/LiBackend/LiBackend.scriptSuite101
-rw-r--r--Frameworks/LiBackend/LiFileHandle.h103
-rw-r--r--Frameworks/LiBackend/LiFileHandle.m476
-rw-r--r--Frameworks/LiBackend/LiFileStore.h168
-rw-r--r--Frameworks/LiBackend/LiFileStore.m1040
-rw-r--r--Frameworks/LiBackend/LiFilter.h38
-rw-r--r--Frameworks/LiBackend/LiFilter.m108
-rw-r--r--Frameworks/LiBackend/LiLog.h21
-rw-r--r--Frameworks/LiBackend/LiLog.m104
-rw-r--r--Frameworks/LiBackend/LiPreferences.h31
-rw-r--r--Frameworks/LiBackend/LiPreferences.m209
-rw-r--r--Frameworks/LiBackend/LiStoreValidator.h11
-rw-r--r--Frameworks/LiBackend/LiStoreValidator.m13
16 files changed, 2545 insertions, 0 deletions
diff --git a/Frameworks/LiBackend/English.lproj/LiBackend.scriptTerminology b/Frameworks/LiBackend/English.lproj/LiBackend.scriptTerminology
new file mode 100644
index 0000000..5e586f9
--- /dev/null
+++ b/Frameworks/LiBackend/English.lproj/LiBackend.scriptTerminology
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Classes</key>
+ <dict>
+ <key>LiFileHandle</key>
+ <dict>
+ <key>Attributes</key>
+ <dict>
+ <key>dictionary</key>
+ <dict>
+ <key>Description</key>
+ <string>The complete attribute dictionary.</string>
+ <key>Name</key>
+ <string>dictionary</string>
+ </dict>
+ <key>fileHandle</key>
+ <dict>
+ <key>Description</key>
+ <string>The unique file identifier which locates a file in a library.</string>
+ <key>Name</key>
+ <string>id</string>
+ </dict>
+ <key>filename</key>
+ <dict>
+ <key>Description</key>
+ <string>The filename of the file, without type extension.</string>
+ <key>Name</key>
+ <string>name</string>
+ </dict>
+ <key>urlString</key>
+ <dict>
+ <key>Description</key>
+ <string>A URL which locates this file.</string>
+ <key>Name</key>
+ <string>address</string>
+ </dict>
+ </dict>
+ <key>Description</key>
+ <string>A file.</string>
+ <key>Name</key>
+ <string>file</string>
+ <key>PluralName</key>
+ <string>files</string>
+ </dict>
+ <key>LiFileStore</key>
+ <dict>
+ <key>Attributes</key>
+ <dict>
+ <key>name</key>
+ <dict>
+ <key>Description</key>
+ <string>The name of the library.</string>
+ <key>Name</key>
+ <string>name</string>
+ </dict>
+ <key>storeID</key>
+ <dict>
+ <key>Description</key>
+ <string>The unique store identifier.</string>
+ <key>Name</key>
+ <string>id</string>
+ </dict>
+ </dict>
+ <key>Description</key>
+ <string>A file library.</string>
+ <key>Name</key>
+ <string>library</string>
+ <key>PluralName</key>
+ <string>libraries</string>
+ </dict>
+ </dict>
+ <key>Description</key>
+ <string>Liaison's low-level scripting interface.</string>
+ <key>Name</key>
+ <string>LiBackend suite</string>
+</dict>
+</plist>
diff --git a/Frameworks/LiBackend/Info.plist b/Frameworks/LiBackend/Info.plist
new file mode 100644
index 0000000..f3c92de
--- /dev/null
+++ b/Frameworks/LiBackend/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>LiBackend</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.kublai.Liaison.LiBackend</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.4d</string>
+ <key>NSAppleScriptEnabled</key>
+ <string>YES</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/Frameworks/LiBackend/LiBackend.h b/Frameworks/LiBackend/LiBackend.h
new file mode 100644
index 0000000..3c6465a
--- /dev/null
+++ b/Frameworks/LiBackend/LiBackend.h
@@ -0,0 +1,17 @@
+/*
+ * LiBackend.h
+ * Liaison
+ *
+ * Created by Brian Cully on Fri May 30 2003.
+ * Copyright (c) 2003 Brian Cully. All rights reserved.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import <LiBackend/LiFileStore.h>
+#import <LiBackend/LiFileHandle.h>
+#import <LiBackend/LiFilter.h>
+#import <LiBackend/LiStoreValidator.h>
+#import <LiBackend/LiPreferences.h>
+#import <LiBackend/LiLog.h> \ No newline at end of file
diff --git a/Frameworks/LiBackend/LiBackend.scriptSuite b/Frameworks/LiBackend/LiBackend.scriptSuite
new file mode 100644
index 0000000..01e0daa
--- /dev/null
+++ b/Frameworks/LiBackend/LiBackend.scriptSuite
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>AppleEventCode</key>
+ <string>LiBE</string>
+ <key>Classes</key>
+ <dict>
+ <key>LiFileHandle</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>LiFH</string>
+ <key>Attributes</key>
+ <dict>
+ <key>dictionary</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>prec</string>
+ <key>ReadOnly</key>
+ <string>YES</string>
+ <key>Type</key>
+ <string>NSDictionary</string>
+ </dict>
+ <key>fileHandle</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>ID </string>
+ <key>ReadOnly</key>
+ <string>YES</string>
+ <key>Type</key>
+ <string>NSNumber&lt;UnsignedLong&gt;</string>
+ </dict>
+ <key>filename</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>pnam</string>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>Type</key>
+ <string>NSString</string>
+ </dict>
+ <key>urlString</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>curl</string>
+ <key>ReadOnly</key>
+ <string>YES</string>
+ <key>Type</key>
+ <string>NSString</string>
+ </dict>
+ </dict>
+ <key>Superclass</key>
+ <string>NSCoreSuite.AbstractObject</string>
+ <key>SupportedCommands</key>
+ <dict/>
+ </dict>
+ <key>LiFileStore</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>LiFS</string>
+ <key>Attributes</key>
+ <dict>
+ <key>name</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>pnam</string>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>Type</key>
+ <string>NSString</string>
+ </dict>
+ <key>storeID</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>ID </string>
+ <key>ReadOnly</key>
+ <string>YES</string>
+ <key>Type</key>
+ <string>NSNumber&lt;UnsignedLong&gt;</string>
+ </dict>
+ </dict>
+ <key>Superclass</key>
+ <string>NSCoreSuite.AbstractObject</string>
+ <key>ToManyRelationships</key>
+ <dict>
+ <key>allFileHandles</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>LiFH</string>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>Type</key>
+ <string>LiFileHandle</string>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+ <key>Name</key>
+ <string>LiBackend</string>
+</dict>
+</plist>
diff --git a/Frameworks/LiBackend/LiFileHandle.h b/Frameworks/LiBackend/LiFileHandle.h
new file mode 100644
index 0000000..5e5dcb3
--- /dev/null
+++ b/Frameworks/LiBackend/LiFileHandle.h
@@ -0,0 +1,103 @@
+//
+// LiFileHandle.h
+// Liaison
+//
+// Created by Brian Cully on Sat May 24 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiFileHandle : NSObject <NSCoding>
+{
+ id theStoreID;
+ id theFileID;
+}
++ (LiFileHandle *)fileHandleWithID: (id)aFileID
+ storeID: (id)aStoreID;
+
+// For the file validator methods.
+- (BOOL)shouldUpdate;
+
+- (id)valueForAttribute: (NSString *)anAttribute;
+- (void)setValue: (id)aValue forAttribute: (NSString *)anAttribute;
+- (NSArray *)valuesForAttributes: (NSArray *)someAttributes;
+- (void)setValues: (NSArray *)someValues forAttributes: (NSArray *)someAttributes;
+@property (retain,getter=fileID) id theFileID;
+@property (retain,getter=storeID) id theStoreID;
+@end
+
+@interface LiFileHandle (Accessors)
+- (id)storeID;
+- (void)setStoreID: (id)aStoreID;
+- (id)fileID;
+- (void)setFileID: (id)aFileID;
+@end
+
+// These are common access methods - actually nothing more than convenience
+// methods that are nothing more than wrappers to valueForAttribute: and
+// setValue:forAttribute:
+// It is recommended that plugins use the same method for attribute access.
+@interface LiFileHandle (CommonAccessors)
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+- (BOOL)isEditable;
+- (void)setIsEditable: (BOOL)editable;
+- (NSString *)filename;
+- (void)setFilename: (NSString *)aFilename;
+- (NSString *)type;
+- (void)setType: (NSString *)aType;
+- (NSNumber *)hfsCreator;
+- (void)setHFSCreator: (NSNumber *)aTypeCode;
+- (NSNumber *)hfsType;
+- (void)setHFSType: (NSNumber *)aTypeCode;
+- (NSString *)application;
+- (void)setApplication: (NSString *)pathToApp;
+- (NSDate *)lastModifiedTime;
+- (void)setLastModifiedTime: (NSDate *)aTime;
+- (NSDate *)creationTime;
+- (void)setCreationTime: (NSDate *)aTime;
+- (NSNumber *)fileSize;
+
+- (NSMutableArray *)groups;
+- (void)addToGroup: (NSString *)aGroup;
+- (BOOL)isMemberOfGroup: (NSString *)aGroup;
+- (void)removeFromGroup: (NSString *)aGroup;
+- (void)renameGroup: (NSString *)oldName toGroup: (NSString *)newName;
+- (BOOL)matchesFilter: (LiFilter *)aFilter;
+@end
+
+@interface LiFileHandle (CommonUtilities)
+- (NSString *)description;
+- (NSDictionary *)dictionary;
+- (void)update;
+- (void)open;
+- (NSURL *)url;
+@end
+
+@interface LiFileHandle (Scripting)
+- (NSScriptObjectSpecifier *)objectSpecifier;
+
+- (NSString *)urlString;
+@end
+
+@interface LiFileStore (LiFileHandleMethods)
+// Add a file to the library with the results of
+// [LiFileStore fileSystemAttributesForPath:].
+- (LiFileHandle *)addFileWithAttributes: (NSDictionary *)someAttributes;
+
+// To get a file's attributes.
+- (NSDictionary *)attributesForFileHandle: (LiFileHandle *)aFileHandle;
+
+// Set the attributes to be updated in the dictionary.
+- (void)updateFileHandle: (LiFileHandle *)aFileHandle
+ withAttributes: (NSDictionary *)someAttributes;
+
+// Remove file from the library.
+- (void)removeFileHandle: (LiFileHandle *)aFileHandle;
+
+// Returns all the LiFileHandles in the store.
+- (NSArray *)allFileHandles;
+
+// Returns a list of LiFileHandle objects for attributes
+// that match the dictionary.
+- (NSArray *)filesMatchingFilter: (LiFilter *)aFilter;
+@end \ No newline at end of file
diff --git a/Frameworks/LiBackend/LiFileHandle.m b/Frameworks/LiBackend/LiFileHandle.m
new file mode 100644
index 0000000..4f057c1
--- /dev/null
+++ b/Frameworks/LiBackend/LiFileHandle.m
@@ -0,0 +1,476 @@
+//
+// LiFileHandle.m
+// Liaison
+//
+// Created by Brian Cully on Sat May 24 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiFileHandle.h"
+
+@implementation LiFileHandle (Copying)
+- (id)copyWithZone: (NSZone *)aZone
+{
+ LiFileHandle *tmpHandle;
+
+ tmpHandle = [[LiFileHandle allocWithZone: aZone] init];
+ [tmpHandle setStoreID: [self storeID]];
+ [tmpHandle setFileID: [self fileID]];
+
+ return tmpHandle;
+}
+
+- (unsigned)hash
+{
+ unsigned long storeID, fileID;
+
+ storeID = [[self storeID] unsignedLongValue];
+ fileID = [[self fileID] unsignedLongValue];
+
+ // Fold the store ID down to 4 bits.
+ storeID = (storeID & 0xf) ^ (storeID >> 4 & 0xf) ^
+ (storeID >> 8 & 0xf) ^ (storeID >> 12 & 0xf) ^
+ (storeID >> 16 & 0xf) ^ (storeID >> 20 & 0xf) ^
+ (storeID >> 24 & 0xf) ^ (storeID >> 28 & 0xf);
+
+ // Fold the handle to 12 bits.
+ fileID = (fileID & 0xfff) ^ (fileID >> 12 & 0xfff) ^
+ (fileID >> 24 & 0xfff);
+
+ return (storeID << 12) | fileID;
+}
+
+- (BOOL)isEqual: (id)anObject
+{
+ if ([anObject isKindOfClass: [self class]]) {
+ IMP compareMethod;
+ id myID;
+
+ myID = [self fileID];
+ compareMethod = [myID methodForSelector: @selector(compare:)];
+ if (compareMethod != nil &&
+ compareMethod(myID, @selector(compare:), [anObject fileID]) == 0) {
+ myID = [self storeID];
+ compareMethod = [myID methodForSelector: @selector(compare:)];
+ if (compareMethod != nil &&
+ compareMethod(myID, @selector(compare:), [anObject storeID]) == 0)
+ return YES;
+ }
+ }
+
+ return NO;
+}
+@end
+
+@implementation LiFileHandle
++ (LiFileHandle *)fileHandleWithID: (id)aFileID
+ storeID: (id)aStoreID
+{
+ LiFileHandle *tmpHandle;
+
+ tmpHandle = [[[LiFileHandle alloc] init] autorelease];
+ [tmpHandle setFileID: aFileID];
+ [tmpHandle setStoreID: aStoreID];
+
+ return tmpHandle;
+}
+
+- (id)init
+{
+ return [super init];
+}
+
+- (void)dealloc
+{
+ [self setStoreID: nil];
+ [self setFileID: nil];
+
+ [super dealloc];
+}
+
+- (id)initWithCoder: (NSCoder *)aCoder
+{
+ id storeID, fileID;
+
+ self = [self init];
+
+ if ([aCoder allowsKeyedCoding]) {
+ storeID = [aCoder decodeObjectForKey: @"LiStoreID"];
+ fileID = [aCoder decodeObjectForKey: @"LiFileID"];
+ } else {
+ storeID = [aCoder decodeObject];
+ fileID = [aCoder decodeObject];
+ }
+ [self setStoreID: storeID];
+ [self setFileID: fileID];
+
+ return self;
+}
+
+- (void)encodeWithCoder: (NSCoder *)aCoder
+{
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject: [self storeID]
+ forKey: @"LiStoreID"];
+ [aCoder encodeObject: [self fileID]
+ forKey: @"LiFileID"];
+ } else {
+ [aCoder encodeObject: [self storeID]];
+ [aCoder encodeObject: [self fileID]];
+ }
+}
+
+- (BOOL)shouldUpdate
+{
+ return NO;
+}
+
+- (id)valueForAttribute: (NSString *)anAttribute
+{
+ NSDictionary *attrDict;
+
+ attrDict = [self dictionary];
+ return [attrDict objectForKey: anAttribute];
+}
+
+- (void)setValue: (id)aValue forAttribute: (NSString *)anAttribute
+{
+ if (aValue != nil)
+ [self setValues: [NSArray arrayWithObject: aValue]
+ forAttributes: [NSArray arrayWithObject: anAttribute]];
+}
+
+- (NSArray *)valuesForAttributes: (NSArray *)someAttributes
+{
+ NSDictionary *attributes;
+ NSMutableArray *someValues;
+ NSString *attribute;
+
+ attributes = [self dictionary];
+ someValues = [NSMutableArray array];
+ for (attribute in someAttributes) {
+ [someValues addObject: [attributes objectForKey: attribute]];
+ }
+
+ return someValues;
+}
+
+- (void)setValues: (NSArray *)someValues forAttributes: (NSArray *)someAttributes
+{
+ NSDictionary *newAttributes;
+
+ newAttributes = [NSDictionary dictionaryWithObjects: someValues
+ forKeys: someAttributes];
+ [[self fileStore] updateFileHandle: self withAttributes: newAttributes];
+}
+@synthesize theStoreID;
+@synthesize theFileID;
+@end
+
+@implementation LiFileHandle (Accessors)
+- (id)storeID
+{
+ return theStoreID;
+}
+
+- (void)setStoreID: (id)aStoreID
+{
+ [aStoreID retain];
+ [theStoreID release];
+ theStoreID = aStoreID;
+}
+
+- (id)fileID
+{
+ return theFileID;
+}
+
+- (void)setFileID: (id)aFileID
+{
+ [aFileID retain];
+ [theFileID release];
+ theFileID = aFileID;
+}
+@end
+
+@implementation LiFileHandle (CommonAccessors)
+- (LiFileStore *)fileStore
+{
+ return [LiFileStore fileStoreWithID: [self storeID]];
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [self setStoreID: [aFileStore storeID]];
+}
+
+- (BOOL)isEditable
+{
+ return [[self valueForAttribute: LiIsEditableAttribute] boolValue];
+}
+
+- (void)setIsEditable: (BOOL)editable
+{
+ [self setValue: [NSNumber numberWithBool: editable]
+ forAttribute: LiIsEditableAttribute];
+}
+
+- (NSString *)filename
+{
+ return [self valueForAttribute: LiFilenameAttribute];
+}
+
+- (void)setFilename: (NSString *)aFilename
+{
+ [self setValue: aFilename forAttribute: LiFilenameAttribute];
+}
+
+- (NSString *)type
+{
+ return [self valueForAttribute: LiTypeAttribute];
+}
+
+- (void)setType: (NSString *)aType
+{
+ [self setValue: aType forAttribute: LiTypeAttribute];
+}
+
+- (NSNumber *)hfsCreator
+{
+ return [self valueForAttribute: LiHFSCreatorAttribute];
+}
+
+- (void)setHFSCreator: (NSNumber *)aTypeCode
+{
+ [self setValue: aTypeCode forAttribute: LiHFSCreatorAttribute];
+}
+
+- (NSNumber *)hfsType
+{
+ return [self valueForAttribute: LiHFSTypeAttribute];
+}
+
+- (void)setHFSType: (NSNumber *)aTypeCode
+{
+ [self setValue: aTypeCode forAttribute: LiHFSTypeAttribute];
+}
+
+- (NSString *)application
+{
+ return [self valueForAttribute: LiApplicationAttribute];
+}
+
+- (void)setApplication: (NSString *)pathToApp
+{
+ [self setValue: pathToApp forAttribute: LiApplicationAttribute];
+}
+
+- (NSDate *)lastModifiedTime
+{
+ return [self valueForAttribute: LiLastModifiedDateAttribute];
+}
+
+- (void)setLastModifiedTime: (NSDate *)aTime
+{
+ [self setValue: aTime forAttribute: LiLastModifiedDateAttribute];
+}
+
+- (NSDate *)creationTime
+{
+ return [self valueForAttribute: LiCreationDateAttribute];
+}
+
+- (void)setCreationTime: (NSDate *)aTime
+{
+ [self setValue: aTime forAttribute: LiCreationDateAttribute];
+}
+
+- (NSNumber *)fileSize
+{
+ return [self valueForAttribute: LiFileSizeAttribute];
+}
+
+- (NSMutableArray *)groups
+{
+ return [self valueForAttribute: LiGroupsAttribute];
+}
+
+- (void)addToGroup: (NSString *)aGroup
+{
+ NSMutableArray *myGroups, *newGroups;
+
+ myGroups = [self groups];
+ if (myGroups != nil) {
+ if ([myGroups containsObject: aGroup]) {
+ return;
+ }
+ newGroups = [NSMutableArray arrayWithArray: myGroups];
+ } else
+ newGroups = [NSMutableArray array];
+ [newGroups addObject: aGroup];
+ [self setValue: newGroups forAttribute: LiGroupsAttribute];
+}
+
+- (BOOL)isMemberOfGroup: (NSString *)aGroup
+{
+ return [[self groups] containsObject: aGroup];
+}
+
+- (void)removeFromGroup: (NSString *)aGroup
+{
+ NSMutableArray *myGroups, *newGroups;
+
+ myGroups = [self groups];
+ if (myGroups != nil) {
+ newGroups = [NSMutableArray arrayWithArray: myGroups];
+ [newGroups removeObject: aGroup];
+ [self setValue: newGroups forAttribute: LiGroupsAttribute];
+ }
+}
+
+- (void)renameGroup: (NSString *)oldName toGroup: (NSString *)newName
+{
+ NSMutableArray *myGroups, *newGroups;
+
+ myGroups = [self groups];
+ if (myGroups != nil) {
+ newGroups = [NSMutableArray arrayWithArray: myGroups];
+ [newGroups removeObject: oldName];
+ [newGroups addObject: newName];
+ [self setValue: newGroups forAttribute: LiGroupsAttribute];
+ }
+}
+
+- (BOOL)matchesFilter: (LiFilter *)aFilter
+{
+ return [[self fileStore] attributes: [self dictionary] matchFilter: aFilter];
+}
+@end
+
+@implementation LiFileHandle (CommonUtilities)
+- (NSString *)description
+{
+ return [[self dictionary] description];
+}
+
+- (NSDictionary *)dictionary
+{
+ return [[self fileStore] attributesForFileID: [self fileID]];
+}
+
+- (void)update
+{
+ [[[self fileStore] delegate] updateFileHandle: self];
+}
+
+- (void)open
+{
+ [[[self fileStore] delegate] openFileHandle: self];
+}
+
+- (NSURL *)url
+{
+ return [[[self fileStore] delegate] urlForFileHandle: self];
+}
+@end
+
+@implementation LiFileHandle (Scripting)
+- (NSScriptObjectSpecifier *)objectSpecifier
+{
+ NSScriptClassDescription *containerDescription;
+ NSScriptObjectSpecifier *containerRef;
+ unsigned index;
+
+ [LiLog logAsDebug: @"[LiFileHandle objectSpecifier]"];
+
+ index = [[[self fileStore] allFileHandles] indexOfObject: self];
+ if (index != NSNotFound) {
+ [LiLog logAsDebug: @"index: %@", [NSNumber numberWithUnsignedInt: index]];
+ containerRef = [LiFileStore objectSpecifier];
+ containerDescription = (NSScriptClassDescription *)[NSScriptClassDescription classDescriptionForClass:[LiFileStore class]];
+
+ return [[[NSIndexSpecifier alloc] initWithContainerClassDescription:containerDescription containerSpecifier:containerRef key:@"allFileHandles" index:index] autorelease];
+ } else
+ return nil;
+}
+
+- (NSString *)urlString
+{
+ return [[self url] absoluteString];
+}
+@end
+
+@implementation LiFileStore (LiFileHandleMethods)
+- (LiFileHandle *)addFileWithAttributes: (NSDictionary *)someAttributes
+{
+ LiFileHandle *tmpHandle;
+ id fileID;
+
+ tmpHandle = nil;
+ fileID = [self fileIDWithAttributes: someAttributes];
+ if (fileID != nil)
+ tmpHandle = [LiFileHandle fileHandleWithID: fileID storeID: [self storeID]];
+ return tmpHandle;
+}
+
+- (NSDictionary *)attributesForFileHandle: (LiFileHandle *)aFileHandle
+{
+ return [self attributesForFileID: [aFileHandle fileID]];
+}
+
+- (void)updateFileHandle: (LiFileHandle *)aFileHandle
+ withAttributes: (NSDictionary *)someAttributes
+{
+ [self updateFileID: [aFileHandle fileID] withAttributes: someAttributes];
+}
+
+- (void)removeFileHandle: (LiFileHandle *)aFileHandle
+{
+ [self removeFileID: [aFileHandle fileID]];
+}
+
+- (NSArray *)allFileHandles
+{
+ NSEnumerator *idEnum;
+ NSMutableArray *fileHandles;
+ id fileID;
+
+ fileHandles = [NSMutableArray array];
+ idEnum = [[self allFileIDs] objectEnumerator];
+ while ((fileID = [idEnum nextObject]) != nil) {
+ LiFileHandle *tmpHandle;
+
+ tmpHandle = [[LiFileHandle alloc] init];
+ [tmpHandle setStoreID: [self storeID]];
+ [tmpHandle setFileID: fileID];
+ [fileHandles addObject: tmpHandle];
+ [tmpHandle release];
+ }
+
+ return fileHandles;
+}
+
+- (NSArray *)filesMatchingFilter: (LiFilter *)aFilter
+{
+ NSArray *fileIDs;
+ NSMutableArray *fileHandles;
+
+ fileHandles = nil;
+ fileIDs = [self fileIDsMatchingFilter: aFilter];
+ if ([fileIDs count] > 0) {
+ id fileID;
+
+ fileHandles = [NSMutableArray array];
+ for (fileID in fileIDs) {
+ LiFileHandle *tmpHandle;
+
+ tmpHandle = [[LiFileHandle alloc] init];
+ [tmpHandle setStoreID: [self storeID]];
+ [tmpHandle setFileID: fileID];
+ [fileHandles addObject: tmpHandle];
+ [tmpHandle release];
+ }
+ }
+ return fileHandles;
+}
+@end \ No newline at end of file
diff --git a/Frameworks/LiBackend/LiFileStore.h b/Frameworks/LiBackend/LiFileStore.h
new file mode 100644
index 0000000..cba804c
--- /dev/null
+++ b/Frameworks/LiBackend/LiFileStore.h
@@ -0,0 +1,168 @@
+//
+// LiFileStore.h
+// Liaison
+//
+// Created by Brian Cully on Sat May 24 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+// Notification names.
+//
+// When the file store list changes.
+#define LiFileStoresChangedNotification @"LiFileStoresChangedNotification"
+// When a file gets changed.
+#define LiFileChangedNotification @"LiFileChangedNotification"
+
+// Keys in the userInfo for LiFileStoresChangedNotification
+#define LiFileStoreAdded @"LiFileStoreAdded"
+#define LiFileStoreRemoved @"LiFileStoreRemoved"
+
+// Keys in the userInfo for LiFileChangedNotification.
+#define LiFilesAdded @"LiFilesAdded"
+#define LiFilesChanged @"LiFilesChanged"
+#define LiFilesRemoved @"LiFilesRemoved"
+
+// For LiFilesChanged and LiFilesRemoved.
+#define LiFileOldAttributes @"LiFilesOldAttributes"
+
+// The keys we supply for every file.
+#define LiFileHandleAttribute @"LiFileHandleAttribute"
+#define LiDirectoryAttribute @"LiDirectoryAttribute"
+#define LiFilenameAttribute @"LiFilenameAttribute"
+#define LiTypeAttribute @"LiTypeAttribute"
+#define LiLastModifiedDateAttribute @"LiLastModifiedDateAttribute"
+#define LiCreationDateAttribute @"LiCreationDateAttribute"
+#define LiFileSizeAttribute @"LiFileSizeAttribute"
+#define LiGroupsAttribute @"LiGroupsAttribute"
+#define LiHFSCreatorAttribute @"LiHFSCreatorAttribute"
+#define LiHFSTypeAttribute @"LiHFSTypeAttribute"
+#define LiApplicationAttribute @"LiApplicationAttribute"
+#define LiIsEditableAttribute @"LiIsEditableAttribute"
+
+@class LiFileHandle;
+@class LiFileStore;
+@class LiFilter;
+
+@protocol LiFileStoreDelegate <NSObject>
+// Sync the file store database to permanent storage.
+- (BOOL)synchronizeFileStore;
+
+ // Sync the file handle to disk.
+- (void)synchronizeFileHandle: (LiFileHandle *)aFileHandle
+ withNewAttributes: (NSMutableDictionary *)someAttributes;
+
+ // Update a file handle from permanent storage.
+- (BOOL)shouldUpdateFileHandle: (LiFileHandle *)aFileHandle;
+- (void)updateFileHandle: (LiFileHandle *)aFileHandle;
+
+// Open a file handle. Since a file must be on local storage to
+// open it, if the file doesn't exist there, delegates must first
+// create the file before opening it.
+- (void)openFileHandle: (LiFileHandle *)aFileHandle;
+
+// Attempt to add a file, specified by a URL, to the library.
+// Returns a non-nil LiFileHandle that matches the newly added file
+// on success, nil on failure.
+- (LiFileHandle *)addURL: (NSURL *)anURL
+ toFileStore: (LiFileStore *)aFileStore;
+
+// Return a standard URL for a file. This can be anything you want,
+// but it should always point to the file in some way.
+- (NSURL *)urlForFileHandle: (LiFileHandle *)aFileHandle;
+
+// Used to fill in default values for a particular attribute.
+// For instance, you can return a set of groups for LiGroupsAttribute.
+- (NSArray *)defaultValuesForAttribute: (NSString *)anAttribute;
+
+// For messing with the default attributes for a plugin.
+- (BOOL)addDefaultAttribute: (NSDictionary *)anAttribute toFileStore: (LiFileStore *)aFileStore;
+- (BOOL)changeDefaultValueForAttribute: (NSDictionary *)anAttribute toValue: (id)aValue inFileStore: (LiFileStore *)aFileStore;
+- (BOOL)removeDefaultAttribute: (NSDictionary *)anAttribute fromFileStore: (LiFileStore *)aFileStore;
+@end
+
+@interface LiFileStore : NSObject <NSCopying, NSCoding>
+{
+ id theStoreID;
+ id <LiFileStoreDelegate>theDelegate;
+
+ BOOL theStoreIsEditable;
+
+ NSImage *theIcon;
+ NSString *theName;
+
+ NSMutableDictionary *theFiles;
+ NSMutableDictionary *theIndexes;
+
+ NSMutableSet *theAddedFiles, *theChangedFiles, *theRemovedFiles;
+
+ unsigned long nextHandle;
+}
+// Look up stores via ID.
++ (NSArray *)allFileStores;
++ (NSEnumerator *)fileStoreEnumerator;
++ (LiFileStore *)fileStoreWithID: (id)aStoreID;
++ (void)removeStoreWithID: (id)aStoreID;
+
+// Create an auto-released file store.
++ (LiFileStore *)fileStoreWithName: (NSString *)aName;
+- (LiFileStore *)initWithName: (NSString *)aName;
+
+// Use the following methods to control how to manage store indexes.
+- (void)addIndexForAttribute: (NSString *)anAttribute;
+- (NSMutableDictionary *)indexForAttribute: (NSString *)anAttribute;
+- (void)removeIndexForAttribute: (NSString *)anAttribute;
+
+- (id)fileIDWithAttributes: (NSDictionary *)someAttributes;
+- (void)updateFileID: (id)aFileID
+ withAttributes: (NSDictionary *)someAttributes;
+- (void)removeFileID: (id)aFileID;
+
+- (NSDictionary *)attributesForFileID: (id)aFileID;
+
+- (NSArray *)allFileIDs;
+
+- (NSArray *)fileIDsMatchingFilter: (LiFilter *)aFilter;
+
+// Test attribute dictionaries against a filter.
+- (BOOL)attributes: (NSDictionary *)someAttributes
+ matchFilter: (LiFilter *)aFilter;
+
+// Return all the values in the library for an attribute.
+- (NSArray *)allValuesForAttribute: (NSString *)anAttribute;
+@property unsigned long nextHandle;
+@property (retain,getter=name) NSString *theName;
+@property (retain,getter=icon) NSImage *theIcon;
+@property (retain) NSMutableDictionary *theIndexes;
+@property (retain,getter=storeID) id theStoreID;
+@property (retain,getter=delegate) id <LiFileStoreDelegate>theDelegate;
+@property (getter=isEditable,setter=setEditable:) BOOL theStoreIsEditable;
+@property (retain) NSMutableDictionary *theFiles;
+@end
+
+@interface LiFileStore (CommonAccessors)
+- (void)synchronize;
+- (LiFileHandle *)addURL: (NSURL *)anURL;
+@end
+
+@interface LiFileStore (Accessors)
+- (id)storeID;
+- (void)setStoreID: (id)anID;
+- (id <LiFileStoreDelegate>)delegate;
+- (void)setDelegate: (id <LiFileStoreDelegate>)aDelegate;
+- (BOOL)isEditable;
+- (void)setEditable: (BOOL)editable;
+- (NSString *)name;
+- (void)setName: (NSString *)aName;
+- (NSImage *)icon;
+- (void)setIcon: (NSImage *)anIcon;
+- (NSMutableSet *)addedFiles;
+- (void)setAddedFiles: (NSMutableSet *)aSet;
+- (NSMutableSet *)changedFiles;
+- (void)setChangedFiles: (NSMutableSet *)aSet;
+- (NSMutableSet *)removedFiles;
+- (void)setRemovedFiles: (NSMutableSet *)aSet;
+@end
+
+@interface LiFileStore (Scripting)
+- (NSScriptObjectSpecifier *)objectSpecifier;
+@end \ No newline at end of file
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 <LiFileStoreDelegate>)delegate
+{
+ return theDelegate;
+}
+
+- (void)setDelegate: (id <LiFileStoreDelegate>)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
diff --git a/Frameworks/LiBackend/LiFilter.h b/Frameworks/LiBackend/LiFilter.h
new file mode 100644
index 0000000..3a81280
--- /dev/null
+++ b/Frameworks/LiBackend/LiFilter.h
@@ -0,0 +1,38 @@
+//
+// LiFilter.h
+// LiFrameworks
+//
+// Created by Brian Cully on Sat Aug 23 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiFilter : NSObject
+{
+ NSString *theAttribute;
+ NSString *theCompareSelector;
+ id theValue;
+}
++ (LiFilter *)filterWithAttribute: (NSString *)anAttribute
+ compareSelector: (SEL)aSelector
+ value: (id)aValue;
+
+- (id)initWithAttribute: (NSString *)anAttribute
+ compareSelector: (SEL)aSelector
+ value: (id)aValue;
+@property (retain,getter=attribute) NSString *theAttribute;
+@property (retain,getter=value) id theValue;
+@property (retain) NSString *theCompareSelector;
+@end
+
+@interface LiFilter (Accessors)
+- (NSString *)attribute;
+- (void)setAttribute: (NSString *)anAttribute;
+- (SEL)compareSelector;
+- (void)setCompareSelector: (SEL)aSelector;
+- (id)value;
+- (void)setValue: (id)aValue;
+@end
+
+@interface LiFilter (CommonAccessors)
+- (NSString *)description;
+@end \ No newline at end of file
diff --git a/Frameworks/LiBackend/LiFilter.m b/Frameworks/LiBackend/LiFilter.m
new file mode 100644
index 0000000..74e2f4a
--- /dev/null
+++ b/Frameworks/LiBackend/LiFilter.m
@@ -0,0 +1,108 @@
+//
+// LiFilter.m
+// LiFrameworks
+//
+// Created by Brian Cully on Sat Aug 23 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiFilter.h"
+
+@implementation LiFilter
++ (LiFilter *)filterWithAttribute: (NSString *)anAttribute
+ compareSelector: (SEL)aSelector
+ value: (id)aValue
+{
+ LiFilter *tmpFilter;
+
+ tmpFilter = [[self alloc] initWithAttribute: anAttribute
+ compareSelector: aSelector
+ value: aValue];
+ return [tmpFilter autorelease];
+}
+
+- (id)init
+{
+ NSException *exception;
+
+ exception = [NSException exceptionWithName: @"LiNoInitException"
+ reason: @"[LiFilter init] not supported"
+ userInfo: nil];
+ [exception raise];
+
+ return nil;
+}
+
+- (void)dealloc
+{
+ [self setAttribute: nil];
+ [self setCompareSelector: nil];
+ [self setValue: nil];
+
+ [super dealloc];
+}
+
+- (id)initWithAttribute: (NSString *)anAttribute
+ compareSelector: (SEL)aSelector
+ value: (id)aValue
+{
+ self = [super init];
+
+ [self setAttribute: anAttribute];
+ [self setCompareSelector: aSelector];
+ [self setValue: aValue];
+
+ return self;
+}
+@synthesize theCompareSelector;
+@synthesize theAttribute;
+@synthesize theValue;
+@end
+
+@implementation LiFilter (Accessors)
+- (NSString *)attribute
+{
+ return theAttribute;
+}
+
+- (void)setAttribute: (NSString *)anAttribute
+{
+ [anAttribute retain];
+ [theAttribute release];
+ theAttribute = anAttribute;
+}
+
+- (SEL)compareSelector
+{
+ return NSSelectorFromString(theCompareSelector);
+}
+
+- (void)setCompareSelector: (SEL)aSelector
+{
+ [theCompareSelector release];
+ theCompareSelector = [NSStringFromSelector(aSelector) retain];
+}
+
+- (id)value
+{
+ return theValue;
+}
+
+- (void)setValue: (id)aValue
+{
+ [aValue retain];
+ [theValue release];
+ theValue = aValue;
+}
+@end
+
+@implementation LiFilter (CommonAccessors)
+- (NSString *)description
+{
+ NSString *desc;
+
+ desc = [NSString stringWithFormat: @"{\n\tattribute: %@\n\tselector: %@\n\tvalue: %@\n}",
+ theAttribute, theCompareSelector, [theValue description]];
+ return desc;
+}
+@end \ No newline at end of file
diff --git a/Frameworks/LiBackend/LiLog.h b/Frameworks/LiBackend/LiLog.h
new file mode 100644
index 0000000..24043f0
--- /dev/null
+++ b/Frameworks/LiBackend/LiLog.h
@@ -0,0 +1,21 @@
+//
+// LiLog.h
+// Liaison
+//
+// Created by Brian Cully on Tue May 20 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiLog : NSObject
++ (void)alertWithHeader: (NSString *)aHeader
+ contents: (NSString *)someContents;
+
++ (void)logAsDebug: (NSString *)format, ...;
++ (void)logAsInfo: (NSString *)format, ...;
++ (void)logAsWarning: (NSString *)format, ...;
++ (void)logAsError: (NSString *)format, ...;
+
++ (id)indentDebugLog;
++ (id)unindentDebugLog;
++ (NSString *)debugIndentString;
+@end
diff --git a/Frameworks/LiBackend/LiLog.m b/Frameworks/LiBackend/LiLog.m
new file mode 100644
index 0000000..5dcb861
--- /dev/null
+++ b/Frameworks/LiBackend/LiLog.m
@@ -0,0 +1,104 @@
+//
+// LiLog.m
+// Liaison
+//
+// Created by Brian Cully on Tue May 20 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiLog.h"
+
+@implementation LiLog
+static int debugIndentLevel = 0;
+
++ (void)alertDidEnd: (NSWindow *)sheet
+ returnCode: (int)returnCode
+ contextInfo: (void *)contextInfo
+{
+ [sheet close];
+
+ return;
+}
+
++ (void)alertWithHeader: (NSString *)aHeader
+ contents: (NSString *)someContents
+ forWindow: (NSWindow *)aWindow
+{
+ if (aWindow != nil)
+ NSBeginAlertSheet(aHeader, @"Okay", nil, nil, aWindow, self,
+ @selector(alertDidEnd:returnCode:contextInfo:),
+ @selector(alertDidEnd:returnCode:contextInfo:),
+ nil, someContents);
+ else
+ NSRunAlertPanel(aHeader, someContents, @"Okay", nil, nil);
+}
+
++ (void)alertWithHeader: (NSString *)aHeader
+ contents: (NSString *)someContents
+{
+ [self alertWithHeader: aHeader contents: someContents forWindow: [NSApp keyWindow]];
+}
+
++ (void)logAsDebug: (NSString *)format, ...
+{
+#define DEBUG 1
+#if DEBUG
+ va_list args;
+
+ va_start(args, format);
+ NSLogv([[@"DEBUG: " stringByAppendingString: [self debugIndentString]] stringByAppendingString: format], args);
+ va_end(args);
+#endif
+}
+
++ (void)logAsInfo: (NSString *)format, ...
+{
+ va_list args;
+
+ va_start(args, format);
+ NSLogv([@"INFO: " stringByAppendingString: format], args);
+ va_end(args);
+}
+
++ (void)logAsWarning: (NSString *)format, ...
+{
+ va_list args;
+
+ va_start(args, format);
+ NSLogv([@"WARNING: " stringByAppendingString: format], args);
+ va_end(args);
+}
+
++ (void)logAsError: (NSString *)format, ...
+{
+ va_list args;
+
+ va_start(args, format);
+ NSLogv([@"ERROR: " stringByAppendingString: format], args);
+ va_end(args);
+}
+
++ (id)indentDebugLog
+{
+ debugIndentLevel++;
+ return self;
+}
+
++ (id)unindentDebugLog
+{
+ if (debugIndentLevel > 0)
+ debugIndentLevel--;
+ return self;
+}
+
++ (NSString *)debugIndentString
+{
+ NSMutableString *indentString;
+ int i;
+
+ indentString = [NSMutableString string];
+ for (i = 0; i < debugIndentLevel; i++)
+ [indentString appendString: @"\t"];
+ return indentString;
+}
+@end
diff --git a/Frameworks/LiBackend/LiPreferences.h b/Frameworks/LiBackend/LiPreferences.h
new file mode 100644
index 0000000..390b67f
--- /dev/null
+++ b/Frameworks/LiBackend/LiPreferences.h
@@ -0,0 +1,31 @@
+//
+// Preferences.h
+// Liaison
+//
+// Created by Brian Cully on Fri Feb 21 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface Preferences : NSObject
+{
+ NSUserDefaults *theDefaults;
+}
+
++ (Preferences *)sharedPreferences;
+
+- (NSString *)downloadDirectory;
+- (void)setDownloadDirectory: (NSString *)aPath;
+- (NSString *)groupPath;
+- (void)setGroupPath: (NSString *)aPath;
+- (NSString *)libraryPath;
+- (void)setLibraryPath: (NSString *)aPath;
+- (NSString *)hostname;
+- (void)setHostname: (NSString *)aHostname;
+- (BOOL)networkEnabled;
+- (void)setNetworkEnabled: (BOOL)isEnabled;
+- (BOOL)useRendezvousGroup;
+- (void)setUseRendezvousGroup: (BOOL)useGroup;
+- (NSDictionary *)fileListPrefs;
+- (void)setFileListPrefs: (NSDictionary *)listPrefs;
+@property (retain) NSUserDefaults *theDefaults;
+@end
diff --git a/Frameworks/LiBackend/LiPreferences.m b/Frameworks/LiBackend/LiPreferences.m
new file mode 100644
index 0000000..0f59581
--- /dev/null
+++ b/Frameworks/LiBackend/LiPreferences.m
@@ -0,0 +1,209 @@
+//
+// Preferences.m
+// Liaison
+//
+// Created by Brian Cully on Fri Feb 21 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiPreferences.h"
+
+// Default download directory.
+#define DOWNLOADSPATH @"/Desktop"
+
+// Default library datastore paths.
+#define LIBRARYPATH @"/Library/Liaison/Liaison Library.xml"
+#define GROUPPATH @"/Library/Liaison/Liaison Groups.xml"
+
+// Default hostname.
+#define HOSTNAME @"Unknown"
+
+// User default keys.
+#define DOWNLOADKEY @"downloadsDirectory"
+#define GROUPPATHKEY @"groupPath"
+#define HOSTNAMEKEY @"rendezvousHostname"
+#define LIBRARYPATHKEY @"libraryPath"
+#define NETWORKENABLEDKEY @"networkEnabled"
+#define USERENDEZVOUSGROUPKEY @"useRendezvousGroup"
+#define FILELISTPREFSKEY @"fileListPrefs"
+
+@implementation Preferences
+static Preferences *sharedInstance = nil;
+
++ (Preferences *)sharedPreferences
+{
+ if (sharedInstance == nil)
+ sharedInstance = [[Preferences alloc] init];
+ return sharedInstance;
+}
+
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ theDefaults = [NSUserDefaults standardUserDefaults];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [theDefaults synchronize];
+ [super dealloc];
+}
+
+- (NSString *)defaultDownloadDirectory
+{
+ return [NSHomeDirectory() stringByAppendingPathComponent:
+ DOWNLOADSPATH];
+}
+
+- (NSString *)downloadDirectory
+{
+ NSString *downloadDirectory;
+
+ downloadDirectory = [theDefaults objectForKey: DOWNLOADKEY];
+ if (downloadDirectory == nil)
+ downloadDirectory = [self defaultDownloadDirectory];
+ return downloadDirectory;
+}
+
+- (void)setDownloadDirectory: (NSString *)aPath
+{
+ [theDefaults setObject: aPath forKey: DOWNLOADKEY];
+}
+
+- (NSString *)defaultGroupPath
+{
+ return [NSHomeDirectory() stringByAppendingPathComponent: GROUPPATH];
+}
+
+- (NSString *)groupPath
+{
+ NSString *groupPath;
+
+ groupPath = [theDefaults objectForKey: GROUPPATHKEY];
+ if (groupPath == nil)
+ groupPath = [self defaultGroupPath];
+ return groupPath;
+}
+
+- (void)setGroupPath: (NSString *)aPath
+{
+ [theDefaults setObject: aPath forKey: GROUPPATHKEY];
+}
+
+- (NSString *)defaultLibraryPath
+{
+ return [NSHomeDirectory() stringByAppendingPathComponent: LIBRARYPATH];
+}
+
+- (NSString *)libraryPath
+{
+ NSString *libraryPath;
+
+ libraryPath = [theDefaults objectForKey: LIBRARYPATHKEY];
+ if (libraryPath == nil)
+ libraryPath = [self defaultLibraryPath];
+ return libraryPath;
+}
+
+- (void)setLibraryPath: (NSString *)aPath
+{
+ [theDefaults setObject: aPath forKey: LIBRARYPATHKEY];
+}
+
+- (NSString *)defaultHostname
+{
+ NSArray *hostnames;
+ NSString *hostname;
+
+ hostnames = [[NSHost currentHost] names];
+ for (hostname in hostnames) {
+ NSRange localRange;
+
+ localRange = [hostname rangeOfString: @".local."];
+ if (localRange.location != NSNotFound) {
+ hostname = [hostname substringToIndex: localRange.location];
+ break;
+ }
+ }
+ if (hostname == nil)
+ hostname = [hostnames objectAtIndex: 0];
+
+ return hostname;
+}
+
+- (NSString *)hostname
+{
+ NSString *hostname;
+
+ hostname = [theDefaults objectForKey: HOSTNAMEKEY];
+ if (hostname == nil)
+ hostname = [self defaultHostname];
+ return hostname;
+}
+
+- (void)setHostname: (NSString *)aHostname
+{
+ [theDefaults setObject: aHostname forKey: HOSTNAMEKEY];
+}
+
+- (BOOL)defaultNetworkEnabled
+{
+ return YES;
+}
+
+- (BOOL)networkEnabled
+{
+ NSNumber *networkEnabled;
+
+ networkEnabled = [theDefaults objectForKey: NETWORKENABLEDKEY];
+ if (networkEnabled == nil)
+ return [self defaultNetworkEnabled];
+ if ([networkEnabled intValue] == 1)
+ return YES;
+ else
+ return NO;
+}
+- (void)setNetworkEnabled: (BOOL)isEnabled
+{
+ if (isEnabled)
+ [theDefaults setObject: [NSNumber numberWithInt: 1]
+ forKey: NETWORKENABLEDKEY];
+ else
+ [theDefaults setObject: [NSNumber numberWithInt: 0]
+ forKey: NETWORKENABLEDKEY];
+}
+
+- (BOOL)defaultUseRendezvousGroup
+{
+ return NO;
+}
+
+- (BOOL)useRendezvousGroup
+{
+ NSNumber *useRendezvousGroup;
+
+ useRendezvousGroup = [theDefaults objectForKey: USERENDEZVOUSGROUPKEY];
+ if (useRendezvousGroup == nil)
+ return [self defaultUseRendezvousGroup];
+
+ return [useRendezvousGroup boolValue];
+}
+- (void)setUseRendezvousGroup: (BOOL)useGroup
+{
+ [theDefaults setObject: [NSNumber numberWithBool: useGroup]
+ forKey: USERENDEZVOUSGROUPKEY];
+}
+
+- (NSDictionary *)fileListPrefs
+{
+ return [theDefaults objectForKey: FILELISTPREFSKEY];
+}
+- (void)setFileListPrefs: (NSDictionary *)listPrefs
+{
+ [theDefaults setObject: listPrefs forKey: FILELISTPREFSKEY];
+}
+@synthesize theDefaults;
+@end
diff --git a/Frameworks/LiBackend/LiStoreValidator.h b/Frameworks/LiBackend/LiStoreValidator.h
new file mode 100644
index 0000000..da5094e
--- /dev/null
+++ b/Frameworks/LiBackend/LiStoreValidator.h
@@ -0,0 +1,11 @@
+//
+// LiStoreValidator.h
+// Liaison
+//
+// Created by Brian Cully on Sun May 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiStoreValidator : NSObject
+- (void)addFileToUpdateQueue: (LiFileHandle *)aFileHandle;
+@end
diff --git a/Frameworks/LiBackend/LiStoreValidator.m b/Frameworks/LiBackend/LiStoreValidator.m
new file mode 100644
index 0000000..3997cd5
--- /dev/null
+++ b/Frameworks/LiBackend/LiStoreValidator.m
@@ -0,0 +1,13 @@
+//
+// LiStoreValidator.m
+// Liaison
+//
+// Created by Brian Cully on Sun May 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@implementation LiStoreValidator
+- (void)addFileToUpdateQueue: (LiFileHandle *)aFileHandle
+{
+}
+@end