|
|
Sample Programs from NeXSTEP 3.3
/* KeyGenerator.m created by cfeder on Wed 26-Oct-1994 */
#import "KeyGenerator.h"
#import "ValueForKey.h"
// Default implementation for all EOs to generate their own primary keys
@implementation NSObject (assignPrimaryKey)
- (void)assignPrimaryKeyForEntity:(EOEntity *)entity
{
NSString *primaryKeyName = [[entity primaryKeyAttributeNames] objectAtIndex:0];
NSNumber *value = [NSNumber numberWithUnsignedInt:[KeyGenerator nexKeyForEntity:entity]];
NSDictionary *pkDict = [NSDictionary dictionaryWithObjects:&value forKeys:&primaryKeyName count:1];
[self takeValuesFromDictionary:pkDict];
}
- (void)assignPrimaryKeyIfNotAlreadyPresentForEntity:(EOEntity *)entity
{
// Figure out whether we already have a key assigned
BOOL hasKey = NO;
NSEnumerator *keys = [[entity primaryKeyAttributeNames] objectEnumerator];
NSString *key;
while (key = [keys nextObject]) {
id value = [self valueForKey:key];
if (value && (value != [EONull null]) && (![value isKindOfClass:[NSNumber class]] || ([value intValue]!=0))){
hasKey = YES;
break;
}
}
// if we don't already have a primary key, then assign ourselves one
// get our current primary key values
if (!hasKey) {
[self assignPrimaryKeyForEntity:entity];
}
}
@end
// Assumptions:
// Model contains:
// - login information
// - UniqueKey entity with attributes entity_name, and max_key
@implementation KeyGenerator
#define RESERVE_SET_SIZE 5
- initWithEntity:(EOEntity *)anEntity dataSource:(EODatabaseDataSource *)aDataSource
{
[super init];
entity = [anEntity retain];
dataSource = [aDataSource retain];
lastGranted = lastReserved = 0;
return self;
}
- (unsigned)nextKey
{
if (lastGranted >= lastReserved) {
// Go out to database and reserve a block of keys
EOQualifier *tableNameQualifier;
EODatabaseChannel *channel = [dataSource databaseChannel];
EODatabaseContext *context = [channel databaseContext];
NSString *externalName = [entity externalName];
EOEntity *uniqueKeyEntity = [dataSource entity];
if (!uniqueKeyEntity)
[NSException raise:NSInternalInconsistencyException format:@"%s: model does not contain UniqueKey entity '%@'", sel_getName(_cmd), [entity name]];
if (![channel isOpen])
[channel openChannel];
[context beginTransaction];
// This is the first time for this entity. We need to get the current setting.
if (!currentMaxRecord) {
// Construct the qualifier for our name
tableNameQualifier = [[[EOQualifier alloc] initWithEntity:uniqueKeyEntity
qualifierFormat:@"%A= '%@'", @"entity_name", externalName] autorelease];
[channel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil];
currentMaxRecord = [[channel fetchWithZone:[self zone]] retain];
[channel cancelFetch];
}
if (currentMaxRecord) {
// attempt to update the max_key value
for(;;) {
lastGranted = [[currentMaxRecord objectForKey:@"max_key"] unsignedIntValue];
lastReserved = lastGranted + RESERVE_SET_SIZE;
[currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
if ([channel updateObject:currentMaxRecord] && [context commitTransaction])
break; // success!!
// Update failed. Setup for another pass
[context rollbackTransaction];
[context beginTransaction];
[channel refetchObject:currentMaxRecord];
}
} else {
// we have to insert a record for this entity
currentMaxRecord = [[EOGenericRecord alloc] initWithPrimaryKey:nil entity:uniqueKeyEntity]; // retained
[currentMaxRecord setObject:externalName forKey:@"entity_name"];
lastReserved = RESERVE_SET_SIZE;
lastGranted = 0;
[currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
NSLog(@"Entity '%@' not in UniqueKey table. Adding record: %@", [entity name], currentMaxRecord);
if (![channel insertObject:currentMaxRecord])
[NSException raise:NSInternalInconsistencyException format:@"Failed to insert UniqueKey object for entity: %@", [entity name]];
[context commitTransaction];
}
}
lastGranted++;
return lastGranted;
}
- (void)dealloc
{
[entity release];
[dataSource release];
[super dealloc];
}
//
// Class methods to automatically cache instances
//
static NSMutableDictionary *entityNameToGenerator;
+ (EODatabaseDataSource *)dataSourceForModel:(EOModel *)model
{
// use the datasource rendevous mechanism to do our caching for us
return [[EODatabaseDataSource alloc] initWithModelName:[model name]
entityName:@"unique_key"
databaseName:nil
contextName:@"_UniqueKey"
channelName:nil];
}
+ keyGeneratorForEntity:(EOEntity *)anEntity
{
KeyGenerator *kg = [entityNameToGenerator objectForKey:[anEntity name]];
if (!kg) {
EODatabaseDataSource *ds = [self dataSourceForModel:[anEntity model]];
if (!ds)
[NSException raise:NSInternalInconsistencyException format:@"%s: unable to allocation UniqueKey data source for entity '%@'", sel_getName(_cmd), [anEntity name]];
kg = [[KeyGenerator alloc] initWithEntity:anEntity dataSource:ds];
if (kg) {
if (!entityNameToGenerator)
entityNameToGenerator = [NSMutableDictionary new];
[entityNameToGenerator setObject:kg forKey:[anEntity name]];
}
}
return kg;
}
+ (unsigned)nexKeyForEntity:(EOEntity *)anEntity
{
KeyGenerator *kg = [self keyGeneratorForEntity:anEntity];
if (!kg)
[NSException raise:NSInternalInconsistencyException format:@"%s: unable to create KeyGenerator for entity '%@'", sel_getName(_cmd), [anEntity name]];
return [kg nextKey];
}
@end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.