/** EODatabaseContext.m EODatabaseContext Class Copyright (C) 2000-2003 Free Software Foundation, Inc. Author: Mirko Viviani Date: June 2000 Author: Manuel Guesdon Date: October 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 2 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. **/ #include "config.h" RCS_ID("$Id$") #ifdef GNUSTEP #include #include #include #include #include #include #include #include #include #include #include #include #else #include #endif #ifndef GNUSTEP #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _LOCK_BUFFER 128 NSString *EODatabaseChannelNeededNotification = @"EODatabaseChannelNeededNotification"; NSString *EODatabaseContextKey = @"EODatabaseContextKey"; NSString *EODatabaseOperationsKey = @"EODatabaseOperationsKey"; NSString *EOFailedDatabaseOperationKey = @"EOFailedDatabaseOperationKey"; @interface EODatabaseContext(EOObjectStoreSupportPrivate) - (id) entityForGlobalID: (EOGlobalID *)globalID; @end @implementation EODatabaseContext // Initializing instances static Class _contextClass = Nil; + (void)initialize { if (!_contextClass) { _contextClass = [EODatabaseContext class]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_registerDatabaseContext:) name: EOCooperatingObjectStoreNeeded object: nil]; } } + (EODatabaseContext*)databaseContextWithDatabase: (EODatabase *)database { return AUTORELEASE([[self alloc] initWithDatabase: database]); } + (void)_registerDatabaseContext:(NSNotification *)notification { EOObjectStoreCoordinator *coordinator = [notification object]; EODatabaseContext *dbContext = nil; EOModel *model = nil; NSString *entityName = nil; id keyValue = nil; keyValue = [[notification userInfo] objectForKey: @"globalID"]; if (keyValue == nil) keyValue = [[notification userInfo] objectForKey: @"fetchSpecification"]; if (keyValue == nil) keyValue = [[notification userInfo] objectForKey: @"object"]; if (keyValue) entityName = [keyValue entityName]; model = [[[EOModelGroup defaultGroup] entityNamed:entityName] model]; if (model == nil) NSLog(@"%@ -- %@ 0x%x: No model for entity named %@", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, entityName); dbContext = [EODatabaseContext databaseContextWithDatabase: [EODatabase databaseWithModel: model]]; [coordinator addCooperatingObjectStore:dbContext]; } - (void) registerForAdaptorContextNotifications: (EOAdaptorContext*)adaptorContext { //OK [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_beginTransaction) name: EOAdaptorContextBeginTransactionNotification object: adaptorContext]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_commitTransaction) name: EOAdaptorContextCommitTransactionNotification object: adaptorContext]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_rollbackTransaction) name: EOAdaptorContextRollbackTransactionNotification object: adaptorContext]; } - (id) initWithDatabase: (EODatabase *)database { //OK EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"database=%p",database); if ((self = [super init])) { _adaptorContext = RETAIN([[database adaptor] createAdaptorContext]); if (_adaptorContext == nil) { NSLog(@"EODatabaseContext could not create adaptor context"); AUTORELEASE(self); return nil; } _database = RETAIN(database); // Register this object into database [_database registerContext: self]; [self setUpdateStrategy: EOUpdateWithOptimisticLocking]; _uniqueStack = [NSMutableArray new]; _deleteStack = [NSMutableArray new]; _uniqueArrayStack = [NSMutableArray new]; _registeredChannels = [NSMutableArray new]; _batchFaultBuffer = [NSMutableDictionary new]; _batchToManyFaultBuffer = [NSMutableDictionary new]; // We want to know when snapshots change in database [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_snapshotsChangedInDatabase:) name: EOObjectsChangedInStoreNotification object: _database]; // We want to know when objects change [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_objectsChanged:) name: EOObjectsChangedInStoreNotification object: self]; [self registerForAdaptorContextNotifications: _adaptorContext]; //??? /*NO _snapshots = [NSMutableDictionary new]; _toManySnapshots = [NSMutableDictionary new]; */ //NO _lock = [NSRecursiveLock new]; //NO _numLocked = 0; //After _lockedObjects = NSZoneMalloc(NULL, _LOCK_BUFFER*sizeof(id)); /* //TODO ? transactionStackTop = NULL; transactionNestingLevel = 0; isKeepingSnapshots = YES; isUniquingObjects = [database uniquesObjects]; [database contextDidInit:self];*/ } EOFLOGObjectFnStop(); return self; } - (void)_snapshotsChangedInDatabase: (NSNotification *)notification { //OK EOObjectsChangedInStoreNotification EODatabase EOFLOGObjectFnStart(); if ([notification object] == _database)//?? [[NSNotificationCenter defaultCenter] postNotificationName: [notification name] object: self userInfo: [notification userInfo]];//==> _objectsChanged EOFLOGObjectFnStop(); } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self]; [_database unregisterContext: self]; DESTROY(_adaptorContext); DESTROY(_database); if (_dbOperationsByGlobalID) { NSDebugMLog(@"MEMORY: dbOperationsByGlobalID count=%u", NSCountMapTable(_dbOperationsByGlobalID)); NSFreeMapTable(_dbOperationsByGlobalID); _dbOperationsByGlobalID = NULL; } /*NO DESTROY(_snapshots); DESTROY(_toManySnapshots); */ DESTROY(_uniqueStack); DESTROY(_deleteStack); DESTROY(_uniqueArrayStack); DESTROY(_registeredChannels); DESTROY(_batchFaultBuffer); DESTROY(_batchToManyFaultBuffer); DESTROY(_lastEntity); if (_nonPrimaryKeyGenerators) { NSDebugMLog(@"MEMORY: nonPrimaryKeyGnerators count=%u", NSCountHashTable(_nonPrimaryKeyGenerators)); NSFreeHashTable(_nonPrimaryKeyGenerators); _nonPrimaryKeyGenerators = NULL; } if (_lockedObjects) NSZoneFree(NULL, _lockedObjects); // Turbocat DESTROY(_lock); [super dealloc]; } + (EODatabaseContext *)registeredDatabaseContextForModel: (EOModel *)model editingContext: (EOEditingContext *)editingContext { EOObjectStoreCoordinator *edObjectStore; NSArray *cooperatingObjectStores; NSEnumerator *storeEnum; EOCooperatingObjectStore *coObjectStore; EODatabase *anDatabase; NSArray *models; EODatabaseContext *dbContext = nil; EOFLOGClassFnStartOrCond2(@"DatabaseLevel", @"EODatabaseContext"); if (model && editingContext) { edObjectStore = (EOObjectStoreCoordinator *)[editingContext rootObjectStore]; cooperatingObjectStores = [edObjectStore cooperatingObjectStores]; // get all EODatabaseContexts storeEnum = [cooperatingObjectStores objectEnumerator]; while ((coObjectStore = [storeEnum nextObject])) { if ([coObjectStore isKindOfClass: [EODatabaseContext class]]) { anDatabase = [(EODatabaseContext *)coObjectStore database]; if (anDatabase && (models = [anDatabase models])) { if ([models containsObject: model]) { dbContext = (EODatabaseContext *)coObjectStore; break; } } } } if (!dbContext) { // no EODatabaseContext found, create a new one dbContext = [EODatabaseContext databaseContextWithDatabase: [EODatabase databaseWithModel: model]]; if (dbContext) { [edObjectStore addCooperatingObjectStore: dbContext]; } } } EOFLOGClassFnStopOrCond2(@"DatabaseLevel", @"EODatabaseContext"); return dbContext; } + (Class)contextClassToRegister { NSEmitTODO(); // TODO; return _contextClass; } + (void)setContextClassToRegister: (Class)contextClass { _contextClass = contextClass; } /** Returns YES if we have at least one busy channel **/ - (BOOL)hasBusyChannels { BOOL busy = NO; int i = 0; int count = 0; count = [_registeredChannels count]; for (i = 0 ; !busy && i < count; i++) { EODatabaseChannel *channel = [[_registeredChannels objectAtIndex: i] nonretainedObjectValue]; busy = [channel isFetchInProgress]; } return busy; } - (NSArray *)registeredChannels { NSMutableArray *array = nil; int i, count; count = [_registeredChannels count]; array = [NSMutableArray arrayWithCapacity: count]; for (i = 0; i < count; i++) [array addObject: [[_registeredChannels objectAtIndex: i] nonretainedObjectValue]]; return array; } - (void)registerChannel: (EODatabaseChannel *)channel { //call channel databaseContext //test if not exists _registeredChannels indexOfObjectIdenticalTo:channel NSLog(@"** REGISTER channel ** debug:%d ** total registered:%d", [[channel adaptorChannel] isDebugEnabled], [_registeredChannels count] + 1); [_registeredChannels addObject: [NSValue valueWithNonretainedObject: channel]]; [channel setDelegate: nil]; } - (void)unregisterChannel: (EODatabaseChannel *)channel { int i; for (i = [_registeredChannels count] - 1; i >= 0; i--) { if ([[_registeredChannels objectAtIndex: i] nonretainedObjectValue] == channel) { [_registeredChannels removeObjectAtIndex: i]; break; } } } /** returns a non busy channel if any, nil otherwise **/ -(EODatabaseChannel *)_availableChannelFromRegisteredChannels { NSEnumerator *channelsEnum; NSValue *channel = nil; channelsEnum = [_registeredChannels objectEnumerator]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"REGISTERED CHANNELS nb=%d", [_registeredChannels count]); while ((channel = [channelsEnum nextObject])) { if ([(EODatabaseChannel *)[channel nonretainedObjectValue] isFetchInProgress] == NO) { EOFLOGObjectLevelArgs(@"EODatabaseContext",@"CHANNEL %p is not busy", [channel nonretainedObjectValue]); return [channel nonretainedObjectValue]; } else { EOFLOGObjectLevelArgs(@"EODatabaseContext",@"CHANNEL %p is busy", [channel nonretainedObjectValue]); } } return nil; } /** return a non busy channel **/ - (EODatabaseChannel *)availableChannel { EODatabaseChannel *channel = nil; int num = 2; while (!channel && num) { channel = [self _availableChannelFromRegisteredChannels]; if (!channel) { //If not channel and last try: send a EODatabaseChannelNeededNotification notification before this last try if (--num) [[NSNotificationCenter defaultCenter] postNotificationName: EODatabaseChannelNeededNotification object: self]; } } if (!channel) channel = [EODatabaseChannel databaseChannelWithDatabaseContext: self]; return channel; } /** returns the database **/ - (EODatabase *)database { return _database; } /** returns the coordinator **/ - (EOObjectStoreCoordinator *)coordinator { return _coordinator; } /** returns the adaptor context **/ - (EOAdaptorContext *)adaptorContext { return _adaptorContext; } /** Set the update strategy to 'strategy' May raise an exception if transaction has began or if you want pessimistic lock when there's already a snapshot recorded **/ - (void)setUpdateStrategy: (EOUpdateStrategy)strategy { if (_flags.beganTransaction) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: transaction in progress", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self]; //Can't set pessimistic locking where there's already snapshosts ! if (strategy == EOUpdateWithPessimisticLocking && [_database snapshots]) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: can't set EOUpdateWithPessimisticLocking when receive's EODatabase already has snapshots", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self]; _updateStrategy = strategy; } /** Get the update strategy **/ - (EOUpdateStrategy)updateStrategy { return _updateStrategy; } /** Get the delegate **/ - (id)delegate { return _delegate; } /** Set the delegate **/ - (void)setDelegate:(id)delegate { NSEnumerator *channelsEnum = [_registeredChannels objectEnumerator]; EODatabaseChannel *channel = nil; _delegate = delegate; _delegateRespondsTo.willRunLoginPanelToOpenDatabaseChannel = [delegate respondsToSelector: @selector(databaseContext:willRunLoginPanelToOpenDatabaseChannel:)]; _delegateRespondsTo.newPrimaryKey = [delegate respondsToSelector: @selector(databaseContext:newPrimaryKeyForObject:entity:)]; _delegateRespondsTo.willPerformAdaptorOperations = [delegate respondsToSelector: @selector(databaseContext:willPerformAdaptorOperations:adaptorChannel:)]; _delegateRespondsTo.shouldInvalidateObject = [delegate respondsToSelector: @selector(databaseContext:shouldInvalidateObjectWithGlobalID:snapshot:)]; _delegateRespondsTo.willOrderAdaptorOperations = [delegate respondsToSelector: @selector(databaseContext:willOrderAdaptorOperationsFromDatabaseOperations:)]; _delegateRespondsTo.shouldLockObject = [delegate respondsToSelector: @selector(databaseContext:shouldLockObjectWithGlobalID:snapshot:)]; _delegateRespondsTo.shouldRaiseForLockFailure = [delegate respondsToSelector: @selector(databaseContext:shouldRaiseExceptionForLockFailure:)]; _delegateRespondsTo.shouldFetchObjects = [delegate respondsToSelector: @selector(databaseContext:shouldFetchObjectsWithFetchSpecification:editingContext:)]; _delegateRespondsTo.didFetchObjects = [delegate respondsToSelector: @selector(databaseContext:didFetchObjects:fetchSpecification:editingContext:)]; _delegateRespondsTo.shouldFetchObjectFault = [delegate respondsToSelector: @selector(databaseContext:shouldFetchObjectsWithFetchSpecification:editingContext:)]; _delegateRespondsTo.shouldFetchArrayFault = [delegate respondsToSelector: @selector(databaseContext:shouldFetchArrayFault:)]; while ((channel = [channelsEnum nextObject])) [channel setDelegate: delegate]; } - (void)handleDroppedConnection { int i; EOFLOGObjectFnStartOrCond2(@"DatabaseLevel", @"EODatabaseContext"); DESTROY(_adaptorContext); for (i = [_registeredChannels count] - 1; i >= 0; i--) { RELEASE((EODatabaseChannel *)[[_registeredChannels objectAtIndex: i] nonretainedObjectValue]); } DESTROY(_registeredChannels); _adaptorContext = RETAIN([[[self database] adaptor] createAdaptorContext]); _registeredChannels = [NSMutableArray new]; EOFLOGObjectFnStopOrCond2(@"DatabaseLevel", @"EODatabaseContext"); } @end @implementation EODatabaseContext (EOObjectStoreSupport) /** Return a fault for row 'row' **/ - (id)faultForRawRow: (NSDictionary *)row entityNamed: (NSString *)entityName editingContext: (EOEditingContext *)context { EOEntity *entity; EOGlobalID *gid; id object; EOFLOGObjectFnStart(); entity = [_database entityNamed: entityName]; gid = [entity globalIDForRow: row]; object = [self faultForGlobalID: gid editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p of class (%@)", object, [object class]); EOFLOGObjectFnStop(); return object; } /** return entity corresponding to 'globalID' **/ - (id) entityForGlobalID: (EOGlobalID *)globalID { //OK NSString *entityName; EOEntity *entity; DESTROY(_lastEntity); entityName = [globalID entityName]; entity = [_database entityNamed: entityName]; ASSIGN(_lastEntity, entity); return entity; } /** Make object a fault **/ - (void) _turnToFault: (id)object gid: (EOGlobalID *)globalID editingContext: (EOEditingContext *)context isComplete: (BOOL)isComplete { //OK EOAccessFaultHandler *handler; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p", object); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"globalID=%@", globalID); NSAssert(globalID, @"No globalID"); NSAssert1([globalID isKindOfClass: [EOKeyGlobalID class]], @"globalID is not a EOKeyGlobalID but a %@", [globalID class]); if ([(EOKeyGlobalID*)globalID areKeysAllNulls]) NSWarnLog(@"All key of globalID %p (%@) are nulls", globalID, globalID); handler = [EOAccessFaultHandler accessFaultHandlerWithGlobalID: (EOKeyGlobalID*)globalID databaseContext: self editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"handler=%@", handler); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object->class_pointer=%p", GSObjCClass(object)); [EOFault makeObjectIntoFault: object withHandler: handler]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object->class_pointer=%p", GSObjCClass(object)); [self _addBatchForGlobalID: (EOKeyGlobalID*)globalID fault: object]; EOFLOGObjectFnStop(); //TODO: use isComplete } /** Get a fault for 'globalID' **/ - (id)faultForGlobalID: (EOGlobalID *)globalID editingContext: (EOEditingContext *)context { //Seems OK EOClassDescription *classDescription = nil; EOEntity *entity; id object = nil; BOOL isFinal; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"globalID=%@", globalID); isFinal = [(EOKeyGlobalID *)globalID isFinal]; entity = [self entityForGlobalID: globalID]; NSAssert(entity, @"no entity"); classDescription = [entity classDescriptionForInstances]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"classDescription=%@", classDescription); object = [classDescription createInstanceWithEditingContext: context globalID: globalID zone: NULL]; NSAssert1(object, @"No Object. classDescription=%@", classDescription); /*mirko: NO NSDictionary *pk; NSEnumerator *pkEnum; NSString *pkKey; NSArray *classPropertyNames; classPropertyNames = [entity classPropertyNames]; pk = [entity primaryKeyForGlobalID:(EOKeyGlobalID *)globalID]; pkEnum = [pk keyEnumerator]; while ((pkKey = [pkEnum nextObject])) { if ([classPropertyNames containsObject:pkKey] == YES) [obj takeStoredValue:[pk objectForKey:pkKey] forKey:pkKey]; } */ EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p", object); if ([(EOKeyGlobalID *)globalID areKeysAllNulls]) NSWarnLog(@"All key of globalID %p (%@) are nulls", globalID, globalID); [self _turnToFault: object gid: globalID editingContext: context isComplete: isFinal];//?? EOFLOGObjectLevel(@"EODatabaseContext", @"Record Object"); [context recordObject: object globalID: globalID]; EOFLOGObjectFnStop(); return object; } /** Get an array fault for globalID for relationshipName **/ - (NSArray *)arrayFaultWithSourceGlobalID: (EOGlobalID *)globalID relationshipName: (NSString *)relationshipName editingContext: (EOEditingContext *)context { //Seems OK NSArray *obj = nil; if (![globalID isKindOfClass: [EOKeyGlobalID class]]) { [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ The globalID %@ must be an EOKeyGlobalID to be able to construct a fault", NSStringFromSelector(_cmd), NSStringFromClass([self class]), globalID]; } else { EOAccessArrayFaultHandler *handler = nil; obj = [EOCheapCopyMutableArray array]; handler = [EOAccessArrayFaultHandler accessArrayFaultHandlerWithSourceGlobalID: (EOKeyGlobalID*)globalID relationshipName: relationshipName databaseContext: self editingContext: context]; [EOFault makeObjectIntoFault: obj withHandler: handler]; [self _addToManyBatchForSourceGlobalID: (EOKeyGlobalID *)globalID relationshipName: relationshipName fault: (EOFault*)obj]; } return obj; } - (void)initializeObject: (id)object withGlobalID: (EOGlobalID *)globalID editingContext: (EOEditingContext *)context { //near OK EOEntity *entity = nil; EOFLOGObjectFnStart(); if ([globalID isTemporary]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } if (![(EOKeyGlobalID *)globalID isFinal]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } //mirko: if (_updateStrategy == EOUpdateWithPessimisticLocking) [self registerLockedObjectWithGlobalID: globalID]; entity = [self entityForGlobalID: globalID]; /*Mirko: if ([object respondsToSelector:@selector(entity)]) entity = [object entity]; else entity = [_database entityNamed:[globalID entityName]]; */ [self initializeObject: object row: [self snapshotForGlobalID: globalID] //shound be _currentSnapshot entity: entity editingContext: context]; EOFLOGObjectFnStop(); } - (void) _objectsChanged: (NSNotification*)notification { EOFLOGObjectFnStart(); /*object==self EOObjectsChangedInStoreNotification userInfo = { deleted = (List Of GlobalIDs); inserted = (List Of GlobalIDs); updated = (List Of GlobalIDs); */ if ([notification object] != self) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { //OK for update //TODO-NOW for insert/delete NSDictionary *userInfo = [notification userInfo]; NSArray *updatedObjects = [userInfo objectForKey: @"updated"]; //NSArray *insertedObjects = [userInfo objectForKey: @"inserted"]; //NSArray *deletedObjects = [userInfo objectForKey: @"deleted"]; int i, count = [updatedObjects count]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"updatedObjects=%@", updatedObjects); for (i = 0; i < count; i++) { EOKeyGlobalID *gid=[updatedObjects objectAtIndex: i]; NSString *entityName; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); entityName = [gid entityName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entityName=%@", entityName); [_database invalidateResultCacheForEntityNamed: entityName]; } } EOFLOGObjectFnStop(); } - (void) _snapshotsChangedInDatabase: (NSNotification*)notification { EOFLOGObjectFnStart(); /* userInfo = { deleted = (List Of GlobalIDs); inserted = (List Of GlobalIDs); updated = (List Of GlobalIDs); }} */ if ([notification object] != self) { [[NSNotificationCenter defaultCenter] postNotificationName: EOObjectsChangedInStoreNotification object: self userInfo: [notification userInfo]]; //call _objectsChanged: and ObjectStoreCoordinator _objectsChangedInSubStore: } EOFLOGObjectFnStop(); } - (NSArray *)objectsForSourceGlobalID: (EOGlobalID *)globalID relationshipName: (NSString *)name editingContext: (EOEditingContext *)context { //Near OK NSArray *objects = nil; id sourceObjectFault = nil; id relationshipValue = nil; NSArray *sourceSnapshot = nil; int sourceSnapshotCount = 0; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"globalID=%@", globalID); //First get the id from which we search the source object sourceObjectFault = [context faultForGlobalID: globalID editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObjectFault %p=%@", sourceObjectFault, sourceObjectFault); // Get the fault value from source object EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", name); relationshipValue = [sourceObjectFault storedValueForKey: name]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipValue %p=%@", relationshipValue, relationshipValue); //Try to see if there is a snapshot for the source object sourceSnapshot = [_database snapshotForSourceGlobalID: globalID relationshipName: name]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceSnapshot %p (%@)=%@", sourceSnapshot, [sourceSnapshot class], sourceSnapshot); sourceSnapshotCount = [sourceSnapshot count]; if (sourceSnapshotCount > 0) { EOGlobalID *snapGID = nil; id snapFault = nil; int i; [EOFault clearFault: relationshipValue]; for (i = 0; i < sourceSnapshotCount; i++) { snapGID = [sourceSnapshot objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapGID=%@", snapGID); snapFault = [context faultForGlobalID: snapGID editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapFault=%@", snapFault); [relationshipValue addObject: snapFault]; } objects = relationshipValue; } else { EOEntity *entity; EORelationship *relationship; unsigned int maxBatch = 0; BOOL isToManyToOne = NO; EOEntity *destinationEntity = nil; EOModel *destinationEntityModel = nil; NSArray *models = nil; EOQualifier *auxiliaryQualifier = nil; NSDictionary *contextSourceSnapshot = nil; id sourceObject = nil; EORelationship *inverseRelationship = nil; EOEntity *invRelEntity = nil; NSArray *invRelEntityClassProperties = nil; NSString *invRelName = nil; EOQualifier *qualifier = nil; EOFetchSpecification *fetchSpec = nil; // Get the source object entity entity = [self entityForGlobalID: globalID]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity name=%@", [entity name]); //Get the relationship named 'name' relationship = [entity relationshipNamed: name]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); //Get the max number of fault to fetch maxBatch = [relationship numberOfToManyFaultsToBatchFetch]; isToManyToOne = [relationship isToManyToOne];//NO if (isToManyToOne) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO if isToManyToOne } //Get the fault entity (aka relationsip destination entity) destinationEntity = [relationship destinationEntity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationEntity name=%@", [destinationEntity name]); //Get the destination entity model destinationEntityModel = [destinationEntity model]; //and _database model to verify if the destinationEntityModel is in database models models = [_database models]; if ([models indexOfObjectIdenticalTo: destinationEntityModel] == NSNotFound) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO error } //Get the relationship qualifier if any auxiliaryQualifier = [relationship auxiliaryQualifier];//nil if (auxiliaryQualifier) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO if auxqualif } //?? contextSourceSnapshot = [self snapshotForGlobalID: globalID]; NSEmitTODO(); //TODO Why first asking for faultForGlobalID and now asking objectForGlobalID ?? sourceObject = [context objectForGlobalID: globalID]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObject=%@", sourceObject); inverseRelationship = [relationship inverseRelationship]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"inverseRelationship=%@", inverseRelationship); if (!inverseRelationship) { NSEmitTODO(); //[self notImplemented: _cmd]; //TODO if !inverseRelationship inverseRelationship = [relationship hiddenInverseRelationship]; //VERIFY (don't know if this is the good way) } invRelEntity = [inverseRelationship entity]; invRelEntityClassProperties = [invRelEntity classProperties]; invRelName = [inverseRelationship name]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"invRelName=%@", invRelName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObject=%@", sourceObject); qualifier = [EOKeyValueQualifier qualifierWithKey: invRelName operatorSelector: @selector(isEqualTo:) value: sourceObject]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"qualifier=%@", qualifier); fetchSpec = [EOFetchSpecification fetchSpecification]; [fetchSpec setQualifier: qualifier]; [fetchSpec setEntityName: [destinationEntity name]]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"fetchSpec=%@", fetchSpec); objects = [context objectsWithFetchSpecification: fetchSpec editingContext: context]; [self _registerSnapshot: objects forSourceGlobalID: globalID relationshipName: name editingContext: context];//OK } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"objects=%@", objects); EOFLOGObjectFnStop(); return objects; } - (void)_registerSnapshot: (NSArray*)snapshot forSourceGlobalID: (EOGlobalID*)globalID relationshipName: (NSString*)name editingContext: (EOEditingContext*)context { //OK NSArray *gids; EOFLOGObjectFnStart(); gids = [context resultsOfPerformingSelector: @selector(globalIDForObject:) withEachObjectInArray: snapshot]; [_database recordSnapshot: gids forSourceGlobalID: globalID relationshipName: name]; EOFLOGObjectFnStop(); } - (void)refaultObject: object withGlobalID: (EOGlobalID *)globalID editingContext: (EOEditingContext *)context { EOFLOGObjectFnStart(); [EOObserverCenter suppressObserverNotification]; NS_DURING { [object clearProperties];//OK } NS_HANDLER { [EOObserverCenter enableObserverNotification]; NSLog(@"EXCEPTION %@", localException); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"EXCEPTION %@", localException); [localException raise]; } NS_ENDHANDLER; [EOObserverCenter enableObserverNotification]; if ([(EOKeyGlobalID *)globalID areKeysAllNulls]) NSWarnLog(@"All key of globalID %p (%@) are nulls", globalID, globalID); [self _turnToFault: object gid: globalID editingContext: context isComplete: YES]; //Why YES ? [self forgetSnapshotForGlobalID:globalID]; EOFLOGObjectFnStop(); } - (void)saveChangesInEditingContext: (EOEditingContext *)context { //TODO: locks ? NSException *exception = nil; EOFLOGObjectFnStart(); [self prepareForSaveWithCoordinator: nil editingContext: context]; [self recordChangesInEditingContext]; NS_DURING { [self performChanges]; } NS_HANDLER { NSDebugMLog(@"EXCEPTION: %@", localException); exception = localException; } NS_ENDHANDLER; //I don't know if this is really the good place to catch exception and rollback... if (exception) { [self rollbackChanges]; [exception raise]; } else [self commitChanges]; EOFLOGObjectFnStop(); } - (void)_fetchRelationship: (EORelationship *)relationship withObjects: (NSArray *)objsArray editingContext: (EOEditingContext *)context { NSMutableArray *qualArray = nil; NSEnumerator *objEnum = nil; NSEnumerator *relEnum = nil; NSDictionary *snapshot = nil; id obj = nil; id relObj = nil; EOFLOGObjectFnStart(); if ([objsArray count] > 0) { qualArray = [NSMutableArray arrayWithCapacity: 5]; if ([relationship isFlattened] == YES) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship %@ isFlattened", relationship); relEnum = [[relationship componentRelationships] objectEnumerator]; while ((relationship = [relEnum nextObject])) { // TODO rebuild object array for relationship path [self _fetchRelationship: relationship withObjects: objsArray editingContext: context]; } } objEnum = [objsArray objectEnumerator]; while ((obj = [objEnum nextObject])) { relObj = [obj storedValueForKey: [relationship name]]; snapshot = [self snapshotForGlobalID: [context globalIDForObject: relObj]]; [qualArray addObject: [relationship qualifierWithSourceRow: snapshot]]; } [self objectsWithFetchSpecification: [EOFetchSpecification fetchSpecificationWithEntityName: [[relationship destinationEntity] name] qualifier: [EOAndQualifier qualifierWithQualifierArray: qualArray] sortOrderings: nil] editingContext: context]; } EOFLOGObjectFnStop(); } - (NSArray *)objectsWithFetchSpecification: (EOFetchSpecification *)fetch editingContext: (EOEditingContext *)context { // TODO EODatabaseChannel *channel = nil; NSMutableArray *array = nil; NSDictionary *snapshot = nil; NSString *entityName = nil; EOEntity *entity = nil; NSString *relationshipKeyPath = nil; NSEnumerator *relationshipKeyPathEnum = nil; NSMutableArray *qualArray = nil; /*NSEnumerator *subEntitiesEnum = nil; EOEntity *subEntity = nil; NSArray *subEntities = nil;*/ NSArray* rawRowKeyPaths = nil; BOOL usesDistinct = NO; int num = 0; int limit=0; int i = 0; id obj = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"fetch=%@", fetch); if (_delegateRespondsTo.shouldFetchObjects == YES) { array = (id)[_delegate databaseContext: self shouldFetchObjectsWithFetchSpecification: fetch editingContext: context]; } if (!array) { array = [NSMutableArray arrayWithCapacity: 8]; entityName = [fetch entityName];//OK entity = [_database entityNamed: entityName];//OK NSAssert1(entity,@"No entity named %@", entityName); /* moved in EODatabaseChannel _selectWithFetchSpecification:(EOFetchSpecification *)fetch editingContext:(EOEditingContext *)context limit = [fetch fetchLimit]; usesDistinct = [fetch usesDistinct]; subEntities = [entity subEntities]; if ([subEntities count] && [fetch isDeep] == YES) { subEntitiesEnum = [subEntities objectEnumerator]; while ((subEntity = [subEntitiesEnum nextObject])) { EOFetchSpecification *fetchSubEntity; fetchSubEntity = AUTORELEASE([fetch copy]); [fetchSubEntity setEntityName:[entity name]]; [array addObjectsFromArray:[context objectsWithFetchSpecification: fetchSubEntity]]; } } */ rawRowKeyPaths = [fetch rawRowKeyPaths];//OK if (rawRowKeyPaths) #if 0 { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } #else // (stephane@sente.ch) Adapted implementation of non raw rows { //cachesObject //fetchspe isDeep ret 1 channel = [self _obtainOpenChannel]; if (!channel) { NSEmitTODO(); [self notImplemented: _cmd];//TODO } else { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"channel class %@ [channel isFetchInProgress]=%s", [channel class], ([channel isFetchInProgress] ? "YES" : "NO")); NSLog(@"%@ -- %@ 0x%x: channel isFetchInProgress=%s", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, ([channel isFetchInProgress] ? "YES" : "NO")); //mirko: #if 0 if (_flags.beganTransaction == NO && _updateStrategy == EOUpdateWithPessimisticLocking) { [_adaptorContext beginTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>YES"); _flags.beganTransaction = YES; } #endif if ([entity isAbstractEntity] == NO) //Mirko ??? // (stephane@sente) Should we test deepInheritanceFetch? { int autoreleaseSteps = 20; int autoreleaseStep = autoreleaseSteps; BOOL promptsAfterFetchLimit = NO; NSAutoreleasePool *arp = nil;//To avoid too much memory use when fetching a lot of objects int limit = 0; [channel selectObjectsWithFetchSpecification: fetch editingContext: context];//OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); limit = [fetch fetchLimit];//OK promptsAfterFetchLimit = [fetch promptsAfterFetchLimit]; EOFLOGObjectLevel(@"EODatabaseContext", @"Will Fetch"); NS_DURING { arp = [NSAutoreleasePool new]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); while ((obj = [channel fetchObject])) { EOFLOGObjectLevel(@"EODatabaseContext", @"fetched an object"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"FETCH OBJECT object=%@\n\n", obj); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"%d usesDistinct: %s", num, (usesDistinct ? "YES" : "NO")); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@\n\n", obj); if (usesDistinct == YES && num) // (stephane@sente) I thought that DISTINCT was done on server-side?!? { for (i = 0; i < num; i++) { if ([[array objectAtIndex: i] isEqual: obj] == YES) { obj = nil; break; } } if (obj == nil) continue; } EOFLOGObjectLevel(@"EODatabaseContext", @"AFTER FETCH"); [array addObject: obj]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array count=%d", [array count]); num++; if (limit > 0 && num >= limit) { if ([[context messageHandler] editingContext: context shouldContinueFetchingWithCurrentObjectCount: num originalLimit: limit objectStore: self] == YES) limit = 0;//?? else break; } if (autoreleaseStep <= 0) { DESTROY(arp); autoreleaseStep = autoreleaseSteps; arp = [NSAutoreleasePool new]; } else autoreleaseStep--; EOFLOGObjectLevel(@"EODatabaseContext", @"WILL FETCH NEXT OBJECT"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); } EOFLOGObjectLevel(@"EODatabaseContext", @"finished fetch"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array=%@", array); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 0 channel is busy=%d", (int)[channel isFetchInProgress]); [channel cancelFetch]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 1 channel is busy=%d", (int)[channel isFetchInProgress]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array=%@", array); //TODO /* handle exceptio in fetchObject channel fetchObject if eception: if ([editcontext handleError:localException]) { //TODO } else { //TODO }; */ } NS_HANDLER { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"AN EXCEPTION: %@", localException); RETAIN(localException); DESTROY(arp); AUTORELEASE(localException); [localException raise]; } NS_ENDHANDLER; } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 2 channel is busy=%d", (int)[channel isFetchInProgress]); } #endif else if ([entity cachesObjects] == YES)//OK { ///TODO MG!!! NSMutableArray *cache; NSEnumerator *cacheEnum; EOQualifier *qualifier; EOGlobalID *gid; BOOL isFault; qualifier = [fetch qualifier]; cache = (id)[_database resultCacheForEntityNamed: entityName]; if (cache == nil) { NSMutableDictionary *row = nil; EOAdaptorChannel *adaptorChannel = nil; channel = [self availableChannel]; adaptorChannel = [channel adaptorChannel]; if (_flags.beganTransaction == NO && _updateStrategy == EOUpdateWithPessimisticLocking) { [_adaptorContext beginTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>YES"); _flags.beganTransaction = YES; } [adaptorChannel selectAttributes: [entity attributesToFetch] fetchSpecification: [EOFetchSpecification fetchSpecificationWithEntityName: entityName qualifier: nil sortOrderings: nil] lock: NO entity: entity]; cache = [NSMutableArray arrayWithCapacity: 16]; while ((row = [adaptorChannel fetchRowWithZone: NULL])) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"row=%@", row); gid = [entity globalIDForRow: row]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); [_database recordSnapshot: row forGlobalID: gid]; [cache addObject: gid]; } EOFLOGObjectLevel(@"EODatabaseContext", @"Finished fetch"); [channel cancelFetch]; EOFLOGObjectLevel(@"EODatabaseContext", @"setResultCache"); [_database setResultCache: cache forEntityNamed: entityName]; } cacheEnum = [cache objectEnumerator]; while ((gid = [cacheEnum nextObject])) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); snapshot = [self snapshotForGlobalID: gid]; if (snapshot) { if (!qualifier || [qualifier evaluateWithObject: snapshot] == YES) { obj = [context objectForGlobalID: gid]; isFault = [EOFault isFault: obj]; if (obj == nil || isFault == YES) { if (isFault == NO) { obj = [[entity classDescriptionForInstances] createInstanceWithEditingContext: context globalID: gid zone: NULL]; NSAssert1(obj, @"No Object. [entity classDescriptionForInstances]=%@", [entity classDescriptionForInstances]); [context recordObject: obj globalID: gid]; } else { [self _removeBatchForGlobalID: (EOKeyGlobalID *)gid fault: obj]; [EOFault clearFault: obj]; } [context initializeObject: obj withGlobalID: gid editingContext: context]; [obj awakeFromFetchInEditingContext: context]; } if (usesDistinct == YES && num) { for (i = 0; i < num; i++) { if ([[array objectAtIndex: i] isEqual: obj] == YES) { obj = nil; break; } } if (obj == nil) continue; } [array addObject: obj]; num++; if (limit && num >= limit) { if ([[context messageHandler] editingContext: context shouldContinueFetchingWithCurrentObjectCount: num originalLimit: limit objectStore: self] == YES) limit = 0; else break; } } } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array before sort: %@", array); if ([fetch sortOrderings]) array = (id)[array sortedArrayUsingKeyOrderArray: [fetch sortOrderings]]; } else { //cachesObject //fetchspe isDeep ret 1 channel = [self _obtainOpenChannel]; if (!channel) { NSEmitTODO(); [self notImplemented: _cmd];//TODO } else { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"channel class %@ [channel isFetchInProgress]=%s", [channel class], ([channel isFetchInProgress] ? "YES" : "NO")); NSLog(@"%@ -- %@ 0x%x: channel isFetchInProgress=%s", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, ([channel isFetchInProgress] ? "YES" : "NO")); //mirko: if (_flags.beganTransaction == NO && _updateStrategy == EOUpdateWithPessimisticLocking) { [_adaptorContext beginTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>YES"); _flags.beganTransaction = YES; } if ([entity isAbstractEntity] == NO) //Mirko ??? { int autoreleaseSteps = 20; int autoreleaseStep = autoreleaseSteps; BOOL promptsAfterFetchLimit = NO; NSAutoreleasePool *arp = nil;//To avoid too much memory use when fetching a lot of objects int limit = 0; [channel selectObjectsWithFetchSpecification: fetch editingContext: context];//OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); limit = [fetch fetchLimit];//OK promptsAfterFetchLimit = [fetch promptsAfterFetchLimit]; EOFLOGObjectLevel(@"EODatabaseContext", @"Will Fetch"); NS_DURING { arp = [NSAutoreleasePool new]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); while ((obj = [channel fetchObject])) { EOFLOGObjectLevel(@"EODatabaseContext", @"fetched an object"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"FETCH OBJECT object=%@\n\n", obj); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"%d usesDistinct: %s", num, (usesDistinct ? "YES" : "NO")); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@\n\n", obj); if (usesDistinct == YES && num) { for (i = 0; i < num; i++) { if ([[array objectAtIndex: i] isEqual :obj] == YES) { obj = nil; break; } } if (obj == nil) continue; } EOFLOGObjectLevel(@"EODatabaseContext", @"AFTER FETCH"); NSAssert(obj,@"No object"); [array addObject: obj]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array count=%d", [array count]); num++; if (limit > 0 && num >= limit) { if ([[context messageHandler] editingContext: context shouldContinueFetchingWithCurrentObjectCount: num originalLimit: limit objectStore: self] == YES) limit = 0;//?? else break; } if (autoreleaseStep <= 0) { DESTROY(arp); autoreleaseStep = autoreleaseSteps; arp = [NSAutoreleasePool new]; } else autoreleaseStep--; EOFLOGObjectLevel(@"EODatabaseContext", @"WILL FETCH NEXT OBJECT"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"[channel isFetchInProgress]=%s", ([channel isFetchInProgress] ? "YES" : "NO")); } EOFLOGObjectLevel(@"EODatabaseContext", @"finished fetch"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array=%@", array); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 0 channel is busy=%d", (int)[channel isFetchInProgress]); [channel cancelFetch]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 1 channel is busy=%d", (int)[channel isFetchInProgress]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array=%@", array); //TODO /* handle exceptio in fetchObject channel fetchObject if eception: if ([editcontext handleError:localException]) { //TODO } else { //TODO }; */ } NS_HANDLER { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"AN EXCEPTION: %@", localException); RETAIN(localException); DESTROY(arp); AUTORELEASE(localException); [localException raise]; } NS_ENDHANDLER; } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"step 2 channel is busy=%d", (int)[channel isFetchInProgress]); } //VERIFY EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array before prefetchingRelationshipKeyPaths: %@", array); if ([fetch prefetchingRelationshipKeyPaths]) //OK qualArray = [NSMutableArray arrayWithCapacity: 5]; relationshipKeyPathEnum = [[fetch prefetchingRelationshipKeyPaths] objectEnumerator]; while ((relationshipKeyPath = [relationshipKeyPathEnum nextObject])) { NSArray *relationshipKeyArray = [relationshipKeyPath componentsSeparatedByString: @"."]; NSEnumerator *relationshipKeyEnum; EORelationship *relationship; EOEntity *currentEntity = entity; NSString *relationshipKey; relationshipKeyEnum = [relationshipKeyArray objectEnumerator]; while ((relationshipKey = [relationshipKeyEnum nextObject])) { relationship = [currentEntity relationshipNamed: relationshipKey]; currentEntity = [relationship destinationEntity]; // TODO rebuild object array for relationship path [self _fetchRelationship: relationship withObjects: array editingContext: context]; } } if (_delegateRespondsTo.didFetchObjects == YES) [_delegate databaseContext: self didFetchObjects: array fetchSpecification: fetch editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"step 1 channel is busy=%d", (int)[channel isFetchInProgress]); } //EOFLOGObjectLevelArgs(@"EODatabaseContext", @"array: %@", array); EOFLOGObjectFnStop(); return array; } - (BOOL)isObjectLockedWithGlobalID: (EOGlobalID *)gid editingContext: (EOEditingContext *)context { return [self isObjectLockedWithGlobalID: gid]; } - (void)lockObjectWithGlobalID: (EOGlobalID *)globalID editingContext: (EOEditingContext *)context { // TODO EOKeyGlobalID *gid = (EOKeyGlobalID *)globalID; EODatabaseChannel *channel; EOEntity *entity; NSArray *attrsUsedForLocking, *primaryKeyAttributes; NSDictionary *snapshot; NSMutableDictionary *qualifierSnapshot, *lockSnapshot; NSMutableArray *lockAttributes; NSEnumerator *attrsEnum; EOQualifier *qualifier; EOAttribute *attribute; if ([self isObjectLockedWithGlobalID: gid] == NO) { snapshot = [self snapshotForGlobalID: gid]; if (_delegateRespondsTo.shouldLockObject == YES && [_delegate databaseContext: self shouldLockObjectWithGlobalID: gid snapshot: snapshot] == NO) return; channel = [self availableChannel]; entity = [_database entityNamed: [gid entityName]]; NSAssert1(entity, @"No entity named %@", [gid entityName]); attrsUsedForLocking = [entity attributesUsedForLocking]; primaryKeyAttributes = [entity primaryKeyAttributes]; qualifierSnapshot = [NSMutableDictionary dictionaryWithCapacity: 16]; lockSnapshot = [NSMutableDictionary dictionaryWithCapacity: 8]; lockAttributes = [NSMutableArray arrayWithCapacity: 8]; attrsEnum = [primaryKeyAttributes objectEnumerator]; while ((attribute = [attrsEnum nextObject])) { NSString *name = [attribute name]; [lockSnapshot setObject: [snapshot objectForKey:name] forKey: name]; } attrsEnum = [attrsUsedForLocking objectEnumerator]; while ((attribute = [attrsEnum nextObject])) { NSString *name = [attribute name]; if ([primaryKeyAttributes containsObject:attribute] == NO) { if ([attribute adaptorValueType] == EOAdaptorBytesType) { [lockAttributes addObject: attribute]; [lockSnapshot setObject: [snapshot objectForKey:name] forKey: name]; } else [qualifierSnapshot setObject: [snapshot objectForKey:name] forKey: name]; } } // Turbocat if ([[qualifierSnapshot allKeys] count] > 0) qualifier = [EOAndQualifier qualifierWithQualifiers: [entity qualifierForPrimaryKey: [entity primaryKeyForGlobalID: gid]], [EOQualifier qualifierToMatchAllValues: qualifierSnapshot], nil]; if ([lockAttributes count] == 0) lockAttributes = nil; if ([lockSnapshot count] == 0) lockSnapshot = nil; if (_flags.beganTransaction == NO) { [[[channel adaptorChannel] adaptorContext] beginTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>YES"); _flags.beganTransaction = YES; } NS_DURING [[channel adaptorChannel] lockRowComparingAttributes: lockAttributes entity: entity qualifier: qualifier snapshot: lockSnapshot]; NS_HANDLER { if (_delegateRespondsTo.shouldRaiseForLockFailure == YES) { if ([_delegate databaseContext: self shouldRaiseExceptionForLockFailure:localException] == YES) [localException raise]; } else [localException raise]; } NS_ENDHANDLER; [self registerLockedObjectWithGlobalID: gid]; } } - (void)invalidateAllObjects { [self invalidateObjectsWithGlobalIDs: [[_database snapshot] allKeys]]; [[NSNotificationCenter defaultCenter] postNotificationName: EOInvalidatedAllObjectsInStoreNotification object: self]; } - (void)invalidateObjectsWithGlobalIDs: (NSArray *)globalIDs { NSMutableArray *array = nil; NSEnumerator *enumerator; EOKeyGlobalID *gid; if (_delegateRespondsTo.shouldInvalidateObject == YES) { array = [NSMutableArray array]; enumerator = [globalIDs objectEnumerator]; while ((gid = [enumerator nextObject])) { if ([_delegate databaseContext: self shouldInvalidateObjectWithGlobalID: gid snapshot: [self snapshotForGlobalID: gid]] == YES) [array addObject: gid]; } } [_database forgetSnapshotsForGlobalIDs: ((id)array ? (id)array : globalIDs)]; } @end @implementation EODatabaseContext(EOCooperatingObjectStoreSupport) - (BOOL)ownsGlobalID: (EOGlobalID *)globalID { if ([globalID isKindOfClass: [EOKeyGlobalID class]] && [_database entityNamed: [globalID entityName]]) return YES; return NO; } - (BOOL)ownsObject: (id)object { if ([_database entityForObject: object]) return YES; return NO; } - (BOOL)ownsEntityNamed: (NSString *)entityName { if ([_database entityNamed: entityName]) return YES; return NO; } - (BOOL)handlesFetchSpecification: (EOFetchSpecification *)fetchSpecification { //OK if ([_database entityNamed: [fetchSpecification entityName]]) return YES; else return NO; } /* //Mirko: - (EODatabaseOperation *)_dbOperationWithObject:object operator:(EODatabaseOperator)operator { NSMapEnumerator gidEnum; EODatabaseOperation *op; EOGlobalID *gid; gidEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (NSNextMapEnumeratorPair(&gidEnum, (void **)&gid, (void **)&op)) { if ([[op object] isEqual:object] == YES) { if ([op databaseOperator] == operator) return op; return nil; } } return nil; } - (void)_setGlobalID:(EOGlobalID *)globalID forDatabaseOperation:(EODatabaseOperation *)op { EOGlobalID *oldGlobalID = [op globalID]; [op _setGlobalID:globalID]; NSMapInsert(_dbOperationsByGlobalID, globalID, op); NSMapRemove(_dbOperationsByGlobalID, oldGlobalID); } - (EODatabaseOperation *)_dbOperationWithGlobalID:(EOGlobalID *)globalID object:object entity:(EOEntity *)entity operator:(EODatabaseOperator)operator { EODatabaseOperation *op; NSMutableDictionary *newRow; NSMapEnumerator gidEnum; EOAttribute *attribute; EOGlobalID *gid; NSString *key; NSArray *classProperties; EONull *null = [EONull null]; BOOL found = NO; int i, count; id val; gidEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (NSNextMapEnumeratorPair(&gidEnum, (void **)&gid, (void **)&op)) { if ([[op object] isEqual:object] == YES) { found = YES; break; } } if (found == YES) return op; if (globalID == nil) globalID = AUTORELEASE([[EOTemporaryGlobalID alloc] init]); op = AUTORELEASE([[EODatabaseOperation alloc] initWithGlobalID:globalID object:object entity:entity]); [op setDatabaseOperator:operator]; [op setDBSnapshot:[self snapshotForGlobalID:globalID]]; newRow = [op newRow]; classProperties = [entity classProperties]; count = [classProperties count]; for (i = 0; i < count; i++) { attribute = [classProperties objectAtIndex:i]; if ([attribute isKindOfClass:[EOAttribute class]] == NO) continue; key = [attribute name]; if ([attribute isFlattened] == NO) { val = [object storedValueForKey:key]; if (val == nil) val = null; [newRow setObject:val forKey:key]; } } NSLog(@"** %@ # %@", globalID, [op dbSnapshot]); NSLog(@"-- %@", [op newRow]); NSMapInsert(_dbOperationsByGlobalID, globalID, op); return op; } */ // Prepares to save changes. Obtains primary keys for any inserted objects // in the EditingContext that are owned by this context. - (void)prepareForSaveWithCoordinator: (EOObjectStoreCoordinator *)coordinator editingContext: (EOEditingContext *)context { //near OK //Ayers: Review NSArray *insertedObjects = nil; NSMutableArray *noPKObjects = nil; int round = 0; EOFLOGObjectFnStart(); NSAssert(context, @"No editing context"); _flags.preparingForSave = YES; _coordinator=coordinator;//RETAIN ? _editingContext=context;//RETAIN ? // First, create dbOperation map if there's none if (!_dbOperationsByGlobalID) _dbOperationsByGlobalID = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 32); // Next, build list of Entity which need PK generator [self _buildPrimaryKeyGeneratorListForEditingContext: context]; // Now get newly inserted objects // For each object, we will recordInsertForObject: and relay PK if it is !nil insertedObjects = [context insertedObjects]; // We can make 2 rounds to try to get primary key for dependant objects for(round=0;round<2;round++) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"round=%d [noPKObjects count]=%d", round,[noPKObjects count]); if (round==1 && [noPKObjects count]==0) break; else { NSArray* array=nil; int i = 0; int count = 0; if (round==0) array=insertedObjects; else { array=noPKObjects; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"noPKObjects=%@", noPKObjects); } count = [array count]; for (i = 0; i < count; i++) { id object = [array objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%@",object); if ([self ownsObject:object]) { NSDictionary *objectPK = nil; EODatabaseOperation *dbOpe = nil; NSMutableDictionary *newRow = nil; EOEntity *entity = [_database entityForObject:object]; if (round==0) [self recordInsertForObject: object]; objectPK = [self _primaryKeyForObject: object raiseException: round>0]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"objectPK=%@", objectPK); if (objectPK) { dbOpe = [self databaseOperationForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p dbOpe=%@", object,dbOpe); newRow=[dbOpe newRow]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%@", newRow); [self relayPrimaryKey: objectPK object: object entity: entity]; if (round>0) { [noPKObjects removeObjectAtIndex:i]; i--; }; } else if (round>0) { if (!noPKObjects) noPKObjects=(NSMutableArray*)[NSMutableArray array]; [noPKObjects addObject:object]; } } } } } EOFLOGObjectFnStop(); } - (void)recordChangesInEditingContext { int which = 0; NSArray *objects[3] = {nil, nil, nil}; EOFLOGObjectFnStart(); [self _assertValidStateWithSelector: @selector(recordChangesInEditingContext)]; NSAssert(_editingContext, @"No editing context"); // We'll examin object in the following order: // insertedObjects, // deletedObjects (because re-inserted object should be removed from deleteds) // updatedObjects (because inserted/deleted objects may cause some other objects to be updated). for (which = 0; which < 3; which++) { int i, count; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Unprocessed: %@", [_editingContext unprocessedDescription]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Objects: %@", [_editingContext objectsDescription]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"which=%d", which); if (which == 0) objects[which] = [_editingContext insertedObjects]; else if (which == 1) objects[which] = [_editingContext deletedObjects]; else objects[which] = [_editingContext updatedObjects]; count = [objects[which] count]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"objects[which]=%@", objects[which]); // For each object for (i = 0; i < count; i++) { NSDictionary *currentCommittedSnapshot = nil; NSArray *relationships = nil; EODatabaseOperation *dbOpe = nil; EOEntity *entity = nil; id object = [objects[which] objectAtIndex: i]; //Mirko ?? if ([self ownsObject:object] == YES) EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p (class=%@):\n%@", object, [object class], object); entity = [_database entityForObject: object]; //OK for Update if (which == 0 || which == 2)//insert or update { NSDictionary *pk = nil; NSDictionary *snapshot; [self recordUpdateForObject: object //Why ForUpdate ? Becuase PK already generated ? changes: nil]; //OK for update // Get a dictionary of object properties+PK+relationships CURRENT values snapshot = [object snapshot]; //OK for Update+Insert EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot %p: %@", snapshot, snapshot); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"currentCommittedSnapshot %p: %@", currentCommittedSnapshot, currentCommittedSnapshot); // Get a dictionary of object properties+PK+relationships DATABASES values if (!currentCommittedSnapshot) currentCommittedSnapshot = [self _currentCommittedSnapshotForObject:object]; //OK For Update EOFLOGObjectLevelArgs(@"EODatabaseContext", @"currentCommittedSnapshot %p: %@", currentCommittedSnapshot, currentCommittedSnapshot); //TODO so what ? // Get the PK pk = [self _primaryKeyForObject: object];//OK for Update EOFLOGObjectLevelArgs(@"EODatabaseContext", @"pk=%@", pk); if (pk) [self relayPrimaryKey: pk object: object entity: entity]; //OK for Update } relationships = [entity relationships]; //OK for Update EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p relationships: %@", object,relationships); if (which == 1) //delete //Not in insert //not in update { int iRelationship = 0; int relationshipsCount = [relationships count]; for (iRelationship = 0; iRelationship < relationshipsCount; iRelationship++) { EORelationship *relationship = [relationships objectAtIndex: iRelationship]; if ([relationship isToManyToOne]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } } EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object: %@", object); [self recordDeleteForObject: object]; } dbOpe = [self databaseOperationForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); if (which == 0 || which == 2) //insert or update { //En update: dbsnapshot //en insert : snapshot ? en insert:dbsnap aussi int iRelationship = 0; int relationshipsCount = 0; NSDictionary *snapshot = nil; if (which == 0) //Insert //see wotRelSaveChanes.1.log seems to use dbSna for insert ! { snapshot=[object snapshot];//NEW2 //snapshot=[dbOpe dbSnapshot]; //NEW NSDebugMLog(@"[dbOpe dbSnapshot]=%@", [dbOpe dbSnapshot]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Insert: [dbOpe snapshot] %p=%@", snapshot, snapshot); } else //Update { //NEWsnapshot=[dbOpe dbSnapshot]; snapshot = [object snapshot]; //EOFLOGObjectLevelArgs(@"EODatabaseContext",@"Update: [dbOpe snapshot] %p=%@",snapshot,snapshot); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Update: [object snapshot] %p=%@", snapshot, snapshot); } relationshipsCount = [relationships count]; for (iRelationship = 0; iRelationship < relationshipsCount; iRelationship++) { NSArray *classProperties = nil; EORelationship *relationship = nil; EORelationship *substitutionRelationship = nil; relationship = [relationships objectAtIndex: iRelationship]; /* get rel entity entity model model modelGroup */ EOFLOGObjectLevelArgs(@"EODatabaseContext", @"HANDLE relationship %@ for object %p (class=%@):\n%@", [relationship name], object, [object class], object); NSDebugMLog(@"HANDLE relationship %@ for object %p (class=%@):\n%@", [relationship name], object, [object class], object); substitutionRelationship = [relationship _substitutionRelationshipForRow: snapshot]; classProperties = [entity classProperties]; /* rel name ==> toCountry rel isToMany (0) nullifyAttributesInRelationship:rel sourceObject:object destinationObject:nil (snapshot objectForKey: rel name ) ? */ EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship: %@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"classProperties: %@", classProperties); if ([classProperties indexOfObjectIdenticalTo: relationship] != NSNotFound) //(or subst) { BOOL valuesAreEqual = NO; BOOL isToMany = NO; id relationshipCommitedSnapshotValue = nil; NSString *relationshipName = [relationship name]; id relationshipSnapshotValue; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"which=%d", which); //EOFLOGObjectLevelArgs(@"EODatabaseContext",@"OBJECT SNAPSHOT %p:\n%@\n\n",[object snapshot]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot for object %p:\nsnapshot %p (count=%d)= \n%@\n\n", object, snapshot, [snapshot count], snapshot); // substitutionRelationship objectForKey: relationshipSnapshotValue = [snapshot objectForKey: relationshipName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipSnapshotValue (snapshot %p rel name=%@): %@", snapshot, relationshipName, relationshipSnapshotValue); if (which == 0) //Insert currentCommittedSnapshot = [dbOpe dbSnapshot]; else //Update { if (!currentCommittedSnapshot) currentCommittedSnapshot = [self _currentCommittedSnapshotForObject: object]; //OK For Update } //update: _commited //insert: dbSn EOFLOGObjectLevelArgs(@"EODatabaseContext", @"currentCommittedSnapshot %p: %@", currentCommittedSnapshot, currentCommittedSnapshot); relationshipCommitedSnapshotValue = [currentCommittedSnapshot objectForKey: relationshipName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipCommitedSnapshotValue (snapshot %p rel name=%@): %@", currentCommittedSnapshot, relationshipName, relationshipCommitedSnapshotValue); isToMany = [relationship isToMany]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"isToMany: %s", (isToMany ? "YES" : "NO")); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipSnapshotValue %p=%@", relationshipSnapshotValue, relationshipSnapshotValue); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipCommitedSnapshotValue %p=%@", relationshipCommitedSnapshotValue, ([EOFault isFault:relationshipCommitedSnapshotValue] ? (NSString*)@"[Fault]" : (NSString*)relationshipCommitedSnapshotValue)); if (relationshipSnapshotValue == relationshipCommitedSnapshotValue) valuesAreEqual = YES; else if (isNilOrEONull(relationshipSnapshotValue)) valuesAreEqual = isNilOrEONull(relationshipCommitedSnapshotValue); else if (isNilOrEONull(relationshipCommitedSnapshotValue)) valuesAreEqual = isNilOrEONull(relationshipSnapshotValue); else if (isToMany) valuesAreEqual = [relationshipSnapshotValue containsIdenticalObjectsWithArray: relationshipCommitedSnapshotValue]; else // ToOne bu not same object valuesAreEqual = NO; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p valuesAreEqual: %s", object,(valuesAreEqual ? "YES" : "NO")); if (valuesAreEqual) { //Equal Values ! } else { if (isToMany) { //relationshipSnapshotValue shallowCopy // Old Values are removed values NSArray *oldValues = [relationshipCommitedSnapshotValue arrayExcludingObjectsInArray: relationshipSnapshotValue]; // Old Values are newly added values NSArray *newValues = [relationshipSnapshotValue arrayExcludingObjectsInArray: relationshipCommitedSnapshotValue]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"oldValues count=%d", [oldValues count]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"oldValues=%@", oldValues); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newValues count=%d", [newValues count]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newValues=%@", newValues); // Record new values snapshots if ([newValues count] > 0) { int newValuesCount = [newValues count]; int iValue; NSMutableArray *valuesGIDs = [NSMutableArray array]; for (iValue = 0; iValue < newValuesCount; iValue++) { id aValue = [relationshipSnapshotValue objectAtIndex: iValue]; EOGlobalID *aValueGID = [self _globalIDForObject: aValue]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"YYYY valuesGIDs=%@", valuesGIDs); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"YYYY aValueGID=%@", aValueGID); [valuesGIDs addObject:aValueGID]; } [dbOpe recordToManySnapshot:valuesGIDs relationshipName: relationshipName]; } // Nullify removed object relation attributes if ([oldValues count] > 0) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"will call nullifyAttributes from source %p (class %@)", object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p=%@ (class=%@)", object, object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", relationshipName); [self nullifyAttributesInRelationship: relationship sourceObject: object destinationObjects: oldValues]; } // Relay relationship attributes in new objects if ([newValues count] > 0) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"will call relay from source %p (class %@)", object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p=%@ (class=%@)", object, object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", relationshipName); [self relayAttributesInRelationship: relationship sourceObject: object destinationObjects: newValues]; } } else // To One { //id destinationObject=[object storedValueForKey:relationshipName]; if (!isNilOrEONull(relationshipCommitedSnapshotValue)) // a value was removed { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"will call nullifyAttributes from source %p (class %@)", object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p=%@ (class=%@)", object, object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", relationshipName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObject %p=%@ (class=%@)", relationshipCommitedSnapshotValue, relationshipCommitedSnapshotValue, [relationshipCommitedSnapshotValue class]); [self nullifyAttributesInRelationship: relationship sourceObject: object destinationObject: relationshipCommitedSnapshotValue]; } if (!isNilOrEONull(relationshipSnapshotValue)) // a value was added { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"will call relay from source %p relname=%@", object, relationshipName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p=%@ (class=%@)", object, object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", relationshipName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObject %p=%@ (class=%@)", relationshipSnapshotValue, relationshipSnapshotValue, [relationshipSnapshotValue class]); [self relayAttributesInRelationship: relationship sourceObject: object destinationObject: relationshipSnapshotValue]; } } } } else { //!toMany: //dbSnapshot was empty EOFLOGObjectLevelArgs(@"EODatabaseContext", @"will call nullifyAttributesInRelationship on source %p relname=%@", object, [relationship name]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object %p=%@ (class=%@)", object, object, [object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", [relationship name]); [self nullifyAttributesInRelationship: relationship sourceObject: object /*CountryLabel*/ destinationObjects: nil]; } /* NSMutableDictionary *row; NSMutableArray *toManySnapshot, *newToManySnapshot; NSArray *joins = [(EORelationship *)property joins]; NSString *joinName; EOJoin *join; int h, count; id value; name = [(EORelationship *)property name]; row = [NSMutableDictionary dictionaryWithCapacity:4]; count = [joins count]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"rel name=%@", name); if ([property isToMany] == YES) { NSMutableArray *toManyGIDArray; NSArray *toManyObjects; EOGlobalID *toManyGID; id toManyObj; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"rel 1 sourceGID=%@", gid); toManySnapshot = AUTORRELEASE([[self snapshotForSourceGlobalID:gid relationshipName:name] mutableCopy]); if (toManySnapshot == nil) toManySnapshot = [NSMutableArray array]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"rel 1", name); newToManySnapshot = [NSMutableArray arrayWithCapacity:10]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"rel 1", name); toManyObjects = [object storedValueForKey:name]; toManyGIDArray = [NSMutableArray arrayWithCapacity: [toManyObjects count]]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"rel 1", name); enumerator = [toManyObjects objectEnumerator]; while ((toManyObj = [enumerator nextObject])) { toManyGID = [_editingContext globalIDForObject: toManyObj]; [toManyGIDArray addObject:toManyGID]; if ([toManySnapshot containsObject:toManyGID] == NO) { [newToManySnapshot addObject:toManyGID]; for (h=0; h 0) { EOAdaptorChannel *adaptorChannel = nil; EODatabaseChannel *dbChannel = [self _obtainOpenChannel]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p preparingForSave=%d beganTransaction=%d", self, (int)_flags.preparingForSave, (int)_flags.beganTransaction); if (_flags.beganTransaction == NO)//MIRKO { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p [_adaptorContext transactionNestingLevel]=%d", self, (int)[_adaptorContext transactionNestingLevel]); if ([_adaptorContext transactionNestingLevel] == 0) //?? [_adaptorContext beginTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>YES"); _flags.beganTransaction = YES; } adaptorChannel = [dbChannel adaptorChannel]; if (_delegateRespondsTo.willPerformAdaptorOperations == YES) orderedAdaptorOperations = [_delegate databaseContext: self willPerformAdaptorOperations: orderedAdaptorOperations adaptorChannel: adaptorChannel]; NS_DURING { EOFLOGObjectLevel(@"EODatabaseContext", @"performAdaptorOperations:"); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p preparingForSave=%d beganTransaction=%d", self, (int)_flags.preparingForSave, (int)_flags.beganTransaction); [adaptorChannel performAdaptorOperations: orderedAdaptorOperations]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p preparingForSave=%d beganTransaction=%d", self, (int)_flags.preparingForSave, (int)_flags.beganTransaction); EOFLOGObjectLevel(@"EODatabaseContext", @"after performAdaptorOperations:"); } NS_HANDLER { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Exception in performAdaptorOperations:%@", localException); [localException raise]; //MIRKO //TODO /* NSException *exp; NSMutableDictionary *userInfo; EOAdaptorOperation *adaptorOp; userInfo = [NSMutableDictionary dictionaryWithCapacity:10]; [userInfo addEntriesFromDictionary:[localException userInfo]]; [userInfo setObject:self forKey:EODatabaseContextKey]; [userInfo setObject:dbOps forKey:EODatabaseOperationsKey]; adaptorOp = [userInfo objectForKey:EOFailedAdaptorOperationKey]; dbEnum = [dbOps objectEnumerator]; while ((op = [dbEnum nextObject])) if ([[op adaptorOperations] containsObject:adaptorOp] == YES) { [userInfo setObject:op forKey:EOFailedDatabaseOperationKey]; break; } exp = [NSException exceptionWithName:EOGeneralDatabaseException reason:[NSString stringWithFormat: @"%@ -- %@ 0x%x: failed with exception name:%@ reason:\"%@\"", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, [localException name], [localException reason]] userInfo:userInfo]; [exp raise]; */ } NS_ENDHANDLER; //This is not done by mirko: EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p preparingForSave=%d beganTransaction=%d", self, (int)_flags.preparingForSave, (int)_flags.beganTransaction); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe)) { EODatabaseOperator databaseOperator = EODatabaseNothingOperator; //call dbOpe adaptorOperations ? if ([dbOpe databaseOperator] == EODatabaseNothingOperator) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!", dbOpe); } else { EOEntity *entity = nil; NSArray *dbSnapshotKeys = nil; NSMutableDictionary *newRow = nil; NSDictionary *values = nil; id object = nil; NSArray *adaptorOpe = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); object = [dbOpe object]; adaptorOpe = [dbOpe adaptorOperations]; databaseOperator = [dbOpe databaseOperator]; entity = [dbOpe entity]; dbSnapshotKeys = [entity dbSnapshotKeys]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbSnapshotKeys=%@", dbSnapshotKeys); newRow = [dbOpe newRow]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%@", newRow); values = [newRow valuesForKeys: dbSnapshotKeys]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"RECORDSNAPSHOT values=%@", values); //if update: forgetSnapshotForGlobalID: [self recordSnapshot: values forGlobalID: gid]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); if (databaseOperator == EODatabaseUpdateOperator) //OK for update //Do it forInsert too //TODO { NSDictionary *toManySnapshots = [dbOpe toManySnapshots]; if (toManySnapshots) { NSDebugMLog(@"toManySnapshots=%@", toManySnapshots); NSEmitTODO(); //TODONOW [self notImplemented: _cmd]; //TODO } } } } } EOFLOGObjectFnStop(); } - (void)commitChanges { BOOL doIt = NO; NSMutableArray *deletedObjects = [NSMutableArray array]; NSMutableArray *insertedObjects = [NSMutableArray array]; NSMutableArray *updatedObjects = [NSMutableArray array]; NSMutableDictionary *gidChangedUserInfo = nil; EOFLOGObjectFnStart(); [self _assertValidStateWithSelector: @selector(commitChanges)]; //REVOIR: don't do it if no adaptor ope { NSMapEnumerator dbOpeEnum; EOGlobalID *gid = nil; EODatabaseOperation *dbOpe = nil; dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (!doIt && NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe)) { doIt = ([dbOpe adaptorOperations] != nil); } } if (doIt) { if (_flags.beganTransaction != YES)//it is not set here in WO { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else if ([_adaptorContext transactionNestingLevel] == 0) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { NSMapEnumerator dbOpeEnum; EOGlobalID *gid = nil; EODatabaseOperation *dbOpe = nil; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>NO"); _flags.beganTransaction = NO; [_adaptorContext commitTransaction]; //adaptorcontext transactionDidCommit dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe)) { EODatabaseOperator databaseOperator = EODatabaseNothingOperator; EOGlobalID *dbOpeGID = nil; EOGlobalID *newGID = nil; EOEntity *entity = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); [EOObserverCenter suppressObserverNotification]; NS_DURING { databaseOperator = [dbOpe databaseOperator]; entity = [dbOpe entity]; if (databaseOperator == EODatabaseInsertOperator || databaseOperator == EODatabaseUpdateOperator)//OK for update { id object = nil; NSDictionary *newRowValues = nil; //gid isTemporary NSDictionary *primaryKeyDiffs = [dbOpe primaryKeyDiffs];//OK for update if (primaryKeyDiffs) { NSEmitTODO(); NSAssert3(NO,@"primaryKeyDiffs=%@ dbOpe=%@ object=%@", primaryKeyDiffs,dbOpe,[dbOpe object]); //TODO: if primaryKeyDiffs } if (databaseOperator == EODatabaseInsertOperator) { NSArray *classPropertyAttributeNames = [entity classPropertyAttributeNames]; NSDictionary *newRow = [dbOpe newRow]; newRowValues = [newRow valuesForKeys: classPropertyAttributeNames]; //TODO REVOIR !! newGID = [entity globalIDForRow: newRow isFinal: YES]; } else { NSArray *classPropertyAttributes = [entity _classPropertyAttributes];//OK for update newRowValues = [dbOpe rowDiffsForAttributes: classPropertyAttributes];//OK for update } object = [dbOpe object];//OK for update [object takeStoredValuesFromDictionary: newRowValues];//OK for update /*mirko instead: [object takeStoredValuesFromDictionary: [op rowDiffsForAttributes:attributes]]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"-_+ %@ # %@", gid, object); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"-_* %@", [op newRow]); [_database recordSnapshot:[op newRow] forGlobalID:gid]; toManySnapshots = [op toManySnapshots]; toManyEnum = [toManySnapshots keyEnumerator]; while ((key = [toManyEnum nextObject])) [_database recordSnapshot:[toManySnapshots objectForKey:key] forSourceGlobalID:gid relationshipName:key]; */ } } NS_HANDLER { [EOObserverCenter enableObserverNotification]; NSLog(@"EXCEPTION %@", localException); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"EXCEPTION %@", localException); [localException raise]; } NS_ENDHANDLER; [EOObserverCenter enableObserverNotification]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); dbOpeGID = [dbOpe globalID]; //OK for update EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpeGID=%@", dbOpeGID); switch (databaseOperator) { case EODatabaseInsertOperator: [insertedObjects addObject: dbOpeGID]; if (!gidChangedUserInfo) gidChangedUserInfo = (NSMutableDictionary *) [NSMutableDictionary dictionary]; [gidChangedUserInfo setObject: newGID forKey: dbOpeGID]; //[_editingContext globalIDForObject:object]]; break; case EODatabaseDeleteOperator: [deletedObjects addObject: dbOpeGID]; [_database forgetSnapshotForGlobalID: dbOpeGID]; //Mirko/?? break; case EODatabaseUpdateOperator: [updatedObjects addObject: dbOpeGID]; break; case EODatabaseNothingOperator: break; } } } } EOFLOGObjectLevel(@"EODatabaseContext", @"call _cleanUpAfterSave"); [self _cleanUpAfterSave];//OK for update if (doIt) { //from mirko. seems ok if (gidChangedUserInfo) { EOFLOGObjectLevel(@"EODatabaseContext", @"post EOGlobalIDChangedNotification"); [[NSNotificationCenter defaultCenter] postNotificationName: EOGlobalIDChangedNotification object: nil userInfo: gidChangedUserInfo]; } EOFLOGObjectLevel(@"EODatabaseContext", @"post EOObjectsChangedInStoreNotification"); [[NSNotificationCenter defaultCenter] postNotificationName: @"EOObjectsChangedInStoreNotification" object: _database userInfo: [NSDictionary dictionaryWithObjectsAndKeys: deletedObjects, @"deleted", insertedObjects, @"inserted", updatedObjects, @"updated", nil, nil]]; //call self _snapshotsChangedInDatabase: } EOFLOGObjectFnStop(); } - (void)rollbackChanges { // TODO //adaptorcontext transactionNestingLevel //if 0 ? _cleanUpAfterSave EOFLOGObjectFnStart(); if (_flags.beganTransaction == YES) { [_adaptorContext rollbackTransaction]; EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>NO"); _flags.beganTransaction = NO; _numLocked = 0; _lockedObjects = NSZoneRealloc(NULL, _lockedObjects, _LOCK_BUFFER*sizeof(id)); NSResetMapTable(_dbOperationsByGlobalID); /* //TODO [_snapshots removeAllObjects]; [_toManySnapshots removeAllObjects]; */ } EOFLOGObjectFnStop(); } - (NSDictionary *)valuesForKeys: (NSArray *)keys object: (id)object { //OK EOEntity *entity; EODatabaseOperation *dbOpe; NSDictionary *newRow; NSDictionary *values = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p keys=%@", object, keys); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p (class=%@)", object, [object class]); //NSAssert(object, @"No object"); if (!isNilOrEONull(object)) { entity = [_database entityForObject: object]; NSAssert1(entity, @"No entity for object %@", object); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity name=%@", [entity name]); dbOpe = [self databaseOperationForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%p", dbOpe); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); newRow = [dbOpe newRow]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%p", newRow); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%@", newRow); values = [newRow valuesForKeys: keys]; } else { EOFLOGObjectLevel(@"EODatabaseContext", @"No object"); values = [NSDictionary dictionary]; } // EOFLOGObjectLevelArgs(@"EODatabaseContext", @"values=%@", values); EOFLOGObjectFnStop(); return values; } -(void)nullifyAttributesInRelationship: (EORelationship*)relationship sourceObject: (id)sourceObject destinationObject: (id)destinationObject { EODatabaseOperation *sourceDBOpe = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObject=%@", sourceObject); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObject=%@", destinationObject); if (destinationObject) { //Get SourceObject database operation sourceDBOpe = [self databaseOperationForObject: sourceObject]; //TODO: useIt EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceDBOpe=%@", sourceDBOpe); if ([relationship isToManyToOne]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { // Key a dictionary of two array: destinationKeys and sourceKeys NSDictionary *sourceToDestinationKeyMap = [relationship _sourceToDestinationKeyMap]; //{destinationKeys = (customerCode); sourceKeys = (code); } BOOL foreignKeyInDestination = [relationship foreignKeyInDestination]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceToDestinationKeyMap=%@", sourceToDestinationKeyMap); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"foreignKeyInDestination=%d", foreignKeyInDestination); if (foreignKeyInDestination) { NSArray *destinationKeys = [sourceToDestinationKeyMap objectForKey: @"destinationKeys"];//(customerCode) int i, destinationKeysCount = [destinationKeys count]; NSMutableDictionary *changes = [NSMutableDictionary dictionaryWithCapacity: destinationKeysCount]; id null = [EONull null]; for (i = 0 ;i < destinationKeysCount; i++) { id destinationKey = [destinationKeys objectAtIndex: i]; [changes setObject: null forKey: destinationKey]; } NSAssert1(destinationObject, @"No destinationObject for call of recordUpdateForObject:changes: changes: %@", changes); [self recordUpdateForObject: destinationObject changes: changes]; } else { //Do nothing ? NSEmitTODO(); //[self notImplemented: _cmd]; //TODO } } } } - (void)nullifyAttributesInRelationship: (EORelationship*)relationship sourceObject: (id)sourceObject destinationObjects: (NSArray*)destinationObjects { int destinationObjectsCount = 0; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObject=%@", sourceObject); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObjects=%@", destinationObjects); destinationObjectsCount = [destinationObjects count]; if (destinationObjectsCount > 0) { int i; for (i = 0; i < destinationObjectsCount; i++) { id object = [destinationObjects objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObject %p=%@ (class %@)", object, object, [object class]); [self nullifyAttributesInRelationship: relationship sourceObject: sourceObject destinationObject: object]; } } EOFLOGObjectFnStop(); } - (void)relayAttributesInRelationship: (EORelationship*)relationship sourceObject: (id)sourceObject destinationObjects: (NSArray*)destinationObjects { int destinationObjectsCount = 0; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceObject=%@", sourceObject); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationObjects=%@", destinationObjects); destinationObjectsCount = [destinationObjects count]; if (destinationObjectsCount > 0) { int i; for (i = 0; i < destinationObjectsCount; i++) { id object = [destinationObjects objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"destinationObject %p=%@ (class %@)", object,object,[object class]); [self relayAttributesInRelationship: (EORelationship*)relationship sourceObject: (id)sourceObject destinationObject: object]; } } EOFLOGObjectFnStop(); } - (NSDictionary*)relayAttributesInRelationship: (EORelationship*)relationship sourceObject: (id)sourceObject destinationObject: (id)destinationObject { //OK NSMutableDictionary *relayedValues = nil; EODatabaseOperation *sourceDBOpe = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"sourceObject %p=%@ (class=%@)", sourceObject,sourceObject,[sourceObject class]); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"destinationObject %p=%@ (class=%@)", destinationObject,destinationObject,[destinationObject class]); //Get SourceObject database operation sourceDBOpe = [self databaseOperationForObject: sourceObject]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceDBOpe=%@", sourceDBOpe); if ([sourceDBOpe databaseOperator] == EODatabaseNothingOperator) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!", sourceDBOpe); } if ([relationship isToManyToOne]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { // Key a dictionary of two array: destinationKeys and sourceKeys NSDictionary *sourceToDestinationKeyMap = [relationship _sourceToDestinationKeyMap];//{destinationKeys = (customerCode); sourceKeys = (code); } NSArray *destinationKeys = [sourceToDestinationKeyMap objectForKey: @"destinationKeys"];//(customerCode) NSArray *sourceKeys = [sourceToDestinationKeyMap objectForKey: @"sourceKeys"];//(code) NSMutableDictionary *sourceNewRow = [sourceDBOpe newRow];//OK in foreignKeyInDestination BOOL foreignKeyInDestination = [relationship foreignKeyInDestination]; int i, count; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceToDestinationKeyMap=%@", sourceToDestinationKeyMap); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationKeys=%@", destinationKeys); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceKeys=%@", sourceKeys); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceNewRow=%@", sourceNewRow); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"foreignKeyInDestination=%s", (foreignKeyInDestination ? "YES" : "NO")); NSAssert([destinationKeys count] == [sourceKeys count], @"destination keys count!=source keys count"); if (foreignKeyInDestination || [relationship propagatesPrimaryKey]) { relayedValues = AUTORELEASE([[sourceNewRow valuesForKeys: sourceKeys] mutableCopy]);// {code = 0; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relayedValues=%@", relayedValues); count = [relayedValues count]; for (i = 0; i < count; i++) { NSString *sourceKey = [sourceKeys objectAtIndex: i]; NSString *destKey = [destinationKeys objectAtIndex: i]; id sourceValue = [relayedValues objectForKey: sourceKey]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceKey=%@", sourceKey); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destKey=%@", destKey); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceValue=%@", sourceValue); [relayedValues removeObjectForKey: sourceKey]; [relayedValues setObject: sourceValue forKey: destKey]; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relayedValues=%@", relayedValues); NSAssert1(destinationObject, @"No destinationObject for call of recordUpdateForObject:changes: relayedValues: %@", relayedValues); [self recordUpdateForObject: destinationObject changes: relayedValues]; } else { //Verify !! NSDictionary *destinationValues; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Call valuesForKeys destinationObject %p (class %@)", destinationObject, [destinationObject class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationKeys=%@", destinationKeys); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceKeys=%@", sourceKeys); //Now take destinationKeys values destinationValues = [self valuesForKeys: destinationKeys object: destinationObject]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationValues=%@", destinationValues); //And put these values for source keys in the return object (sourceValues) count = [destinationKeys count]; relayedValues = (NSMutableDictionary*)[NSMutableDictionary dictionary]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relayedValues=%@", relayedValues); for (i = 0; i < count; i++) { id destinationKey = [destinationKeys objectAtIndex: i]; id sourceKey = [sourceKeys objectAtIndex: i]; id destinationValue = [destinationValues objectForKey: destinationKey]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationKey=%@", destinationKey); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"sourceKey=%@", sourceKey); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destinationValue=%@", destinationValue); if (!isNilOrEONull(destinationValue))//?? or always [relayedValues setObject: destinationValue forKey: sourceKey]; } //Put these values in source object database ope new row EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relayedValues=%@", relayedValues); [sourceNewRow takeValuesFromDictionary: relayedValues]; } } if ([sourceDBOpe databaseOperator] == EODatabaseNothingOperator) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!", sourceDBOpe); } EOFLOGObjectFnStop(); return relayedValues; //Mirko Code: /* NSMutableArray *keys; NSDictionary *values; EOGlobalID *relationshipGID; id objectTo; objectTo = [object storedValueForKey:name]; relationshipGID = [_editingContext globalIDForObject:objectTo]; if ([self ownsObject:objectTo] == YES) { for (h=0; h propagatesPrimaryKey=%s", object, [relationship name], (propagatesPrimaryKey ? "YES" : "NO")); if (propagatesPrimaryKey) { NSString *relName = [substRelationship name]; //this one ?? EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relName=%@", relName); if ([classPropertyNames containsObject: relName]) { id value = nil; id snapshot = nil; id snapshotValue = nil; BOOL isToMany = NO; value = [object storedValueForKey: relName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"value=%@", value); snapshot = [self _currentCommittedSnapshotForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot=%@", snapshot); snapshotValue = [snapshot objectForKey:relName];//ret nil EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshotValue=%@", snapshotValue); isToMany = [substRelationship isToMany]; //this one ?? EOFLOGObjectLevelArgs(@"EODatabaseContext", @"isToMany=%s", (isToMany ? "YES" : "NO")); if (isToMany) { int valueValuesCount = 0; int iValueValue = 0; value = [value shallowCopy]; valueValuesCount = [value count]; iValueValue = 0; for (iValueValue = 0; iValueValue < valueValuesCount; iValueValue++) { id valueValue = [value objectAtIndex: iValueValue]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"valueValue=%@", valueValue); [self relayPrimaryKey: pk sourceObject: object destObject: valueValue relationship: substRelationship]; //this one ?? } } else { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"value=%@", value); // 1:1 relationships may be optional so we may have no value here if (value) { [self relayPrimaryKey: pk sourceObject: object destObject: value relationship: substRelationship]; //this one ?? }; } } } } EOFLOGObjectFnStop(); } - (void) createAdaptorOperationsForDatabaseOperation: (EODatabaseOperation*)dbOpe attributes: (NSArray*)attributes { //NEAR OK BOOL isSomethingTodo = YES; EOEntity *entity = nil; EODatabaseOperator dbOperator = EODatabaseNothingOperator; NSDictionary *changedValues = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); NSAssert(dbOpe, @"No operation"); entity = [dbOpe entity]; //OK dbOperator = [dbOpe databaseOperator]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attributes=%@", attributes); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOperator=%d", (int)dbOperator); switch (dbOperator) { case EODatabaseUpdateOperator: { changedValues = [dbOpe rowDiffsForAttributes:attributes]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"changedValues %p=%@", changedValues, changedValues); if ([changedValues count] == 0) isSomethingTodo = NO; else { } } break; case EODatabaseInsertOperator: { changedValues = [dbOpe newRow]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"changedValues %p=%@", changedValues, changedValues); } break; case EODatabaseDeleteOperator: { isSomethingTodo = YES; } break; case EODatabaseNothingOperator: { //Nothing! } break; default: { NSEmitTODO(); // [self notImplemented:_cmd]; //TODO } break; } if (isSomethingTodo) { EOAdaptorOperation *adaptorOpe = nil; NSString *procedureOpeName = nil; EOAdaptorOperator adaptorOperator = EOAdaptorUndefinedOperator; EOStoredProcedure *storedProcedure = nil; NSDictionary *valuesToWrite = nil; EOQualifier *lockingQualifier = nil; switch (dbOperator) { case EODatabaseUpdateOperator: case EODatabaseDeleteOperator: { NSArray *pkAttributes; NSArray *lockingAttributes; NSDictionary *dbSnapshot; pkAttributes = [self primaryKeyAttributesForAttributes: attributes entity: entity]; lockingAttributes = [self lockingAttributesForAttributes: attributes entity: entity]; dbSnapshot = [dbOpe dbSnapshot]; lockingQualifier = [self qualifierForLockingAttributes: lockingAttributes primaryKeyAttributes: pkAttributes entity: entity snapshot: dbSnapshot]; NSEmitTODO(); //TODO=self lockingNonQualifiableAttributes:##### ret nil EOFLOGObjectLevelArgs(@"EODatabaseContext", @"lockingQualifier=%@", lockingQualifier); /*MIRKO for UPDATE: //TODO-NOW { if ([self isObjectLockedWithGlobalID:gid] == NO) { EOAdaptorOperation *lockOperation; EOQualifier *qualifier; EOAttribute *attribute; NSEnumerator *attrsEnum; NSArray *attrsUsedForLocking, *primaryKeyAttributes; NSMutableDictionary *qualifierSnapshot, *lockSnapshot; NSMutableArray *lockAttributes; lockOperation = [EOAdaptorOperation adaptorOperationWithEntity: entity]; attrsUsedForLocking = [entity attributesUsedForLocking]; primaryKeyAttributes = [entity primaryKeyAttributes]; qualifierSnapshot = [NSMutableDictionary dictionaryWithCapacity:16]; lockSnapshot = [NSMutableDictionary dictionaryWithCapacity:8]; lockAttributes = [NSMutableArray arrayWithCapacity:8]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"lock start %@", snapshot); attrsEnum = [primaryKeyAttributes objectEnumerator]; while ((attribute = [attrsEnum nextObject])) { NSString *name = [attribute name]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@" %@", name); [lockSnapshot setObject:[snapshot objectForKey:name] forKey:name]; } EOFLOGObjectLevelArgs(@"EODatabaseContext",@"lock stop"); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"lock start2"); attrsEnum = [attrsUsedForLocking objectEnumerator]; while ((attribute = [attrsEnum nextObject])) { NSString *name = [attribute name]; if ([primaryKeyAttributes containsObject:attribute] == NO) { if ([attribute adaptorValueType] == EOAdaptorBytesType) { [lockAttributes addObject:attribute]; [lockSnapshot setObject:[snapshot objectForKey:name] forKey:name]; } else [qualifierSnapshot setObject:[snapshot objectForKey:name] forKey:name]; } } EOFLOGObjectLevelArgs(@"EODatabaseContext",@"lock stop2"); qualifier = AUTORELEASE([[EOAndQualifier alloc] initWithQualifiers: [entity qualifierForPrimaryKey: [entity primaryKeyForGlobalID: (EOKeyGlobalID *)gid]], [EOQualifier qualifierToMatchAllValues: qualifierSnapshot], nil]); if ([lockAttributes count] == 0) lockAttributes = nil; if ([lockSnapshot count] == 0) lockSnapshot = nil; [lockOperation setAdaptorOperator:EOAdaptorLockOperator]; [lockOperation setQualifier:qualifier]; [lockOperation setAttributes:lockAttributes]; [lockOperation setChangedValues:lockSnapshot]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"*+ %@", lockSnapshot); [op addAdaptorOperation:lockOperation]; } */ } break; case EODatabaseInsertOperator: break; case EODatabaseNothingOperator: break; } adaptorOpe = [EOAdaptorOperation adaptorOperationWithEntity: entity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOpe=%@", adaptorOpe); switch (dbOperator) { case EODatabaseInsertOperator: procedureOpeName = @"EOInsertProcedure"; adaptorOperator = EOAdaptorInsertOperator; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"changedValues %p=%@", changedValues, changedValues); valuesToWrite = [self valuesToWriteForAttributes: attributes entity: entity changedValues: changedValues]; break; case EODatabaseUpdateOperator: procedureOpeName = @"EOUpdateProcedure"; adaptorOperator = EOAdaptorUpdateOperator; valuesToWrite = [self valuesToWriteForAttributes: attributes entity: entity changedValues: changedValues]; break; case EODatabaseDeleteOperator: procedureOpeName = @"EODeleteProcedure"; adaptorOperator = EOAdaptorDeleteOperator; /* MIRKO NSMutableArray *newKeys = AUTORELEASE([[NSMutableArray alloc] initWithCapacity:count]); NSMutableArray *newVals = AUTORELEASE([[NSMutableArray alloc] initWithCapacity:count]); if ([entity isReadOnly] == YES) { [NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: cannot delete object for readonly entity %@", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, [entity name]]; } [aOp setAdaptorOperator:EOAdaptorDeleteOperator]; count = [primaryKeys count]; for (i = 0; i < count; i++) { EOAttribute *attribute = [primaryKeys objectAtIndex:i]; NSString *key = [attribute name]; id val; if ([attribute isFlattened] == NO) { // Turbocat //val = [object storedValueForKey:key]; if (currentSnapshot) { val = [currentSnapshot objectForKey:key]; } if (!val) { [NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: cannot delete object (snapshot) '%@' for unkown primarykey value '%@'", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, currentSnapshot, key]; } if (val == nil) val = null; [newKeys addObject:key]; [newVals addObject:val]; } } row = [NSDictionary dictionaryWithObjects:newVals forKeys:newKeys]; [aOp setQualifier:[entity qualifierForPrimaryKey:[op newRow]]]; ==>NO? in _commitTransaction [self forgetSnapshotForGlobalID:[op globalID]]; */ break; case EODatabaseNothingOperator: EOFLOGObjectLevelArgs(@"EODatabaseContext", @"Db Ope %@ for Nothing !!!", dbOpe); //Nothing? break; default: NSEmitTODO(); [self notImplemented: _cmd]; //TODO break; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOperator=%d", adaptorOperator); // only for insert ?? storedProcedure = [entity storedProcedureForOperation: procedureOpeName]; if (storedProcedure) { adaptorOperator = EOAdaptorStoredProcedureOperator; NSEmitTODO(); [self notImplemented: _cmd]; //TODO } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOperator=%d", adaptorOperator); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOpe=%@", adaptorOpe); if (adaptorOpe) { [adaptorOpe setAdaptorOperator: adaptorOperator]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"valuesToWrite=%@", valuesToWrite); if (valuesToWrite) [adaptorOpe setChangedValues: valuesToWrite]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"lockingQualifier=%@", lockingQualifier); if (lockingQualifier) [adaptorOpe setQualifier: lockingQualifier]; [dbOpe addAdaptorOperation: adaptorOpe]; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOpe=%@", adaptorOpe); } EOFLOGObjectFnStop(); } - (void) createAdaptorOperationsForDatabaseOperation: (EODatabaseOperation*)dbOpe { //OK for Update - Test for others NSArray *attributesToSave = nil; NSMutableArray *attributes = nil; int i, count; EODatabaseOperator dbOperator = EODatabaseNothingOperator; EOEntity *entity = [dbOpe entity]; //OK NSDictionary *rowDiffs = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); [self processSnapshotForDatabaseOperation: dbOpe]; //OK dbOperator = [dbOpe databaseOperator]; //OK if (dbOperator == EODatabaseUpdateOperator) //OK { rowDiffs = [dbOpe rowDiffs]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"rowDiffs=%@", rowDiffs); } attributesToSave = [entity _attributesToSave]; //OK for update, OK for insert attributes = [NSMutableArray array]; count = [attributesToSave count]; for (i = 0; i < count; i++) { EOAttribute *attribute = [attributesToSave objectAtIndex: i]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attribute=%@", attribute); if (![attribute isFlattened] && ![attribute isDerived]) //VERIFY { [attributes addObject: attribute]; if ([rowDiffs objectForKey: [attribute name]] && [attribute isReadOnly]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO: excption ??? } } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attributes=%@", attributes); [self createAdaptorOperationsForDatabaseOperation: dbOpe attributes: attributes]; } - (NSArray*) orderAdaptorOperations { //seems OK NSMutableArray *orderedAdaptorOpe = (NSMutableArray*)[NSMutableArray array]; EOFLOGObjectFnStart(); //MIRKO if (_delegateRespondsTo.willOrderAdaptorOperations == YES) orderedAdaptorOpe = (NSMutableArray*) [_delegate databaseContext: self willOrderAdaptorOperationsFromDatabaseOperations: NSAllMapTableValues(_dbOperationsByGlobalID)]; else { NSArray *entities = nil; NSMutableArray *adaptorOperations = [NSMutableArray array]; NSMapEnumerator dbOpeEnum; EOGlobalID *gid = nil; EODatabaseOperation *dbOpe = nil; NSHashTable *entitiesHashTable = NSCreateHashTable(NSNonOwnedPointerHashCallBacks,32); dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID); while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe)) { NSArray *dbOpeAdaptorOperations = [dbOpe adaptorOperations]; int i, count = [dbOpeAdaptorOperations count]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); for (i = 0; i < count; i++) { EOAdaptorOperation *adaptorOpe = [dbOpeAdaptorOperations objectAtIndex: i]; EOEntity *entity = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorOpe=%@", adaptorOpe); [adaptorOperations addObject: adaptorOpe]; entity = [adaptorOpe entity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity=%@", [entity name]); NSHashInsertIfAbsent(entitiesHashTable, entity); } } entities = NSAllHashTableObjects(entitiesHashTable); NSFreeHashTable(entitiesHashTable); entitiesHashTable = NULL; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entities=%@", entities); { NSArray *entityNameOrderingArray = [self entityNameOrderingArrayForEntities:entities]; int iAdaptoOpe = 0; int adaptorOpeCount = [adaptorOperations count]; int entitiesCount = [entityNameOrderingArray count]; int iEntity; for (iEntity = 0; iEntity < entitiesCount; iEntity++) { EOEntity *entity = [entityNameOrderingArray objectAtIndex: iEntity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity=%@", [entity name]); for (iAdaptoOpe = 0; iAdaptoOpe < adaptorOpeCount; iAdaptoOpe++) { EOAdaptorOperation *adaptorOpe = [adaptorOperations objectAtIndex: iAdaptoOpe]; EOEntity *opeEntity = [adaptorOpe entity]; if (opeEntity == entity) [orderedAdaptorOpe addObject: adaptorOpe]; } } NSAssert2([orderedAdaptorOpe count] == adaptorOpeCount, @"Different ordered (%d) an unordered adaptor operations count (%d)", [orderedAdaptorOpe count], adaptorOpeCount); } } EOFLOGObjectFnStop(); return orderedAdaptorOpe; } - (NSArray*) entitiesOnWhichThisEntityDepends: (EOEntity*)entity { NSMutableArray *entities = nil; NSArray *relationships = nil; int i, count; EOFLOGObjectFnStart(); relationships = [entity relationships]; count = [relationships count]; for (i = 0; i < count; i++) { EORelationship *relationship = [relationships objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); if (![relationship isToMany]) //If to many: do nothing { if ([relationship isFlattened]) { //TODO VERIFY EOExpressionArray *definitionArray=[relationship _definitionArray]; EORelationship *firstRelationship=[definitionArray objectAtIndex:0]; EOEntity *firstDefEntity=[firstRelationship destinationEntity]; NSArray *defDependEntities=[self entitiesOnWhichThisEntityDepends:firstDefEntity]; if ([defDependEntities count]>0) { if (!entities) entities = [NSMutableArray array]; [entities addObjectsFromArray: defDependEntities]; }; } else { //Here ?? EOEntity *destinationEntity = [relationship destinationEntity]; EORelationship *inverseRelationship = [relationship anyInverseRelationship]; if ([inverseRelationship isToMany]) { //Do nothing ? } else { if ([inverseRelationship propagatesPrimaryKey]) { //OK if (!entities) entities = [NSMutableArray array]; [entities addObject: destinationEntity]; } else { if ([inverseRelationship ownsDestination]) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } } } } } } EOFLOGObjectFnStop(); return entities; } - (NSArray*)entityNameOrderingArrayForEntities: (NSArray*)entities { //TODO NSMutableArray *ordering = [NSMutableArray array]; NSMutableSet *orderedEntities = [NSMutableSet set]; /*EODatabase *database = [self database]; NSArray *models = [database models];*/ NSMutableDictionary *dependsDict = [NSMutableDictionary dictionary]; int i, count = [entities count]; //TODO NSArray* originalOrdering=... /*TODO for each mdoel: userInfo (ret nil) */ for (i = 0; i < count; i++) { //OK EOEntity *entity=[entities objectAtIndex: i]; NSArray *dependsEntities = [self entitiesOnWhichThisEntityDepends: entity]; if ([dependsEntities count]) [dependsDict setObject: dependsEntities forKey: [entity name]]; } ordering = [NSMutableArray array]; for (i = 0; i < count; i++) { EOEntity *entity = [entities objectAtIndex: i]; [self insertEntity: entity intoOrderingArray: ordering withDependencies: dependsDict processingSet: orderedEntities]; } //TODO /* model userInfo //ret nil setUserInfo: {EOEntityOrdering = ordering; } */ return ordering; } - (BOOL) isValidQualifierTypeForAttribute: (EOAttribute*)attribute { //OK BOOL isValid = NO; EOEntity *entity = nil; EOModel *model = nil; EODatabase *database = nil; EOAdaptor *adaptor = nil; NSString *externalType = nil; EOFLOGObjectFnStart(); entity = [attribute entity]; NSAssert1(entity, @"No entity for attribute %@", attribute); model = [entity model]; database = [self database]; adaptor = [database adaptor]; externalType = [attribute externalType]; isValid = [adaptor isValidQualifierType: externalType model: model]; //TODO REMOVE if (!isValid) { EOFLOGObjectLevelArgs(@"EODatabaseContext",@"attribute=%@", attribute); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"externalType=%@", externalType); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"entity name=%@", entity); } EOFLOGObjectFnStop(); return isValid; } - (id) lockingNonQualifiableAttributes: (NSArray*)attributes { //TODO finish EOEntity *entity = nil; NSArray *attributesUsedForLocking = nil; int i, count = 0; count = [attributes count]; for (i = 0; i < count; i++) { id attribute = [attributes objectAtIndex: i]; if (!entity) { entity = [attribute entity]; attributesUsedForLocking = [entity attributesUsedForLocking]; } if (![self isValidQualifierTypeForAttribute: attribute]) { NSEmitTODO(); // [self notImplemented:_cmd]; //TODO } else { NSEmitTODO(); //Nothing ?? // [self notImplemented:_cmd]; //TODO ?? } } return nil;//?? } - (NSArray*) lockingAttributesForAttributes: (NSArray*)attributes entity: (EOEntity*)entity { //TODO NSArray *retAttributes = nil; int i, count = 0; NSArray *attributesUsedForLocking = nil; EOFLOGObjectFnStart(); attributesUsedForLocking = [entity attributesUsedForLocking]; count = [attributes count]; for (i = 0; i < count; i++) { id attribute = [attributes objectAtIndex: i]; //do this on 1st only BOOL isFlattened = [attribute isFlattened]; if (isFlattened) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { NSArray *rootAttributesUsedForLocking = [entity rootAttributesUsedForLocking]; retAttributes = rootAttributesUsedForLocking; } } EOFLOGObjectFnStop(); return retAttributes; //TODO } - (NSArray*) primaryKeyAttributesForAttributes: (NSArray*)attributes entity: (EOEntity*)entity { //TODO NSArray *retAttributes = nil; int i, count = 0; EOFLOGObjectFnStart(); //TODO count = [attributes count]; for (i = 0; i < count; i++) { id attribute = [attributes objectAtIndex: i]; BOOL isFlattened = [attribute isFlattened]; //call isFlattened on 1st only if (isFlattened) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { NSArray *primaryKeyAttributes = [entity primaryKeyAttributes]; retAttributes = primaryKeyAttributes; } } EOFLOGObjectFnStop(); return retAttributes; } - (EOQualifier*) qualifierForLockingAttributes: (NSArray*)attributes primaryKeyAttributes: (NSArray*)primaryKeyAttributes entity: (EOEntity*)entity snapshot: (NSDictionary*)snapshot { //OK EOQualifier *qualifier = nil; NSMutableArray *qualifiers = nil; int which; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attributes=%@", attributes); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"primaryKeyAttributes=%@", primaryKeyAttributes); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot=%@", snapshot); //First use primaryKeyAttributes, next use attributes for (which = 0; which < 2; which++) { NSArray *array = (which == 0 ? primaryKeyAttributes : attributes); int i, count = [array count]; for (i = 0; i < count; i++) { EOAttribute *attribute = [array objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attribute=%@", attribute); if (which == 0 || ![primaryKeyAttributes containsObject: attribute])// Test if we haven't already processed it { if (![self isValidQualifierTypeForAttribute: attribute]) { NSLog(@"Invalid externalType for attribute '%@' of entity named '%@' - model '%@'", [attribute name], [[attribute entity] name], [[[attribute entity] model] name]); NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { NSString *attributeName = nil; NSString *snapName = nil; id value = nil; EOQualifier *aQualifier = nil; attributeName = [attribute name]; NSAssert1(attributeName, @"no attribute name for attribute %@", attribute); snapName = [entity snapshotKeyForAttributeName: attributeName]; NSAssert2(snapName, @"no snapName for attribute %@ in entity %@", attributeName, [entity name]); value = [snapshot objectForKey:snapName]; if (!value) { EOFLOGObjectLevel(@"EODatabaseContext", @"NO VALUE"); } NSAssert4(value, @"no value for %@ in %p %@ entity %@", snapName, snapshot, snapshot, [entity name]); aQualifier = [EOKeyValueQualifier qualifierWithKey: attributeName operatorSelector: @selector(isEqualTo:) value: value]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"aQualifier=%@", aQualifier); if (!qualifiers) qualifiers = [NSMutableArray array]; [qualifiers addObject: aQualifier]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"qualifiers=%@", qualifiers); } } } } if ([qualifiers count] == 1) qualifier = [qualifiers objectAtIndex: 0]; else qualifier = [EOAndQualifier qualifierWithQualifierArray: qualifiers]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"qualifier=%@", qualifier); EOFLOGObjectFnStop(); return qualifier; } - (void) insertEntity: (EOEntity*)entity intoOrderingArray: (NSMutableArray*)orderingArray withDependencies: (NSDictionary*)dependencies processingSet: (NSMutableSet*)processingSet { //TODO: manage dependencies {CustomerCredit = (); } // and processingSet [orderingArray addObject: entity]; [processingSet addObject: [entity name]]; } - (void) processSnapshotForDatabaseOperation: (EODatabaseOperation*)dbOpe { //Near OK EOAdaptor *adaptor = [_database adaptor];//OK EOEntity *entity = [dbOpe entity];//OK NSDictionary *newRow = nil; NSDictionary *dbSnapshot = nil; NSEnumerator *attrNameEnum = nil; id attrName = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); newRow = [dbOpe newRow]; //OK{a3code = Q77; code = Q7; numcode = 007; } //ALLOK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow %p=%@", newRow, newRow); dbSnapshot = [dbOpe dbSnapshot]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbSnapshot %p=%@", dbSnapshot, dbSnapshot); attrNameEnum = [newRow keyEnumerator]; while ((attrName = [attrNameEnum nextObject])) { EOAttribute *attribute = [entity attributeNamed: attrName]; id newRowValue = nil; id dbSnapshotValue = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attribute=%@", attribute); newRowValue = [newRow objectForKey:attrName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRowValue=%@", newRowValue); dbSnapshotValue = [dbSnapshot objectForKey: attrName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbSnapshotValue=%@", dbSnapshotValue); if (dbSnapshotValue && ![newRowValue isEqual: dbSnapshotValue]) { id adaptorValue = [adaptor fetchedValueForValue: newRowValue attribute: attribute]; //this call is OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"adaptorValue=%@", adaptorValue); //TODO-NOW SO WHAT ?? may be replacing newRow diff values by adaptorValue if different ???? } } EOFLOGObjectFnStop(); } - (NSDictionary*) valuesToWriteForAttributes: (NSArray*)attributes entity: (EOEntity*)entity changedValues: (NSDictionary*)changedValues { //NEAR OK NSMutableDictionary *valuesToWrite = [NSMutableDictionary dictionary]; BOOL isReadOnlyEntity = NO; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attributes=%@", attributes); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity=%@", [entity name]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"changedValues=%@", changedValues); isReadOnlyEntity = [entity isReadOnly]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"isReadOnlyEntity=%s", (isReadOnlyEntity ? "YES" : "NO")); if (isReadOnlyEntity) { NSEmitTODO(); [self notImplemented: _cmd]; //TODO } else { int i, count = [attributes count]; for (i = 0; i < count; i++) { EOAttribute *attribute = [attributes objectAtIndex: i]; BOOL isReadOnly = [attribute isReadOnly]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attribute=%@", attribute); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"isReadOnly=%s", (isReadOnly ? "YES" : "NO")); if (isReadOnly) { NSEmitTODO(); NSDebugMLog(@"attribute=%@",attribute); [self notImplemented: _cmd]; //TODO } else { NSString *attrName = [attribute name]; NSString *snapName = nil; id value = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"attrName=%@", attrName); snapName = [entity snapshotKeyForAttributeName: attrName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapName=%@", snapName); value = [changedValues objectForKey: snapName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"value=%@", value); if (value) [valuesToWrite setObject: value forKey: attrName]; } } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"valuesToWrite=%@", valuesToWrite); EOFLOGObjectFnStop(); return valuesToWrite; } @end @implementation EODatabaseContext(EOBatchFaulting) - (void)batchFetchRelationship: (EORelationship *)relationship forSourceObjects: (NSArray *)objects editingContext: (EOEditingContext *)editingContext { // TODO NSMutableArray *qualifierArray, *valuesArray, *toManySnapshotArray; NSMutableDictionary *values; NSArray *array; NSEnumerator *objsEnum, *joinsEnum, *keyEnum; NSString *key; EOFetchSpecification *fetch; EOQualifier *qualifier; EOFault *fault; EOJoin *join; BOOL equal; int i, count; id object; qualifierArray = [NSMutableArray array]; valuesArray = [NSMutableArray array]; toManySnapshotArray = [NSMutableArray array]; objsEnum = [objects objectEnumerator]; while ((object = [objsEnum nextObject])) { values = [NSMutableDictionary dictionaryWithCapacity: 4]; fault = [object valueForKey: [relationship name]]; [EOFault clearFault: fault]; joinsEnum = [[relationship joins] objectEnumerator]; while ((join = [joinsEnum nextObject])) { [values setObject: [object valueForKey: [[join sourceAttribute] name]] forKey: [[join destinationAttribute] name]]; } [valuesArray addObject: values]; [toManySnapshotArray addObject: [NSMutableArray array]]; [qualifierArray addObject: [EOQualifier qualifierToMatchAllValues: values]]; } if ([qualifierArray count] == 1) qualifier = [qualifierArray objectAtIndex: 0]; else qualifier = [EOOrQualifier qualifierWithQualifierArray: qualifierArray]; fetch = [EOFetchSpecification fetchSpecificationWithEntityName: [[relationship destinationEntity] name] qualifier: qualifier sortOrderings: nil]; array = [self objectsWithFetchSpecification: fetch editingContext: editingContext]; count = [valuesArray count]; objsEnum = [array objectEnumerator]; while ((object = [objsEnum nextObject])) { for (i = 0; i < count; i++) { equal = YES; values = [valuesArray objectAtIndex: i]; keyEnum = [values keyEnumerator]; while ((key = [keyEnum nextObject])) { if ([[object valueForKey: key] isEqual: [values objectForKey:key]] == NO) { equal = NO; break; } } if (equal == YES) { [[[objects objectAtIndex: i] valueForKey: [relationship name]] addObject: object]; [[toManySnapshotArray objectAtIndex: i] addObject: [editingContext globalIDForObject: object]]; break; } } } EOFLOGObjectLevel(@"EODatabaseContext",@"** 3"); //==> see _registerSnapshot:forSourceGlobalID:relationshipName:editingContext: for (i = 0; i < count; i++) [_database recordSnapshot: [toManySnapshotArray objectAtIndex: i] forSourceGlobalID: [editingContext globalIDForObject: [objects objectAtIndex: i]] relationshipName: [relationship name]]; EOFLOGObjectLevel(@"EODatabaseContext", @"** 4"); } @end @implementation EODatabaseContext (EODatabaseContextPrivate) - (void) _fireArrayFault: (id)object { //OK ?? BOOL fetchIt = YES; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p", object); if (_delegateRespondsTo.shouldFetchObjectFault == YES) fetchIt = [_delegate databaseContext: self shouldFetchObjectFault: object]; if (fetchIt) { /*Class targetClass = Nil; void *extraData = NULL;*/ EOAccessArrayFaultHandler *handler = (EOAccessArrayFaultHandler *)[EOFault handlerForFault:object]; EOEditingContext *context = [handler editingContext]; NSString *relationshipName= [handler relationshipName]; EOKeyGlobalID *gid = [handler sourceGlobalID]; NSArray *objects = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipName=%@", relationshipName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); objects = [context objectsForSourceGlobalID: gid relationshipName: relationshipName editingContext: context]; [EOFault clearFault: object]; //?? /* in clearFault [handler faultWillFire:object]; targetClass=[handler targetClass]; extraData=[handler extraData]; RELEASE(handler); */ EOFLOGObjectLevelArgs(@"EODatabaseContext", @"NEAR FINISHED 1 object count=%d %p %@", [object count], object, object); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"NEAR FINISHED 1 objects count=%d %p %@", [objects count], objects, objects); if (objects != object) { //No, not needed [object removeObjectsInArray:objects];//Because some objects may be here. We don't want duplicate. It's a hack because I don't see why there's objects in object ! EOFLOGObjectLevelArgs(@"EODatabaseContext", @"NEAR FINISHED 1 object count=%d %p %@", [object count], object, object); [object addObjectsFromArray: objects]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"NEAR FINISHED 2 object count=%d %@", [object count], object); } } //END! /* } - (void)_batchToMany:(id)fault withHandler:(EOAccessArrayFaultHandler *)handler { */ /* EOAccessArrayFaultHandler *usedHandler, *firstHandler, *lastHandler; EOAccessArrayFaultHandler *bufHandler; NSMutableDictionary *batchBuffer; EOEditingContext *context; NSMutableArray *objects; EOKeyGlobalID *gid; EOEntity *entity; EORelationship *relationship; unsigned int maxBatch; BOOL batch = YES, changeBatch = NO; gid = [handler sourceGlobalID];//OK context = [handler editingContext];//OK entity = [_database entityNamed:[gid entityName]];//-done relationship = [entity relationshipNamed:[handler relationshipName]];//-done maxBatch = [relationship numberOfToManyFaultsToBatchFetch];//-done batchBuffer = [_batchToManyFaultBuffer objectForKey:[entity name]]; bufHandler = [batchBuffer objectForKey:[relationship name]]; objects = [NSMutableArray array]; [objects addObject:[context objectForGlobalID:gid]];//-done firstHandler = lastHandler = nil; usedHandler = handler; if (bufHandler && [bufHandler isEqual:usedHandler] == YES) changeBatch = YES; if (maxBatch > 1) { maxBatch--; while (maxBatch--) { if (lastHandler == nil) { usedHandler = (EOAccessArrayFaultHandler *)[usedHandler previous]; if (usedHandler) firstHandler = usedHandler; else lastHandler = usedHandler = (EOAccessArrayFaultHandler *) [handler next]; } else { usedHandler = (EOAccessArrayFaultHandler *)[lastHandler next]; if (usedHandler) lastHandler = usedHandler; } if (usedHandler == nil) break; if (bufHandler && [bufHandler isEqual:usedHandler] == YES) changeBatch = YES; [objects addObject:[context objectForGlobalID:[usedHandler sourceGlobalID]]]; } } if (firstHandler == nil) firstHandler = handler; if (lastHandler == nil) lastHandler = handler; usedHandler = (id)[firstHandler previous]; bufHandler = (id)[lastHandler next]; if (usedHandler) [usedHandler _linkNext:bufHandler]; usedHandler = bufHandler; if (usedHandler) { [usedHandler _linkPrev:[firstHandler previous]]; if (bufHandler == nil) bufHandler = usedHandler; } if (changeBatch == YES) { if (bufHandler) [batchBuffer setObject:bufHandler forKey:[relationship name]]; else [batchBuffer removeObjectForKey:[relationship name]]; } [self batchFetchRelationship:relationship forSourceObjects:objects editingContext:context]; */ EOFLOGObjectFnStop(); } - (void) _fireFault: (id)object { //TODO BOOL fetchIt = YES;//MIRKO EOFLOGObjectFnStart(); //MIRKO EOFLOGObjectLevelArgs(@"EODatabaseContext",@"Fire Fault: object %p of class %@", object,[object class]); if (_delegateRespondsTo.shouldFetchObjectFault == YES) fetchIt = [_delegate databaseContext: self shouldFetchObjectFault: object]; if (fetchIt) { EOAccessFaultHandler *handler; EOEditingContext *context; EOGlobalID *gid; NSDictionary *snapshot; EOEntity *entity = nil; NSString *entityName = nil; handler = (EOAccessFaultHandler *)[EOFault handlerForFault: object]; context = [handler editingContext]; gid = [handler globalID]; snapshot = [self snapshotForGlobalID: gid]; //nil if (snapshot) { //TODO _fireFault snapshot NSEmitTODO(); // [self notImplemented: _cmd]; //TODO } entity = [self entityForGlobalID: gid]; entityName = [entity name]; if ([entity cachesObjects]) { //TODO _fireFault [entity cachesObjects] NSEmitTODO(); [self notImplemented: _cmd]; //TODO } //??? generation # EOAccessGenericFaultHandler//ret 2 { EOAccessFaultHandler *previousHandler; EOAccessFaultHandler *nextHandler; EOFetchSpecification *fetchSpecif; NSArray *objects; EOQualifier *qualifier; /*int maxNumberOfInstancesToBatchFetch = [entity maxNumberOfInstancesToBatchFetch]; NSDictionary *snapshot = [self snapshotForGlobalID: gid];*///nil //TODO use it ! NSDictionary *pk = [entity primaryKeyForGlobalID: (EOKeyGlobalID *)gid]; EOQualifier *pkQualifier = [entity qualifierForPrimaryKey: pk]; NSMutableArray *qualifiers = [NSMutableArray array]; [qualifiers addObject: pkQualifier]; previousHandler = (EOAccessFaultHandler *)[handler previous]; nextHandler = (EOAccessFaultHandler *)[handler next]; //nil fetchSpecif = AUTORELEASE([EOFetchSpecification new]); [fetchSpecif setEntityName: entityName]; qualifier = [EOOrQualifier qualifierWithQualifierArray: qualifiers]; [fetchSpecif setQualifier: qualifier]; objects = [self objectsWithFetchSpecification: fetchSpecif editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"objects %p=%@ class=%@", objects, objects, [objects class]); } } EOFLOGObjectFnStop(); } /* - (void)_batchToOne:(id)fault withHandler:(EOAccessFaultHandler *)handler { EOAccessFaultHandler *usedHandler, *firstHandler, *lastHandler; EOAccessFaultHandler *bufHandler; EOFetchSpecification *fetch; EOEditingContext *context; EOKeyGlobalID *gid; EOQualifier *qualifier; EOEntity *entity; NSMutableArray *qualifierArray; BOOL batch = YES, changeBatch = NO; unsigned int maxBatch; if (_delegateRespondsTo.shouldFetchObjectFault == YES)//-done batch = [_delegate databaseContext:self shouldFetchObjectFault:fault];//-done if (batch == NO)//-done return;//-done gid = [handler globalID];//-done context = [handler editingContext];//-done entity = [_database entityNamed:[gid entityName]];//-done maxBatch = [entity maxNumberOfInstancesToBatchFetch];//-done bufHandler = [_batchFaultBuffer objectForKey:[entity name]]; firstHandler = lastHandler = nil; usedHandler = handler; if (bufHandler && [bufHandler isEqual:usedHandler] == YES) changeBatch = YES; if (maxBatch <= 1) { qualifier = [entity qualifierForPrimaryKey: [entity primaryKeyForGlobalID:gid]]; } else { qualifierArray = [NSMutableArray array]; [qualifierArray addObject: [entity qualifierForPrimaryKey: [entity primaryKeyForGlobalID:gid]]]; maxBatch--; while (maxBatch--) { if (lastHandler == nil) { usedHandler = (EOAccessFaultHandler *)[usedHandler previous]; if (usedHandler) firstHandler = usedHandler; else lastHandler = usedHandler = (EOAccessFaultHandler *)[handler next]; } else { usedHandler = (EOAccessFaultHandler *)[lastHandler next]; if (usedHandler) lastHandler = usedHandler; } if (usedHandler == nil) break; if (changeBatch == NO && bufHandler && [bufHandler isEqual:usedHandler] == YES) changeBatch = YES; [qualifierArray addObject: [entity qualifierForPrimaryKey: [entity primaryKeyForGlobalID: [usedHandler globalID]]]]; } qualifier = AUTORELEASE([[EOOrQualifier alloc] initWithQualifierArray:qualifierArray]); } if (firstHandler == nil) firstHandler = handler; if (lastHandler == nil) lastHandler = handler; usedHandler = (id)[firstHandler previous]; bufHandler = (id)[lastHandler next]; if (usedHandler) [usedHandler _linkNext:bufHandler]; usedHandler = bufHandler; if (usedHandler) { [usedHandler _linkPrev:[firstHandler previous]]; if (bufHandler == nil) bufHandler = usedHandler; } if (changeBatch == YES) { if (bufHandler) [_batchFaultBuffer setObject:bufHandler forKey:[entity name]]; else [_batchFaultBuffer removeObjectForKey:[entity name]]; } fetch = [EOFetchSpecification fetchSpecificationWithEntityName:[entity name] qualifier:qualifier sortOrderings:nil];//-done [context objectsWithFetchSpecification:fetch];//-done }*/ // Clear all the faults for the relationship pointed by the source objects and // make sure to perform only a single, efficient, fetch (two fetches if the // relationship is many to many). - (void)_addBatchForGlobalID: (EOKeyGlobalID *)globalID fault: (EOFault *)fault { EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"globalID=%@", globalID); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"fault=%@", fault); if (fault) { EOAccessGenericFaultHandler *handler = nil; NSString *entityName = [globalID entityName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entityName=%@", entityName); handler = [_batchFaultBuffer objectForKey: entityName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"handler=%@", handler); if (handler) { [(EOAccessGenericFaultHandler *) [EOFault handlerForFault: fault] linkAfter: handler usingGeneration: [handler generation]]; } else { handler = (EOAccessGenericFaultHandler *)[EOFault handlerForFault: fault]; NSAssert1(handler, @"No handler for fault:%@", fault); [_batchFaultBuffer setObject: handler forKey: entityName]; } } EOFLOGObjectFnStop(); } - (void)_removeBatchForGlobalID: (EOKeyGlobalID *)globalID fault: (EOFault *)fault { EOAccessGenericFaultHandler *handler, *prevHandler, *nextHandler; NSString *entityName = [globalID entityName]; handler = (EOAccessGenericFaultHandler *)[EOFault handlerForFault: fault]; prevHandler = [handler previous]; nextHandler = [handler next]; if (prevHandler) [prevHandler _linkNext: nextHandler]; if (nextHandler) [nextHandler _linkPrev: prevHandler]; if ([_batchFaultBuffer objectForKey: entityName] == handler) { if (prevHandler) [_batchFaultBuffer setObject: prevHandler forKey: entityName]; else if (nextHandler) [_batchFaultBuffer setObject: nextHandler forKey: entityName]; else [_batchFaultBuffer removeObjectForKey: entityName]; } } - (void)_addToManyBatchForSourceGlobalID: (EOKeyGlobalID *)globalID relationshipName: (NSString *)relationshipName fault: (EOFault *)fault { if (fault) { NSMutableDictionary *buf; EOAccessGenericFaultHandler *handler; NSString *entityName = [globalID entityName]; buf = [_batchToManyFaultBuffer objectForKey: entityName]; if (buf == nil) { buf = [NSMutableDictionary dictionaryWithCapacity: 8]; [_batchToManyFaultBuffer setObject: buf forKey: entityName]; } handler = [buf objectForKey: relationshipName]; if (handler) { [(EOAccessGenericFaultHandler *) [EOFault handlerForFault: fault] linkAfter: handler usingGeneration: [handler generation]]; } else [buf setObject: [EOFault handlerForFault: fault] forKey: relationshipName]; } } @end @implementation EODatabaseContext (EODatabaseSnapshotting) - (void)recordSnapshot: (NSDictionary *)snapshot forGlobalID: (EOGlobalID *)gid { EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p database=%p", self, _database); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); if ([_uniqueStack count] > 0) { NSMutableDictionary *snapshots = [_uniqueStack lastObject]; [snapshots setObject: snapshot forKey: gid]; } else { NSEmitTODO(); NSWarnLog(@"_uniqueStack is empty. May be there's no runing transaction !", ""); [self notImplemented: _cmd]; //TODO } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); EOFLOGObjectFnStop(); } - (NSDictionary *)snapshotForGlobalID: (EOGlobalID *)gid after: (NSTimeInterval)ti { [self notImplemented: _cmd]; return nil; } - (NSDictionary *)snapshotForGlobalID: (EOGlobalID *)gid { //OK NSDictionary *snapshot = nil; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p database=%p", self, _database); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid %p=%@", gid, gid); snapshot = [self localSnapshotForGlobalID: gid]; if (!snapshot) { NSAssert(_database, @"No database"); snapshot = [_database snapshotForGlobalID: gid]; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot for gid %@: %p %@", gid, snapshot, snapshot); EOFLOGObjectFnStop(); return snapshot; } - (void)recordSnapshot: (NSArray *)gids forSourceGlobalID: (EOGlobalID *)gid relationshipName: (NSString *)name { EOFLOGObjectFnStart(); NSEmitTODO(); [self notImplemented: _cmd]; //TODO /* NSMutableDictionary *toMany = [_toManySnapshots objectForKey:gid]; if (toMany == nil) { toMany = [NSMutableDictionary dictionaryWithCapacity:16]; [_toManySnapshots setObject:toMany forKey:gid]; } [toMany setObject:gids forKey:name]; */ EOFLOGObjectFnStop(); } - (NSArray *)snapshotForSourceGlobalID: (EOGlobalID *)gid relationshipName: (NSString *)name { NSArray *snapshot = nil; EOFLOGObjectFnStart(); NSEmitTODO(); [self notImplemented: _cmd]; //TODO /* snapshot = [[_toManySnapshots objectForKey:gid] objectForKey:name]; if (!snapshot) snapshot=[_database snapshotForSourceGlobalID:gid relationshipName:name]; */ EOFLOGObjectFnStop(); return snapshot; } - (NSDictionary *)localSnapshotForGlobalID: (EOGlobalID *)gid { //OK NSDictionary *snapshot = nil; int i, snapshotsDictCount = 0; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p database=%p", self, _database); snapshotsDictCount = [_uniqueStack count]; for (i = 0; !snapshot && i < snapshotsDictCount; i++) { NSDictionary *snapshots = [_uniqueStack objectAtIndex: i]; snapshot = [snapshots objectForKey: gid]; } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot for gid %@: %p %@", gid, snapshot, snapshot); EOFLOGObjectFnStop(); return snapshot; } - (NSArray *)localSnapshotForSourceGlobalID: (EOGlobalID *)gid relationshipName: (NSString *)name { NSArray *snapshot = nil; //TODO EOFLOGObjectFnStart(); NSEmitTODO(); [self notImplemented: _cmd]; //TODO /* return [[_toManySnapshots objectForKey:gid] objectForKey:name]; */ EOFLOGObjectFnStop(); return snapshot; } - (void)forgetSnapshotForGlobalID: (EOGlobalID *)gid { //TODO-VERIFY deleteStack EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p database=%p [_uniqueStack count]=%d", self, _database,[_uniqueStack count]); if ([_uniqueStack count] > 0) { NSMutableDictionary *snapshots = [_uniqueStack lastObject]; //call _deleteStack lastObject [snapshots removeObjectForKey: gid]; snapshots = [_uniqueArrayStack lastObject]; [snapshots removeObjectForKey: gid]; } [_database forgetSnapshotForGlobalID: gid]; //MG Really here ? EOFLOGObjectFnStop(); } - (void)forgetSnapshotsForGlobalIDs: (NSArray *)gids { //TODO NSEmitTODO(); [self notImplemented: _cmd]; //TODO /* int i, count; count = [gids count]; for (i=0; i 0) { NSMutableDictionary *toManySnapshots = [_uniqueArrayStack lastObject]; NSArray *keys = [snapshots allKeys]; int i, count = [keys count]; for (i = 0; i < count; i++) { id key = [keys objectAtIndex: i]; NSDictionary *snapshotsDict = [snapshots objectForKey: key]; NSMutableDictionary *currentSnapshotsDict = [toManySnapshots objectForKey: key]; if (!currentSnapshotsDict) { currentSnapshotsDict = (NSMutableDictionary *)[NSMutableDictionary dictionary]; [toManySnapshots setObject: currentSnapshotsDict forKey: key]; } [currentSnapshotsDict addEntriesFromDictionary: snapshotsDict]; } } EOFLOGObjectFnStop(); } - (void)registerLockedObjectWithGlobalID: (EOGlobalID *)globalID { EOFLOGObjectFnStart(); if (_numLocked && (_numLocked+1) % _LOCK_BUFFER == 0) _lockedObjects = NSZoneRealloc(NULL, _lockedObjects, (_numLocked+_LOCK_BUFFER)*sizeof(id)); _lockedObjects[_numLocked++] = globalID; EOFLOGObjectFnStop(); } - (BOOL)isObjectLockedWithGlobalID: (EOGlobalID *)globalID { int i; EOFLOGObjectFnStart(); for (i = 0; i < _numLocked; i++) if ([_lockedObjects[i] isEqual: globalID]) return YES; EOFLOGObjectFnStop(); return NO; } - (void)initializeObject: (id)object row: (NSDictionary*)row entity: (EOEntity*)entity editingContext: (EOEditingContext*)context { //really near ok NSArray *relationships = nil; NSArray *classPropertyAttributeNames = nil; EONull *null = [EONull null]; int i, count = 0; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=0x%x", object); classPropertyAttributeNames = [entity classPropertyAttributeNames]; count = [classPropertyAttributeNames count]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"count=%d", count); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"row=%@", row); for (i = 0; i < count; i++) { id key = [classPropertyAttributeNames objectAtIndex: i]; id value = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"key=%@", key); value = [row objectForKey: key]; if (value == null) value = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"value=%@", value); [object takeStoredValue: value forKey: key]; } relationships = [entity _relationshipsToFaultForRow: row]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationships=%@", relationships); count = [relationships count]; for (i = 0; i < count; i++) { id relObject = nil; EORelationship *relationship = [relationships objectAtIndex: i]; NSString *relName = [relationship name]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); if ([relationship isToMany]) { EOGlobalID *gid = [entity globalIDForRow: row]; relObject = [self arrayFaultWithSourceGlobalID: gid relationshipName: [relationship name] editingContext: context]; } else if ([relationship isFlattened]) { // to one flattened relationship like aRelationship.anotherRelationship... // I don't know how to handle this case.... May be we shouldn't treat this as real property ?? NSEmitTODO(); relObject = nil; } else { EOMutableKnownKeyDictionary *foreignKeyForSourceRow = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@ foreignKeyInDestination:%d", [relationship name],[relationship foreignKeyInDestination]); foreignKeyForSourceRow = [relationship _foreignKeyForSourceRow: row]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"row=%@\nforeignKeyForSourceRow:%@", row,foreignKeyForSourceRow); if (![foreignKeyForSourceRow containsObjectsNotIdenticalTo: [EONull null]]) { NSEmitTODO();//TODO: what to do if rel is mandatory ? relObject = nil; } else { EOEntity *destinationEntity = [relationship destinationEntity]; EOGlobalID *relRowGid = [destinationEntity globalIDForRow: foreignKeyForSourceRow]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relRowGid=%@", relRowGid); if ([(EOKeyGlobalID*)relRowGid areKeysAllNulls]) NSWarnLog(@"All key of relRowGid %p (%@) are nulls", relRowGid, relRowGid); relObject = [context faultForGlobalID: relRowGid editingContext: context]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relObject=%p (%@)", relObject, [relObject class]); //end /* NSArray *joins = [(EORelationship *)prop joins]; EOJoin *join; NSMutableDictionary *row; EOGlobalID *faultGID; int h, count; id value, realValue = nil; row = [NSMutableDictionary dictionaryWithCapacity:4]; count = [joins count]; for (h=0; h 0) { NSMutableDictionary *snapshotsDict = AUTORELEASE(RETAIN([_uniqueStack lastObject])); NSMutableDictionary *toManySnapshotsDict = AUTORELEASE(RETAIN([_uniqueArrayStack lastObject])); /*NSMutableDictionary *deleteSnapshotsDict = AUTORELEASE(RETAIN([_deleteStack lastObject]));*/ //?? [_uniqueStack removeLastObject]; [_uniqueArrayStack removeLastObject]; [_deleteStack removeLastObject]; [self forgetAllLocks]; [_database recordSnapshots: snapshotsDict]; [_database recordToManySnapshots: toManySnapshotsDict]; /* //TODO if (moified ojects) call forgetSnapshotForGlobalID: ... <<<< DESTROY(_modifiedObjects); */ } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); EOFLOGObjectFnStop(); } - (void) _beginTransaction { EOFLOGObjectFnStart(); [_uniqueStack addObject: [NSMutableDictionary dictionary]]; [_uniqueArrayStack addObject: [NSMutableDictionary dictionary]]; [_deleteStack addObject: [NSMutableDictionary dictionary]]; //TODO: put an object in the dictionary EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@", self, _uniqueStack, _uniqueStack); EOFLOGObjectFnStop(); } - (EODatabaseChannel*) _obtainOpenChannel { EODatabaseChannel *channel = [self availableChannel]; if (![self _openChannelWithLoginPanel: channel]) { NSEmitTODO(); [self notImplemented: _cmd];//TODO } return channel; } - (BOOL) _openChannelWithLoginPanel: (EODatabaseChannel*)databaseChannel { // veridy: LoginPanel ??? EOAdaptorChannel *adaptorChannel = [databaseChannel adaptorChannel]; if (![adaptorChannel isOpen]) //?? { [adaptorChannel openChannel]; } return [adaptorChannel isOpen]; } - (void) _forceDisconnect { // TODO NSEmitTODO(); [self notImplemented: _cmd]; } @end @implementation EODatabaseContext(EOMultiThreaded) - (void)lock { [_lock lock]; } - (void)unlock { [_lock unlock]; } @end @implementation EODatabaseContext (EODatabaseContextPrivate2) - (void) _verifyNoChangesToReadonlyEntity: (EODatabaseOperation*)dbOpe { //TODO EOEntity *entity = nil; EOFLOGObjectFnStart(); entity = [dbOpe entity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe); if ([entity isReadOnly]) { //?? exception I presume } else { [dbOpe databaseOperator]; //SoWhat } EOFLOGObjectFnStop(); } - (void) _cleanUpAfterSave { //TODO EOFLOGObjectFnStart(); _coordinator = nil; //realesae ? _editingContext = nil; //realesae ? if (_dbOperationsByGlobalID) { //Really free it because we don't want to record some db ope (select for exemple). NSFreeMapTable(_dbOperationsByGlobalID); _dbOperationsByGlobalID = NULL; } _flags.preparingForSave = NO; /* //TODO HERE or in _commitTransaction ? _numLocked = 0; _lockedObjects = NSZoneRealloc(NULL, _lockedObjects, _LOCK_BUFFER*sizeof(id)); */ EOFLOGObjectFnStop(); } - (EOGlobalID*)_globalIDForObject: (id)object { EOEditingContext *objectEditingContext = nil; EOGlobalID *gid = nil; EOFLOGObjectFnStart(); NSAssert(object, @"No object"); EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p of class %@", object,[object class]); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_editingContext=%p", _editingContext); objectEditingContext = [object editingContext]; NSAssert2(objectEditingContext, @"No editing context for object %p: %@", object,object); gid = [objectEditingContext globalIDForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); if (!gid) { NSEmitTODO(); NSLog(@"TODO: no GID in EODatabaseContext _globalIDForObject:"); //TODO exception ? ==> RollbackCh } EOFLOGObjectFnStop(); return gid; } - (NSDictionary*)_primaryKeyForObject: (id)object { //Ayers: Review return [self _primaryKeyForObject: object raiseException: YES]; } - (NSDictionary*)_primaryKeyForObject: (id)object raiseException: (BOOL)raiseException { //NEAR OK //Ayers: Review NSDictionary *pk = nil; EOEntity *entity = nil; NSArray *pkNames = nil; BOOL shouldGeneratePrimaryKey = NO; EOFLOGObjectFnStart(); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@", object); NSAssert(!isNilOrEONull(object), @"No object"); entity = [_database entityForObject: object]; shouldGeneratePrimaryKey = [self _shouldGeneratePrimaryKeyForEntityName: [entity name]]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p shouldGeneratePrimaryKey=%d", object,shouldGeneratePrimaryKey); /* if (shouldGeneratePrimaryKey) */ { BOOL isPKValid = NO; EOGlobalID *gid = [self _globalIDForObject: object]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid); pk = [entity primaryKeyForGlobalID: (EOKeyGlobalID*)gid]; //OK EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p pk=%@", object,pk); { NSDictionary *pk2 = nil; pkNames = [entity primaryKeyAttributeNames]; pk2 = [self valuesForKeys: pkNames object: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p pk2=%@", object,pk2); if (pk) { //merge pk2 into pk NSEnumerator *pk2Enum = [pk2 keyEnumerator]; NSMutableDictionary *realPK = [NSMutableDictionary dictionaryWithDictionary: pk];//revoir id key = nil; while ((key = [pk2Enum nextObject])) { [realPK setObject: [pk2 objectForKey: key] forKey: key]; } pk = realPK; } else pk=pk2; } EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p pk=%@", object,pk); isPKValid = [entity isPrimaryKeyValidInObject: pk]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p isPKValid=%d", object,isPKValid); if (isPKValid == NO) pk = nil; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p isPKValid=%d shouldGeneratePrimaryKey=%d", object,isPKValid,shouldGeneratePrimaryKey); if (isPKValid == NO && shouldGeneratePrimaryKey) { pk = nil; if (_delegateRespondsTo.newPrimaryKey == YES) pk = [_delegate databaseContext: self newPrimaryKeyForObject: object entity: entity]; if (!pk) { NSArray *pkAttributes = nil; EOAdaptorChannel *channel = nil; EOStoredProcedure *nextPKProcedure = nil; nextPKProcedure = [entity storedProcedureForOperation: EONextPrimaryKeyProcedureOperation]; #if 0 // TODO execute storedProcedure and fetch results; if (nextPKProcedure) [[[self _obtainOpenChannel] adaptorChannel] executeStoredProcedure: nextPKProcedure withValues:]; #endif pkAttributes = [entity primaryKeyAttributes]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p pk=%@ [pkAttributes count]=%d", object,pk,[pkAttributes count]); if (pk == nil && [pkAttributes count] == 1) { EOAttribute *pkAttr = [pkAttributes objectAtIndex: 0]; //TODO attr: adaptorValueType //returned EOAdaptorNumberType //TODO [entity rootParent];//so what if (channel == nil) { channel = [[self _obtainOpenChannel] adaptorChannel]; if ([[channel adaptorContext] transactionNestingLevel] == 0) [[channel adaptorContext] beginTransaction]; if (_flags.beganTransaction == NO) { EOFLOGObjectLevel(@"EODatabaseContext", @"BEGAN TRANSACTION FLAG==>NO"); _flags.beganTransaction = YES; } } pk = [channel primaryKeyForNewRowWithEntity: entity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"** prepare pk %@", pk); if (pk == nil && [[pkAttr valueClassName] isEqual:@"NSData"] == YES) { unsigned char data[EOUniqueBinaryKeyLength]; [EOTemporaryGlobalID assignGloballyUniqueBytes: data]; pk = [NSDictionary dictionaryWithObject: [NSData dataWithBytes: data length: EOUniqueBinaryKeyLength] forKey: [pkAttr name]]; }; } } if (!pk && raiseException) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: cannot generate primary key for object '%@'", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, object]; } } /* else // !shouldGeneratePrimaryKey */ if (!pk) { // MG // Here we'll find if there is a "parent" objects which relay pk // I'm not sure if we could do it here but it's handle this case: // object is a new child of a previous existing object. // if we don't generate it's pk here, the chld object // will relay it's pk to parent which is not good NSDictionary *objectSnapshot=nil; NSArray *relationships=nil; int i=0; int relationshipsCount=0; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@", object); // get object snapshot objectSnapshot = [object snapshot]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"objectSnapshot=%@", objectSnapshot); // Get object relationships relationships = [entity relationships]; EOFLOGObjectLevelArgs(@"EODatabaseContext",@"relationships=%@", relationships); relationshipsCount = [relationships count]; for (i = 0; i < relationshipsCount; i++) { EORelationship *inverseRelationship = nil; EORelationship *relationship = [relationships objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@", relationship); inverseRelationship = [relationship inverseRelationship]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"inverseRelationship=%@", inverseRelationship); // if there's inverse relationship with propagates primary key if ([inverseRelationship propagatesPrimaryKey]) { NSString *relationshipName= [relationship name]; NSDictionary *relationshipValuePK = nil; // get object value for the relationship id relationshipValue = [objectSnapshot valueForKey:relationshipName]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity name=%@ relationship name=%@ Value=%@", [entity name], relationshipName, relationshipValue); // get relationshipValue pk NSAssert2(!isNilOrEONull(relationshipValue), @"No relationshipValue for relationship %@ in objectSnapshot %@ ", relationshipName, objectSnapshot); relationshipValuePK = [self _primaryKeyForObject: relationshipValue]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationshipValuePK=%@", relationshipValuePK); // force object to relay pk now ! [self relayPrimaryKey: relationshipValuePK object: relationshipValue entity: [_database entityForObject: relationshipValue]]; }; }; pk = [self valuesForKeys: pkNames object: object]; if (![entity isPrimaryKeyValidInObject: pk]) pk=nil; }; if (pk) { EODatabaseOperation *dbOpe = [self databaseOperationForObject: object]; NSMutableDictionary *newRow = [dbOpe newRow]; [newRow addEntriesFromDictionary: pk];// VERIFY Here we replace // previous key } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"pk=%@", pk); NSDebugMLog(@"object %p=%@\npk=%@",object, object, pk); EOFLOGObjectFnStop(); return pk; } - (BOOL) _shouldGeneratePrimaryKeyForEntityName: (NSString*)entityName { //OK BOOL shouldGeneratePK = YES; EOFLOGObjectFnStart(); if (_nonPrimaryKeyGenerators) shouldGeneratePK = !NSHashGet(_nonPrimaryKeyGenerators, entityName); EOFLOGObjectLevelArgs(@"EODatabaseContext", @"shouldGeneratePK for %@: %s", entityName, (shouldGeneratePK ? "YES" : "NO")); NSAssert(![entityName isEqualToString: @"Country"] || shouldGeneratePK, @"MGVALID: Failed"); EOFLOGObjectFnStop(); return shouldGeneratePK; } - (void)_buildPrimaryKeyGeneratorListForEditingContext: (EOEditingContext*)context { NSArray *objects[3]; NSHashTable *processedEntities = NULL; NSMutableArray *entityToProcess = nil; int i, which; EOFLOGObjectFnStart(); if (_nonPrimaryKeyGenerators) { NSResetHashTable(_nonPrimaryKeyGenerators); } processedEntities = NSCreateHashTable(NSObjectHashCallBacks, 32); objects[0] = [context updatedObjects]; objects[1] = [context insertedObjects]; objects[2] = [context deletedObjects]; for (which = 0; which < 3; which++) { int count = [objects[which] count]; for (i = 0; i < count; i++) { id object = [objects[which] objectAtIndex: i]; EOEntity *entity = [_database entityForObject: object]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"add entity to process: %@", [entity name]); if (entityToProcess) [entityToProcess addObject: entity]; else entityToProcess = [NSMutableArray arrayWithObject: entity]; } } while ([entityToProcess count]) { EOEntity *entity = [entityToProcess lastObject]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"test entity: %@", [entity name]); [entityToProcess removeLastObject]; if (!NSHashInsertIfAbsent(processedEntities, entity)) //Already processed ? { NSArray *relationships = nil; int iRelationship = 0; int relationshipsCount = 0; relationships = [entity relationships]; iRelationship = 0; relationshipsCount = [relationships count]; for (iRelationship = 0; iRelationship < relationshipsCount; iRelationship++) { EORelationship *relationship = [relationships objectAtIndex: iRelationship]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"test entity: %@ relationship=%@", [entity name], relationship); if ([relationship propagatesPrimaryKey]) { EOEntity *destinationEntity = [relationship destinationEntity]; EOFLOGObjectLevelArgs(@"EODatabaseContext", @"test entity: %@ destinationEntity=%@", [entity name], [destinationEntity name]); if (destinationEntity) { NSArray *destAttrs; NSArray *pkAttrs; int i, count; BOOL destPK = NO; destAttrs = [relationship destinationAttributes]; pkAttrs = [destinationEntity primaryKeyAttributes]; count = [destAttrs count]; for (i = 0; i < count; i++) { if ([pkAttrs containsObject: [destAttrs objectAtIndex: i]]) destPK = YES; } if (destPK) { EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destination entity: %@ No PrimaryKey Generation [Relationship = %@]", [destinationEntity name], [relationship name]); if (!_nonPrimaryKeyGenerators) _nonPrimaryKeyGenerators = NSCreateHashTable(NSObjectHashCallBacks, 32); NSHashInsertIfAbsent(_nonPrimaryKeyGenerators, [destinationEntity name]); [entityToProcess addObject: destinationEntity]; } } } } } } EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_nonPrimaryKeyGenerators=%@", NSStringFromHashTable(_nonPrimaryKeyGenerators)); EOFLOGObjectFnStop(); NSFreeHashTable(processedEntities); } /** Returns a dictionary containing a snapshot of object that reflects its committed values (last values putted in the database; i.e. values before changes were made on the object). It is updated after commiting new values. If the object has been just inserted, the dictionary is empty. **/ - (NSDictionary*)_currentCommittedSnapshotForObject: (id)object { NSDictionary *snapshot = nil; EOGlobalID *gid = nil; EODatabaseOperation *dbOpe = nil; EODatabaseOperator dbOperator = (EODatabaseOperator)0; EOFLOGObjectFnStart(); gid = [_editingContext globalIDForObject: object]; dbOpe = [self databaseOperationForGlobalID: gid]; //I'm not sure. Retrieve it directly ? dbOperator = [dbOpe databaseOperator]; switch (dbOperator) { case EODatabaseUpdateOperator: snapshot = [_editingContext committedSnapshotForObject: object];//OK break; case EODatabaseInsertOperator: snapshot = [NSDictionary dictionary]; break; //TODO /* else snapshot=XX;//TODO */ case EODatabaseDeleteOperator: break; case EODatabaseNothingOperator: break; } EOFLOGObjectFnStop(); return snapshot; } - (void) _assertValidStateWithSelector: (SEL)sel { // [self notImplemented:_cmd]; //TODO } - (id) _addDatabaseContextStateToException: (id)param0 { NSEmitTODO(); return [self notImplemented: _cmd]; //TODO } - (id) _databaseContextState { NSEmitTODO(); return [self notImplemented: _cmd]; //TODO } @end