/* ** SmartFolderSource.m ** ** Copyright (c) 2004 ** ** Author: Yen-Ju Chen ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "SmartFolderSource.h" #include "Sources.h" #include "LibrarySource.h" #include "AttributesSource.h" #include "GNUstep.h" #include "Criterion.h" #include "ContentIndexer.h" #include "TimeStamp.h" @implementation SmartFolderSource - (void) _refreshWithSources: (BOOL) withSource { Sources *sources = [Sources sharedSources]; SourceType sourceType; LibrarySource *library = [LibrarySource sharedSource]; AttributesSource *attributes = [AttributesSource sharedSource]; NSString *title, *path, *key, *object; NSDictionary *attr; NSEnumerator *e; id o, value; BOOL matchSuccessfully; unsigned int index; SourceType type; unsigned int i, icount; unsigned int j, jcount; unsigned int uid; unsigned attrIndex; NSMutableDictionary *allInformation = [[NSMutableDictionary alloc] init]; NSMutableArray *allSources; id source; icount = [library count]; //NSLog(@"icount %d", icount); for(i = 0; i < icount; i++) { [allInformation removeAllObjects]; /* Title */ title = [library titleAtIndex: i]; uid = [library uniqueNumberAtIndex: i]; [allInformation setObject: title forKey: sTitle]; index = i; //if ([from isEqualToString: sLibrary] == NO) if (withSource) { allSources = [NSMutableArray array]; jcount = [sources count]; for(j = 0; j < jcount; j++) { sourceType = [sources typeOfSourceAtIndex: j]; switch(sourceType) { case SourceFolderType: case SourceSmartFolderType: source = [sources containerAtIndex: j]; if ([source indexOfTitle: title] != NSNotFound) { [allSources addObject: [sources titleAtIndex: j]]; } break; default: continue; } } [allInformation setObject: allSources forKey: sSource]; } /* Note and Render*/ object = [library noteAtIndex: index]; if (object) { object = [object stringByAppendingString: [[library renderedNoteAtIndex: index] string]]; [allInformation setObject: object forKey: sNote]; } /* File name */ path = [library pathOfItemAtIndex: index]; if (path) { attrIndex = [attributes indexOfTitle: path]; object = [path lastPathComponent]; if (object) [allInformation setObject: object forKey: sFilename]; /* File content, send full path */ object = [library fullPathOfItemAtIndex: index]; if (object) [allInformation setObject: object forKey: sFileContent]; } /* type */ object = [library typeStringOfItemAtIndex: index]; if (object) [allInformation setObject: object forKey: sFiletype]; /* All attributes */ attr = [attributes attributesAtIndex: attrIndex]; e = [[attr allKeys] objectEnumerator]; while ((o = [e nextObject])) { key = nameOfAttributeKey(o); /* Add prefix "- " to indicates it is attribute */ key = [@"- " stringByAppendingString: key]; value = [attr objectForKey: o]; [allInformation setObject: value forKey: key]; } //NSLog(@"SmartSource match %@", title); matchSuccessfully = [criterion match: allInformation]; if (matchSuccessfully == YES) { //NSLog(@"success"); if ([self isDuplicatedTitle: title] == NO) { index = [self newTitle: title]; [self setUniqueNumber: uid atIndex: index]; } } } RELEASE(allInformation); } - (void) refreshAll { /* Check whether it is refreshed already */ if ([[TimeStamp timeStampWithIdentifier: LibraryEditedTimeStamp] isBeforeTime: lastRefreshTime]) { //NSLog(@"Library doesn't change"); return; } if ([lock tryLock] == NO) return; ASSIGN(lastRefreshTime, [NSDate date]); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSArray *subjectsToMatch; [container removeAllObjects]; subjectsToMatch = [criterion subjectsToMatch]; if ([subjectsToMatch containsObject: sFileContent]) { /* Set up content index */ NSString *path = [MyLibraryPath(nil) stringByAppendingPathComponent: ContentIndexDirectory]; //NSLog(@"SmartFolderSource: setLocation %@", path); [[ContentIndexer sharedIndexer] setIndexLocation: path]; } if ([subjectsToMatch containsObject: sSource]) { /* refresh other source first */ id source= nil; SourceType type; Sources *sources = [Sources sharedSources]; unsigned int i, count = [sources count]; for (i = 0; i < count; i++) { type = [sources typeOfSourceAtIndex: i]; if (type == SourceSmartFolderType) { source= [sources containerAtIndex: i]; if ((source) && (source != self)) [source refreshAll]; } } [self _refreshWithSources: YES]; } else { [self _refreshWithSources: NO]; } RELEASE(pool); [lock unlock]; } - (BOOL) readFromFile: (NSString *) absolutePath { NSData *data = [NSData dataWithContentsOfFile: absolutePath]; if (data == nil) return NO; NSString *err; id propertyList = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListImmutable format: NULL errorDescription: &err]; if (err) { NSLog(@"General container: Read Error \"%@\"", err); RELEASE(err); return NO; } if (propertyList) { [criterion loadPropertyList: propertyList]; addAvailableSubjectsIntoCriterion(criterion); return YES; } else return NO; } - (BOOL) loadSourceAtPath: (NSString *) path { if ([super loadSourceAtPath: path] == NO) return NO; NSString *folderPath = [self path]; BOOL loadResult; loadResult = [self readFromFile: folderPath]; if (loadResult == YES) { makeBackup(folderPath); /* Remove old one in case the name changes */ NSFileManager *fm = [NSFileManager defaultManager]; [fm removeFileAtPath: folderPath handler: nil]; return YES; } else { /* Check backup file */ NSFileManager *fm = [NSFileManager defaultManager]; BOOL isDir; NSString *backup = [folderPath stringByAppendingString: @"~"]; loadResult = [self readFromFile: backup]; if (loadResult == YES) { int result = NSRunAlertPanel(sFileError, [NSString stringWithFormat: sFileNotExistWantBackup____, sSmartFolder, folderPath, sSmartFolder, sSmartFolder], sYES, sNO, nil, nil); if (result == NSAlertDefaultReturn) { return YES; } } } /* nil will clean all criterions */ [criterion loadPropertyList: nil]; return YES; } - (BOOL) writeToFile: (NSString *) absolutePath { id object = [criterion propertyList]; if (object == nil) { NSLog(@"Internal Error: No criterion property list"); return NO; } NSString *err = nil; NSData *data = [NSPropertyListSerialization dataFromPropertyList: object format: NSPropertyListXMLFormat_v1_0 errorDescription: &err]; if (err) { NSLog(@"SmartFolderSource: Write Error %@", err); RELEASE(err); return NO; } return [data writeToFile: absolutePath atomically: YES]; } - (BOOL) unloadSourceAtPath: (NSString *) path { BOOL loadResult = [self writeToFile: path]; if (loadResult == NO) { NSLog(@"Unload failed %@", path); return NO; } if (([self path]) && ([[self path] isEqualToString: path] == NO)) { /* Load and save file in different name. * Move the backup file to new name */ NSString *extension = [[self path] pathExtension]; NSString *oldPath = [[self path] stringByAppendingString: @"~"]; NSString *newPath = [[self path] stringByDeletingLastPathComponent]; newPath = [newPath stringByAppendingPathComponent: [[path lastPathComponent] stringByAppendingString: @"~"]]; //NSLog(@"Move backup %@", newPath); NSFileManager *fm = [NSFileManager defaultManager]; return [fm movePath: oldPath toPath: newPath handler: nil]; } return YES; /* Don't call superclass because smart fonder * don't really save the list of notes and files. * It use criterion to dynamically generate new one. * * return [super unloadSourceAtPath: path]; */ } - (Criterion *) criterion { return criterion; } - (id) init { self = [super init]; criterion = [[Criterion alloc] init]; lock = [[NSLock alloc] init]; ASSIGN(lastRefreshTime, [NSDate date]); return self; } - (void) dealloc { RELEASE(criterion); RELEASE(lock); RELEASE(lastRefreshTime); [super dealloc]; } @end