home *** CD-ROM | disk | FTP | other *** search
- #import <appkit/appkit.h>
- #import "UniqueKey.h"
-
-
- static BOOL _Debug = YES;
- static id _connectionDictionary = nil;
-
-
- @implementation UniqueKey
-
- /******************************************************************************
- * Allow the programmer to specify login information for our separate UniqueKey
- * channel. If no information is supplied, we display our own login panel.
- ******************************************************************************/
- + setConnectionDictionary:(NSDictionary *)connectionDictionary
- {
- if(_connectionDictionary) [_connectionDictionary autorelease];
- _connectionDictionary = [connectionDictionary retain];
- return self;
- }
- + connectionDictionary
- {
- return [[_connectionDictionary retain] autorelease];
- }
-
-
- /******************************************************************************
- * The UniqueKey objects share a database channel that is sure
- * to be free to allow immediate reservation of a block of keys.
- ******************************************************************************/
- - initSharedChannel
- {
- NSString *modelName = @"UniqueKey";
- NSString *path;
- NSString *_entityName = @"UniqueKey";
- EOModel *eoModel;
- EOAdaptor *eoAdaptor;
- EODatabase *db;
- static EODatabaseContext *_sharedContext;
- static EODatabaseChannel *_sharedChannel;
- static EOEntity *_sharedEntity;
- static BOOL initFlag = NO;
-
- if(!initFlag) {
- path = [EOModel findPathForModelNamed:modelName];
- if(!path) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to find %s",NULL,NULL,NULL,[modelName cString]);
- return nil;
- }
-
- eoModel=[[EOModel alloc] initWithContentsOfFile:path];
- if(!eoModel) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to open %s",NULL,NULL,NULL,[path cString]);
- return nil;
- }
-
- eoAdaptor= [EOAdaptor adaptorWithModel:eoModel];
- if(!eoAdaptor) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to create adaptorWithModel:",NULL,NULL,NULL);
- return nil;
- }
-
- if([UniqueKey connectionDictionary]!=nil)
- [eoAdaptor setConnectionDictionary:[UniqueKey connectionDictionary]];
- if(![eoAdaptor hasValidConnectionDictionary])
- [eoAdaptor runLoginPanelAndValidateConnectionDictionary];
-
- db=[[EODatabase alloc] initWithAdaptor:eoAdaptor];
- if(!db) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to initWithAdaptor:",NULL,NULL,NULL);
- return nil;
- }
-
- _sharedContext=[[EODatabaseContext alloc] initWithDatabase:db];
- if(!_sharedContext) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to initWithDatabase:",NULL,NULL,NULL);
- return nil;
- }
-
- _sharedChannel=[[EODatabaseChannel alloc] initWithDatabaseContext:_sharedContext];
- if(!_sharedChannel) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to initWithDatabaseContext:",NULL,NULL,NULL);
- return nil;
- }
-
- _sharedEntity= [eoModel entityNamed:_entityName];
- if(!_sharedEntity) {
- NXRunAlertPanel([NXApp appName],
- "UniqueKey is unable to find entityNamed:%s",NULL,NULL,NULL,[_entityName cString]);
- return nil;
- }
-
- [[_sharedChannel adaptorChannel] openChannel];
- if(_Debug) [[_sharedChannel adaptorChannel] setDelegate:self];
-
- initFlag=TRUE;
- }
-
- dbContext = [_sharedContext retain];
- dbChannel = [_sharedChannel retain];
- uniqueKeyEntity = [_sharedEntity retain];
- return self;
- }
-
-
- /******************************************************************************
- * Init a UniqueKey object to reserve integer keys in blocks of <count> keys. Hold
- * off reserving any keys until the first call to nextKey. Each call to nextKey
- * returns a unique integer key. The first call reserves a block of <count> keys.
- * When the block has been used, a new block is reserved.
- *
- * UniqueKey uses a separate table to hold the external entity name and current
- * max reserved integer key. The UniqueKey objects share a database channel that
- * is sure to be free to allow immediate reservation of a block of keys.
- ******************************************************************************/
- - initWithEntity:(EOEntity*)entity count:(unsigned int)count
- {
- NSString *qualifierString;
- EOQualifier *tableNameQualifier;
- NSNumber *max;
-
- keyCount = count;
- nextKey = 0;
- maxKey = 0;
-
- if(!entity || ![self initSharedChannel]) return nil;
-
- // Construct the qualifier for our name
- qualifierString = [NSString stringWithFormat:@"EntityName = '%@'",[entity externalName]];
-
- tableNameQualifier = [[[EOQualifier allocWithZone:[self zone]]
- initWithEntity:uniqueKeyEntity qualifierFormat:qualifierString] autorelease];
-
- // Select the object and check for the presence of our name and "MaxKey"
- [dbContext beginTransaction];
- [dbChannel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil];
- tableMax=[[dbChannel fetchWithZone:[dbChannel zone]] retain];
- [dbChannel cancelFetch];
- [dbContext commitTransaction];
-
- // Was there an entry for our table name?
- if(!tableMax) {
- NSLog(@"Unique key was unable to fetch EntityName = %@",[entity externalName]);
- return nil;
- }
-
- // Then there should be a value for "MaxKey" for our table
- max=[tableMax objectForKey:@"MaxKey"];
- if(!max) {
- NSLog(@"Unique key method nextKey could not fine MaxKey");
- return nil;
- }
- return self;
- }
-
-
- /******************************************************************************
- * Hand out the next unique key from our reserved block. If the block is exhausted,
- * increment the max_id (high-water mark) and attempt to updateObject:. This
- * writes the new max_id back to the table. Under optimistic locking (the default)
- * the update will fail if another client has bumped up the max_id since our last use.
- * In that case, we need to refetchObject: and try again.
- ******************************************************************************/
- - (int) nextKey
- {
- NSNumber *max;
-
- // Hand out our next reserved key. If we are out of keys, fall through to
- // reserve another block of keyCount keys.
- if(++nextKey<=maxKey) return nextKey;
-
- // Attempt to bump up the count by keyCount. Fail on optomistic lock if someone
- // else reserved a chunck since our original select (or our last update).
- [dbContext beginTransaction];
- while(1) {
- max = [tableMax objectForKey:@"MaxKey"];
- maxKey = [max intValue]+keyCount;
- [tableMax setObject:[NSNumber numberWithInt:maxKey] forKey:@"MaxKey"];
- if([dbChannel updateObject:tableMax]) break;
-
- [dbContext rollbackTransaction];
- [dbContext beginTransaction];
- [dbChannel refetchObject:tableMax];
- }
- [dbContext commitTransaction];
-
- nextKey=maxKey-keyCount+1;
- return nextKey;
- }
-
-
- /******************************************************************************
- * Echo SQL when debug is enabled.
- ******************************************************************************/
- - (EODelegateResponse)adaptorChannel:channel
- willEvaluateExpression:(NSMutableString *)expression
- {
- NSLog(expression);
- return EODelegateApproves;
- }
-
-
- - (void)dealloc
- {
- [dbContext release];
- [dbChannel release];
- [uniqueKeyEntity release];
- [tableMax release];
- if (_connectionDictionary)
- [_connectionDictionary release];
- [super dealloc];
-
- }
-
- @end
-