/** EOUtilities.m Copyright (C) 2000-2002,2003,2004,2005 Free Software Foundation, Inc. Author: Manuel Guesdon Date: Sep 2000 $Revision$ $Date$ This file is part of the GNUstep Database Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 3 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "config.h" RCS_ID("$Id$") #ifdef GNUSTEP #include #include #include #include #include #include #include #else #include #endif #ifndef GNUSTEP #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "EOPrivate.h" NSString *EOMoreThanOneException = @"EOMoreThanOneException"; @implementation EOEditingContext (EOUtilities) - (NSArray *)objectsForEntityNamed: (NSString *)entityName { NSArray *objects; EOFetchSpecification *fetchSpecif; NSAssert([entityName length] > 0, @"No entity name"); fetchSpecif = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: nil sortOrderings: nil]; objects = [self objectsWithFetchSpecification: fetchSpecif]; return objects; } - (NSArray *)objectsOfClass: (Class)classObject { EOEntity *entity; NSArray *objects; entity = [self entityForClass: classObject]; objects = [self objectsForEntityNamed: [entity name]]; return objects; } - (NSArray *)objectsWithFetchSpecificationNamed: (NSString *)fetchSpecName entityNamed: (NSString *)entityName bindings: (NSDictionary *)bindings { EOModelGroup *modelGroup; EOFetchSpecification *unboundFetchSpec; EOFetchSpecification *boundFetchSpec; NSArray *results; modelGroup = [self modelGroup]; unboundFetchSpec = [modelGroup fetchSpecificationNamed: fetchSpecName entityNamed: entityName]; if ( !unboundFetchSpec ) { [NSException raise: NSObjectNotAvailableException format: @"%@: Fetch specification '%@' not found in entity named '%@'", NSStringFromSelector(_cmd), fetchSpecName, entityName]; } boundFetchSpec = [unboundFetchSpec fetchSpecificationWithQualifierBindings: bindings]; results = [self objectsWithFetchSpecification: boundFetchSpec]; return results; } - (NSArray *)objectsForEntityNamed: (NSString *)entityName qualifierFormat: (NSString *)format,... { EOQualifier *qualifier; EOFetchSpecification *fetchSpec; NSArray *results; va_list args; va_start(args, format); qualifier = [EOQualifier qualifierWithQualifierFormat: format varargList: args]; va_end(args); fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: qualifier sortOrderings: nil]; results = [self objectsWithFetchSpecification: fetchSpec]; return results; } - (NSArray *)objectsMatchingValue: (id)value forKey: (NSString *)key entityNamed: (NSString *)entityName { //OK NSArray *objects; NSDebugMLLog(@"gsdb", @"START value=%@ key=%@ entityName=%@", value, key, entityName); if (!value) value=GDL2_EONull; NSAssert(value, @"No Value"); //Transform it to EONull ? NSAssert(key, @"No Key"); NSAssert([entityName length] > 0, @"No entity name"); objects = [self objectsMatchingValues: [NSDictionary dictionaryWithObject: value forKey: key] entityNamed: entityName]; return objects; //TC: /* EOKeyValueQualifier *qualifier; EOFetchSpecification *fetch; NSArray *newArray; qualifier = [[EOKeyValueQualifier alloc] initWithKey:key operatorSelector:EOQualifierOperatorEqual value:value]; fetch = [EOFetchSpecification fetchSpecificationWithEntityName:name qualifier:[qualifier autorelease] sortOrderings:nil]; newArray = [self objectsWithFetchSpecification:fetch]; return newArray; */ } - (NSArray *)objectsMatchingValues: (NSDictionary *)values entityNamed: (NSString *)entityName { //OK NSArray *objects = nil; EOFetchSpecification *fetchSpec; EOQualifier *qualifier; NSEnumerator *valuesEnum; id key=nil; NSMutableArray* kvQualifiers=nil; NS_DURING { NSAssert([entityName length] > 0, @"No entity name"); valuesEnum = [values keyEnumerator]; kvQualifiers = [NSMutableArray array]; while ((key = [valuesEnum nextObject])) { id value = [values objectForKey: key]; EOKeyValueQualifier *tmpQualifier = [EOKeyValueQualifier qualifierWithKey: key operatorSelector: @selector(isEqualTo:) value: value]; [kvQualifiers addObject: tmpQualifier]; } qualifier = [EOAndQualifier qualifierWithQualifierArray: kvQualifiers]; fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: qualifier sortOrderings: nil]; objects = [self objectsWithFetchSpecification: fetchSpec]; } NS_HANDLER { NSDebugMLog(@"exception in EOEditingContext (EOUtilities) objectsMatchingValues:entityNamed:", ""); NSLog(@"exception in EOEditingContext (EOUtilities) objectsMatchingValues:entityNamed:"); NSDebugMLog(@"exception=%@", localException); NSLog(@"exception=%@", localException); /* localException=ExceptionByAddingUserInfoObjectFrameInfo(localException, @"In EOEditingContext (EOUtilities) objectsMatchingValues:entityNamed:"); */ NSLog(@"exception=%@", localException); [localException raise]; } NS_ENDHANDLER; return objects; //TC: /* EOQualifier *qualifier; EOFetchSpecification *fetchSpec; NSArray *results; qualifier = [EOQualifier qualifierToMatchAllValues:values]; fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:name qualifier:qualifier sortOrderings:nil]; results = [self objectsWithFetchSpecification:fetchSpec]; return results; */ } - (id)objectWithFetchSpecificationNamed: (NSString *)fetchSpecName entityNamed: (NSString *)entityName bindings: (NSDictionary *)bindings { id object = nil; int count; NSArray *objects; NSAssert([entityName length] > 0, @"No entity name"); objects = [self objectsWithFetchSpecificationNamed:fetchSpecName entityNamed:entityName bindings:bindings]; count = [objects count]; switch (count) { case 0: [NSException raise: NSInvalidArgumentException format: @"%@: No item selected for fetch specification %@ in entity %@ with bindings %@", NSStringFromSelector(_cmd), fetchSpecName, entityName, bindings]; break; case 1: object = [objects objectAtIndex: 0]; break; default: [NSException raise: EOMoreThanOneException format: @"%@: Selected more than one item for fetch specification %@ in entity %@ with bindings %@", NSStringFromSelector(_cmd), fetchSpecName, entityName, bindings]; break; } return object; } - (id)objectForEntityNamed: (NSString *)entityName qualifierFormat: (NSString *)format,... { id object = nil; int count; EOQualifier *qualifier; EOFetchSpecification *fetchSpec; NSArray *objects; va_list args; va_start(args, format); qualifier = [EOQualifier qualifierWithQualifierFormat: format varargList: args]; va_end(args); fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: qualifier sortOrderings: nil]; objects = [self objectsWithFetchSpecification: fetchSpec]; count = [objects count]; switch (count) { case 0: [NSException raise: NSInvalidArgumentException format: @"%@: No item selected for entity %@ qualified by %@", NSStringFromSelector(_cmd), entityName, format]; break; case 1: object = [objects objectAtIndex: 0]; break; default: [NSException raise: EOMoreThanOneException format: @"%@: Selected more than one item for entity %@ qualified by %@", NSStringFromSelector(_cmd), entityName, format]; } return object; } - (id)objectMatchingValue: (id)value forKey: (NSString *)key entityNamed: (NSString *)entityName { //OK id object = nil; int count; NSArray *objects; NSDebugMLLog(@"gsdb", @"START value=%@ key=%@ entityName=%@", value, key, entityName); NS_DURING //Debugging Purpose { NSAssert([entityName length] > 0, @"No entity name"); objects = [self objectsMatchingValue: value forKey: key entityNamed: entityName]; count = [objects count]; switch (count) { case 0: [NSException raise: NSObjectNotAvailableException format: @"%@: No %@ found with key %@ matching %@", NSStringFromSelector(_cmd), entityName, key, value]; break; case 1: object = [objects objectAtIndex: 0]; break; default: [NSException raise: EOMoreThanOneException format: @"%@: Selected more than one %@ with key %@ matching %@", NSStringFromSelector(_cmd), entityName, key, value]; break; } } NS_HANDLER { NSLog(@"exception in EOEditingContext (EOUtilities) objectMatchingValue:forKey:entityNamed:"); NSLog(@"exception=%@", localException); /* localException=ExceptionByAddingUserInfoObjectFrameInfo(localException, @"In EOEditingContext (EOUtilities) objectMatchingValue:forKey:entityNamed:"); */ NSLog(@"exception=%@", localException); [localException raise]; } NS_ENDHANDLER; return object; } - (id)objectMatchingValues: (NSDictionary *)values entityNamed: (NSString *)entityName { id object = nil; int count; NSArray *objects; NSAssert([entityName length] > 0, @"No entity name"); objects = [self objectsMatchingValues: values entityNamed: entityName]; count = [objects count]; switch(count) { case 0: [NSException raise: NSInvalidArgumentException format: @"%@: No %@ found matching %@", NSStringFromSelector(_cmd), entityName, values]; break; case 1: object = [objects objectAtIndex: 0]; break; default: [NSException raise: EOMoreThanOneException format: @"%@: Selected more than one %@ matching %@", NSStringFromSelector(_cmd), entityName, values]; break; } return object; } - (id)objectWithPrimaryKeyValue: (id)value entityNamed: (NSString *)entityName { //OK id object = nil; EOEntity *entity; NSAssert([entityName length] > 0, @"No entity name"); entity = [self entityNamed: entityName]; if (!entity) [NSException raise: NSInvalidArgumentException format: @"objectWithPrimaryKeyValue:%@ entityNamed:%@; no entity", value, entityName]; else { NSArray *primaryKeyAttributes = [entity primaryKeyAttributes]; if ([primaryKeyAttributes count] != 1) { [NSException raise: NSInvalidArgumentException format: @"objectWithPrimaryKeyValue:%@ entityNamed:%@ may only be called for entities with one primary key attribute. For entities with compound primary keys, use objectWithPrimaryKey:entityNamed and provide a dictionary for the primary key.", value, entityName]; } else { NSDictionary* pk; if (!value) value=GDL2_EONull; pk = [NSDictionary dictionaryWithObject: value forKey: [(EOAttribute*)[primaryKeyAttributes objectAtIndex: 0] name]]; object = [self objectWithPrimaryKey: pk entityNamed: entityName]; } } return object; } - (id)objectWithPrimaryKey: (NSDictionary *)pkDict entityNamed: (NSString *)entityName { //OK id object = nil; EOEntity *entity; NSAssert([pkDict count] > 0, @"Empty primary key."); NSAssert([entityName length] > 0, @"No entity name"); entity = [self entityNamed: entityName]; if (!entity) [NSException raise: NSInvalidArgumentException format: @"objectWithPrimaryKeyValue:%@ entityNamed:%@; no entity", pkDict, entityName]; else { EOGlobalID *gid = [entity globalIDForRow: pkDict]; object = [self faultForGlobalID: gid editingContext: self]; } return object; } - (NSArray *)rawRowsForEntityNamed: (NSString *)entityName qualifierFormat: (NSString *)format,... { EOQualifier *qualifier; EOFetchSpecification *fetchSpec; NSArray *results; va_list args; va_start(args, format); qualifier = [EOQualifier qualifierWithQualifierFormat: format varargList: args]; va_end(args); NSAssert([entityName length] > 0, @"No entity name"); fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: qualifier sortOrderings: nil]; [fetchSpec setFetchesRawRows: YES]; results = [self objectsWithFetchSpecification: fetchSpec]; return results; } - (NSArray *)rawRowsMatchingValue: (id)value forKey: (NSString *)key entityNamed: (NSString *)entityName { NSDictionary *valueDict; NSArray *results; NSAssert([entityName length]>0,@"No entity name"); if (!value) value=GDL2_EONull; valueDict = [NSDictionary dictionaryWithObject: value forKey: key]; results = [self rawRowsMatchingValues: valueDict entityNamed: entityName]; return results; } - (NSArray *)rawRowsMatchingValues: (NSDictionary *)values entityNamed: (NSString *)entityName { EOQualifier *qualifier; EOFetchSpecification *fetchSpec; NSArray *results; NSAssert([entityName length] > 0, @"No entity name"); qualifier = [EOQualifier qualifierToMatchAllValues: values]; fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: qualifier sortOrderings: nil]; [fetchSpec setFetchesRawRows: YES]; results = [self objectsWithFetchSpecification: fetchSpec]; return results; } - (NSArray *)rawRowsWithSQL: (NSString *)sqlString modelNamed: (NSString *)name { EODatabaseContext *databaseContext; EODatabaseChannel *databaseChannel; EOAdaptorChannel *adaptorChannel; NSMutableArray *results = nil; NSArray *attributes = nil; NSDictionary *row; databaseContext = [self databaseContextForModelNamed: name]; [databaseContext lock]; NS_DURING { databaseChannel = [databaseContext availableChannel]; adaptorChannel = [databaseChannel adaptorChannel]; if (![adaptorChannel isOpen]) [adaptorChannel openChannel]; [adaptorChannel evaluateExpression: [EOSQLExpression expressionForString: sqlString]]; attributes = [adaptorChannel describeResults]; [adaptorChannel setAttributesToFetch:attributes]; results = [NSMutableArray array]; while ((row = [adaptorChannel fetchRowWithZone: [self zone]])) [results addObject: row]; [databaseContext unlock]; } NS_HANDLER { [databaseContext unlock]; [localException raise]; } NS_ENDHANDLER; return results; } - (NSArray *)rawRowsWithStoredProcedureNamed: (NSString *)name arguments: (NSDictionary *)args { EODatabaseContext *databaseContext; EODatabaseChannel *databaseChannel; EOAdaptorChannel *adaptorChannel; EOStoredProcedure *storedProcedure; NSMutableArray *results = nil; NSDictionary *row; storedProcedure = [[self modelGroup] storedProcedureNamed: name]; databaseContext = [self databaseContextForModelNamed: [[storedProcedure model] name]]; [databaseContext lock]; NS_DURING { databaseChannel = [databaseContext availableChannel]; adaptorChannel = [databaseChannel adaptorChannel]; if (![adaptorChannel isOpen]) [adaptorChannel openChannel]; [adaptorChannel executeStoredProcedure: storedProcedure withValues: args]; [adaptorChannel setAttributesToFetch: [adaptorChannel describeResults]]; results = [NSMutableArray array]; while ((row = [adaptorChannel fetchRowWithZone: [self zone]])) [results addObject: row]; [databaseContext unlock]; } NS_HANDLER { [databaseContext unlock]; [localException raise]; } NS_ENDHANDLER; return results; } - (NSDictionary *)executeStoredProcedureNamed: (NSString *)name arguments: (NSDictionary *)args { EODatabaseContext *databaseContext; EODatabaseChannel *databaseChannel; EOAdaptorChannel *adaptorChannel; EOStoredProcedure *storedProcedure; NSDictionary *returnValues = nil; storedProcedure = [[self modelGroup] storedProcedureNamed: name]; databaseContext = [self databaseContextForModelNamed: [[storedProcedure model] name]]; [databaseContext lock]; NS_DURING { databaseChannel = [databaseContext availableChannel]; adaptorChannel = [databaseChannel adaptorChannel]; if (![adaptorChannel isOpen]) [adaptorChannel openChannel]; [adaptorChannel executeStoredProcedure: storedProcedure withValues: args]; returnValues = [adaptorChannel returnValuesForLastStoredProcedureInvocation]; [databaseContext unlock]; } NS_HANDLER { [databaseContext unlock]; [localException raise]; } NS_ENDHANDLER; return returnValues; } - (id)objectFromRawRow: (NSDictionary *)row entityNamed: (NSString *)entityName { NSAssert([entityName length] > 0, @"No entity name"); return [self faultForRawRow: row entityNamed: entityName]; } - (EODatabaseContext *)databaseContextForModelNamed: (NSString *)name { EOModelGroup *modelGroup; EOModel *model; EODatabaseContext *databaseContext; modelGroup = [self modelGroup]; model = [modelGroup modelNamed: name]; if ( !model ) [NSException raise: NSInvalidArgumentException format: @"%@: cannot find model named %@ associated with this EOEditingContext", NSStringFromSelector(_cmd), name]; databaseContext = [EODatabaseContext registeredDatabaseContextForModel: model editingContext: self]; return databaseContext; } - (void)connectWithModelNamed: (NSString *)name connectionDictionaryOverrides: (NSDictionary *)overrides { EOModel *model; model = [[self modelGroup] modelNamed: name]; [self notImplemented: _cmd]; } - (id)createAndInsertInstanceOfEntityNamed: (NSString *)entityName { id object; EOClassDescription *classDescription; classDescription = [EOClassDescription classDescriptionForEntityName: entityName]; if (!classDescription) [NSException raise: NSInvalidArgumentException format: @"%@ could not find class description for entity named %@", NSStringFromSelector(_cmd), entityName]; object = [classDescription createInstanceWithEditingContext: self globalID: nil zone: [self zone]]; [self insertObject: object]; return object; } - (NSDictionary *)primaryKeyForObject: (id)object { EOKeyGlobalID *gid; EOEntity *entity; NSDictionary *newDict; gid = (EOKeyGlobalID *)[self globalIDForObject: object]; entity = [self entityForObject: object]; newDict = [entity primaryKeyForGlobalID: gid]; return newDict; } - (NSDictionary *)destinationKeyForSourceObject: (id)object relationshipNamed: (NSString*)name { EODatabaseContext *databaseContext; EODatabase *database; EOEntity *sourceEntity; EORelationship *relationship; NSArray *joins; EOJoin *join; NSString *sourceAttributeName; NSString *destinationAttributeName; NSDictionary *snapshot; NSMutableDictionary *result = nil; int i, count; sourceEntity = [self entityForObject: object]; relationship = [sourceEntity relationshipNamed: name]; if (!relationship) [NSException raise: NSInvalidArgumentException format: @"%@: entity %@ does not have relationship named %@", NSStringFromSelector(_cmd), [sourceEntity name], name]; databaseContext = [self databaseContextForModelNamed: [[sourceEntity model] name]]; [databaseContext lock]; NS_DURING { database = [databaseContext database]; snapshot = [database snapshotForGlobalID:[self globalIDForObject: object]]; joins = [relationship joins]; count = [joins count]; result = (NSMutableDictionary *)[NSMutableDictionary dictionary]; for (i = 0 ; i < count ; i++) { join = [joins objectAtIndex: i]; sourceAttributeName = [[join sourceAttribute] name]; destinationAttributeName = [[join destinationAttribute] name]; [result setObject: [snapshot objectForKey: sourceAttributeName] forKey: destinationAttributeName]; } [databaseContext unlock]; } NS_HANDLER { [databaseContext unlock]; [localException raise]; } NS_ENDHANDLER; return result; } - (id)localInstanceOfObject: (id)object { EOGlobalID *gid; id newInstance; gid = [[object editingContext] globalIDForObject:object]; newInstance = [self faultForGlobalID: gid editingContext: self]; return newInstance; } - (NSArray *)localInstancesOfObjects: (NSArray *)objects { NSMutableArray *array; int i, count = [objects count]; id obj; array = [NSMutableArray arrayWithCapacity: count]; for (i = 0; i < count; i++) { obj = [self localInstanceOfObject: [objects objectAtIndex: i]]; [array addObject: obj]; } return array; } - (EOModelGroup *)modelGroup { EOObjectStore *rootObjectStore; EOObjectStoreCoordinator *objectStoreCoordinator; EOModelGroup *modelGroup; rootObjectStore = [self rootObjectStore]; if (![rootObjectStore isKindOfClass: [EOObjectStoreCoordinator class]]) [NSException raise: NSInvalidArgumentException format: @"%@: an EOEditingContext's root object store must be an EOObjectStoreCoordinator for this method to function.", NSStringFromSelector(_cmd)]; objectStoreCoordinator = (EOObjectStoreCoordinator *)rootObjectStore; modelGroup = [objectStoreCoordinator modelGroup]; return modelGroup; } - (EOEntity *)entityNamed: (NSString *)entityName { EOEntity *entity; EOModelGroup *modelGroup; NSAssert([entityName length] > 0, @"No entity name"); modelGroup = [self modelGroup]; NSAssert(modelGroup, @"No model group"); entity = [modelGroup entityNamed: entityName]; if (!entity) [NSException raise: NSInvalidArgumentException format: @"%@: could not find entity named:%@", NSStringFromSelector(_cmd), entityName]; return entity; } - (EOEntity *)entityForClass: (Class)classObject { EOModelGroup *modelGroup; NSArray *models; EOModel *model; int modelCount; NSArray *entities; EOEntity *entity; NSString *className; NSString *entityClassName; int i, j, entityCount; EOEntity *result = nil; BOOL matchesClassName; className = NSStringFromClass(classObject); modelGroup = [self modelGroup]; models = [modelGroup models]; modelCount = [models count]; for (i = 0 ; i < modelCount ; i++) { model = [models objectAtIndex: i]; entities = [model entities]; entityCount = [entities count]; for (j = 0 ; j < entityCount ; j++) { entity = [entities objectAtIndex: j]; entityClassName = [entity className]; matchesClassName = [className isEqualToString: entityClassName]; // Java class names in the Objective-C run-time system use '/' instead of '.' to separate package names, so we also check for a class name in which we replaced '.' with '/'. if (!matchesClassName && ([entityClassName rangeOfString:@"."].length != 0)) matchesClassName = [className isEqualToString: [[entityClassName componentsSeparatedByString: @"."] componentsJoinedByString: @"/"]]; if (matchesClassName) { if (result) [NSException raise: EOMoreThanOneException format: @"%@ found more than one entity for class named %@", NSStringFromSelector(_cmd), className]; else result = entity; } } } if (!result) [NSException raise: NSObjectNotAvailableException format: @"%@ could not find entity for class named %@", NSStringFromSelector(_cmd), className]; return result; } - (EOEntity *)entityForObject: (id)object { EOClassDescription *classDesc; EOEntity *newEntity; classDesc = [(EOGenericRecord *)object classDescription]; if ([classDesc isKindOfClass: [EOEntityClassDescription class]] == NO) [NSException raise: NSInvalidArgumentException format: @"%@ - %@: the object's class description must be an EOEntityClassDescription", NSStringFromSelector(_cmd), object]; newEntity = [(EOEntityClassDescription *)classDesc entity]; return newEntity; } @end @implementation EOFetchSpecification (EOAccess) + (EOFetchSpecification *)fetchSpecificationNamed: (NSString *)name entityNamed: (NSString *)entityName { EOFetchSpecification *newEOFetchSpecification = nil; EOModelGroup *anModelGroup; anModelGroup = [EOModelGroup defaultGroup]; if (anModelGroup) newEOFetchSpecification = [anModelGroup fetchSpecificationNamed: name entityNamed: entityName]; return newEOFetchSpecification; } @end