/*! @header FTDictionaryServiceForGraphImpl @abstract Module of FT @availability OS X, GNUstep @copyright 2004, 2005, 2006 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  -------------------------------------------------------------------------
  Modification history

  16.07.06 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  
*/ #include "FTDictionaryServiceForGraphImpl.h" #include #include #include @implementation FTDictionaryServiceForGraphImpl - initForGraph: (id ) aGraph serviceLoader: (FTDictionaryServiceLoader *) aServiceLoader { self = [super init]; self->database = nil; self->databaseIsOpen = NO; self->serviceLoader = [aServiceLoader retain]; self->writeLock = [[NSLock alloc] init]; if( ![aGraph isKindOfClass: [FTGraphImpl class]] ) { [[FTLogging coreLog] error: @"FTDictionaryServiceForGraphImpl::initForGraph: Given graph is "\ "NOT kind of class FTGraphImpl... Graph: %@", aGraph]; [[[ECIllegalStateException alloc] initWithIllegalStateInfo: [NSString stringWithFormat: @"FTDictionaryServiceForGraphImpl::initForGraph: Given graph is "\ "NOT kind of class FTGraphImpl... Graph: %@", aGraph]] raise]; } [self openDatabaseForGraph: (FTGraphImpl *) aGraph usingDatabaseName: [aServiceLoader databaseNameScheme]]; return self; } - (void) dealloc { [self close]; [self->serviceLoader release]; [self->writeLock release]; [super dealloc]; } - (void) close { [self->writeLock lock]; NS_DURING if( YES == self->databaseIsOpen ) { if( nil != self->database ) { [self->database close]; [self->database release]; self->database = nil; } self->databaseIsOpen = NO; } else { [self->database release]; } NS_HANDLER [self->writeLock unlock]; [localException raise]; NS_ENDHANDLER [self->writeLock unlock]; } - (BOOL) isOpen { return self->databaseIsOpen; } - addDatabaseEntry: (BDBDatabaseEntry *) anEntry forDatabaseKey: (BDBDatabaseEntry *) aKey { BDBOperationStatus status = BDB_STATUS_UNKNOWN; NS_DURING status = [self->database putEntryWithTransaction: nil key: aKey value: anEntry]; if( BDB_STATUS_SUCCESS != status ) { [[[FTDatabaseUpdateException alloc] initWithOperationStatus: status operationInformation: @"FTDictionaryServiceForGraphImpl::"\ "addDatabaseEntry: Error while updating the BDB database"] raise]; } NS_HANDLER FTDatabaseUpdateException *ex; [[FTLogging coreLog] error: @"FTDictionaryServiceForGraphImpl::addDatabaseEntry: Error while "\ "updating the BDB database"]; if( ![localException isKindOfClass: [FTDatabaseUpdateException class]] ) { ex = [[FTDatabaseUpdateException alloc] initWithOperationStatus: status operationInformation: @"FTDictionaryServiceForGraphImpl::"\ "addDatabaseEntry: Error while updating the BDB database"]; [ex setCause: localException]; } else { ex = (FTDatabaseUpdateException *) localException; } [ex raise]; NS_ENDHANDLER return self; } - addObject: (id ) anObject forKey: (id ) aKey forNode: (id ) aNode { BDBDatabaseEntry *dbKey, *dbValue; _FTDictionaryServiceKeysOfNode *keysOfNode = nil; if( [[FTLogging coreLog] isTraceEnabled] ) { [[FTLogging coreLog] trace: @"FTDictionaryServiceForGraphImpl::addObject: forKey=\"%@\""\ " forNodeId=\"%@\"", aKey, [aNode nodeId]]; } [self->writeLock lock]; NS_DURING /* add the data: */ dbKey = [self createDatabaseEntryKeyForNodeId: [aNode nodeId] forKey: aKey]; dbValue = [[[BDBDatabaseEntry alloc] initWithObject: anObject] autorelease]; [self addDatabaseEntry: dbValue forDatabaseKey: dbKey]; /* now add the key itself to the dictionary as well: */ keysOfNode = [[_FTDictionaryServiceKeysOfNode alloc] initForNode: aNode dictionaryServiceForGraph: self]; [keysOfNode addKey: aKey]; NS_HANDLER [self->writeLock unlock]; [keysOfNode release]; [localException raise]; NS_ENDHANDLER [keysOfNode release]; [self->writeLock unlock]; return self; } - (id ) allKeysOfNode: (id ) aNode { _FTDictionaryServiceKeysOfNode *dictOfNode = [[_FTDictionaryServiceKeysOfNode alloc] initForNode: aNode dictionaryServiceForGraph: self]; return [dictOfNode allKeys]; } - (BDBDatabaseEntry *) createDatabaseEntryKeyForNodeId: (id ) aNodeId forKey: (id ) aKey { NSMutableData *keyData = [[[NSMutableData alloc] init] autorelease]; NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: keyData] autorelease]; [archiver encodeObject: aNodeId forKey: @"nodeId"]; [archiver encodeObject: aKey forKey: @"key"]; [archiver finishEncoding]; return [[[BDBDatabaseEntry alloc] initWithData: keyData] autorelease]; } - (BDBDatabaseEntry *) databaseEntryForKey: (BDBDatabaseEntry *) aDBKey { BDBDatabaseEntry *toReturn = [[[BDBDatabaseEntry alloc] init] autorelease]; BDBOperationStatus opStatus = BDB_STATUS_UNKNOWN; NS_DURING opStatus = [self->database getEntryWithTransaction: nil key: aDBKey data:toReturn]; NS_HANDLER [[FTLogging coreLog] error: @"FTDictionaryServiceForGraphImpl::databaseEntryForKey: "\ "Operation failed due to exception: \"%@\". Key=%@", localException, aDBKey]; [localException raise]; NS_ENDHANDLER if( BDB_STATUS_SUCCESS != opStatus ) { if( BDB_STATUS_NOTFOUND == opStatus ) { /* entry not found -> return nil */ toReturn = nil; } else { [[[FTInternalDatamanagementException alloc] initWithOperationStatus: opStatus] raise]; } } return toReturn; } - objectForKey: (id ) aKey ofNode: (id ) aNode { BDBDatabaseEntry *dbKey; BDBDatabaseEntry *dbValue; id toReturn; if( [[FTLogging coreLog] isTraceEnabled] ) { [[FTLogging coreLog] trace: @"FTDictionaryServiceForGraphImpl::objectForKey"]; } dbKey = [self createDatabaseEntryKeyForNodeId: [aNode nodeId] forKey: aKey]; dbValue = [self databaseEntryForKey: dbKey]; if( nil != dbValue ) { toReturn = [dbValue object]; } return toReturn; } - openDatabaseForGraph: (FTGraphImpl *) aGraph usingDatabaseName: (NSString *) aDatabaseName { NSString *databaseName; BDBDatabaseConfig * dbConfig; if( YES == self->databaseIsOpen ) { if( nil != self->database ) { [self->database close]; [self->database release]; } } databaseName = [NSString stringWithFormat: @"%@/%@", [aGraph graphDatabaseDirectory], aDatabaseName]; dbConfig = [[[BDBDatabaseConfig alloc] init] autorelease]; [dbConfig setDatabaseType: BDB_BTREE]; [dbConfig setBTreeRecordNumbering: NO]; [dbConfig setAllowDuplicates: NO]; /* * Check wether a file databaseName already exists. If so, then simply * use it as dictionary service db; otherwise create one: */ if( ![[NSFileManager defaultManager] fileExistsAtPath: databaseName] ) { [dbConfig setAllowCreate: YES]; } self->database = [BDBDatabase initWithFilename: databaseName databaseName: nil databaseConfig: dbConfig]; if( nil != self->database ) { self->databaseIsOpen = YES; } return self; } - removeDatabaseEntryForKey: (BDBDatabaseEntry *) key { [self->database deleteEntryWithTransaction: nil key: key]; return self; } - removeObjectForKey: (id ) aKey ofNode: (id ) aNode { BDBDatabaseEntry *dbKey; _FTDictionaryServiceKeysOfNode *servicesOfNode; if( [[FTLogging coreLog] isTraceEnabled] ) { [[FTLogging coreLog] trace: @"FTDictionaryServiceForGraphImpl::removeObjectForKey" ]; } dbKey = [self createDatabaseEntryKeyForNodeId: [aNode nodeId] forKey: aKey]; [self removeDatabaseEntryForKey: dbKey]; servicesOfNode = [[[_FTDictionaryServiceKeysOfNode alloc] initForNode: aNode dictionaryServiceForGraph: self] autorelease]; [servicesOfNode removeKey: aKey]; return self; } - (id ) serviceForNode: (id ) aNode { return [[[FTDictionaryServiceForNodeImpl alloc] initForDictionaryServiceForGraph: self forNode: aNode] autorelease]; } - (id ) serviceLoader { return self->serviceLoader; } @end /** * Used as key in order to store keys per node */ #define _Key4NodeKey @"__nodeKey" @implementation _FTDictionaryServiceKeysOfNode - initForNode: (id ) aNode dictionaryServiceForGraph: (FTDictionaryServiceForGraphImpl *) aDictService { self = [super init]; self->node = [aNode retain]; self->dictionaryForGraph = [aDictService retain]; return self; } - (void) dealloc { [self-> node release]; [self->dictionaryForGraph release]; [super dealloc]; } - (id ) allKeys { return [[[ECEnumeratorIterator alloc] initWithEnumerator: [[self fetchKeys] objectEnumerator]] autorelease]; } - addKey: (id ) aKey { NSMutableSet *allKeys = [self fetchKeys]; [allKeys addObject: aKey]; [self storeKeys: allKeys]; return self; } - (BDBDatabaseEntry *) dbKeyForAllkeys { return [self->dictionaryForGraph createDatabaseEntryKeyForNodeId: [self->node nodeId] forKey: _Key4NodeKey]; } - (NSMutableSet *) fetchKeys { BDBDatabaseEntry *key; BDBDatabaseEntry *entry; NSMutableSet *toReturn; key = [self dbKeyForAllkeys]; entry = [self->dictionaryForGraph databaseEntryForKey: key]; if( nil == entry ) { toReturn = [[[NSMutableSet alloc] init] autorelease]; } else { toReturn = [entry object]; } return toReturn; } - removeKey: (id ) aKey { NSMutableSet *allKeys = [self fetchKeys]; if( [allKeys containsObject: aKey] ) { [allKeys removeObject: aKey]; [self storeKeys: allKeys]; } return self; } - storeKeys: (NSMutableSet *) allKeys { BDBDatabaseEntry *key = [self dbKeyForAllkeys]; BDBDatabaseEntry *value; [self->dictionaryForGraph removeDatabaseEntryForKey: key]; value = [[[BDBDatabaseEntry alloc] initWithObject: allKeys] autorelease]; [self->dictionaryForGraph addDatabaseEntry: value forDatabaseKey: key]; return self; } @end