libs-gdl2/EOAccess/EODatabaseContext.m
David Ayers a601888682 * *.[hm] Use GNUSTEP in favor of NeXT_Foundation_LIBRARY
as requested by Marcus Mueller <znek@mulle-kybernetik.com>.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@18856 72102866-910b-0410-8b05-ffd578937521
2004-03-19 16:41:21 +00:00

7123 lines
213 KiB
Objective-C

/**
EODatabaseContext.m <title>EODatabaseContext Class</title>
Copyright (C) 2000-2003 Free Software Foundation, Inc.
Author: Mirko Viviani <mirko.viviani@rccr.cremona.it>
Date: June 2000
Author: Manuel Guesdon <mguesdon@orange-concept.com>
Date: October 2000
$Revision$
$Date$
<abstract></abstract>
This file is part of the GNUstep Database Library.
<license>
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.
</license>
**/
#include "config.h"
RCS_ID("$Id$")
#ifdef GNUSTEP
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSException.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSZone.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSData.h>
#include <Foundation/NSKeyValueCoding.h>
#include <Foundation/NSDebug.h>
#else
#include <Foundation/Foundation.h>
#endif
#ifndef GNUSTEP
#include <GNUstepBase/GNUstep.h>
#include <GNUstepBase/GSCategories.h>
#endif
#include <GNUstepBase/GSObjCRuntime.h>
#include <EOControl/EOFault.h>
#include <EOControl/EOEditingContext.h>
#include <EOControl/EOClassDescription.h>
#include <EOControl/EOGenericRecord.h>
#include <EOControl/EOQualifier.h>
#include <EOControl/EOKeyGlobalID.h>
#include <EOControl/EOFetchSpecification.h>
#include <EOControl/EOSortOrdering.h>
#include <EOControl/EOKeyValueCoding.h>
#include <EOControl/EOMutableKnownKeyDictionary.h>
#include <EOControl/EOCheapArray.h>
#include <EOControl/EONSAddOns.h>
#include <EOControl/EONull.h>
#include <EOControl/EODebug.h>
#include <EOAccess/EOAdaptor.h>
#include <EOAccess/EOAdaptorChannel.h>
#include <EOAccess/EOAdaptorContext.h>
#include <EOAccess/EOModel.h>
#include <EOAccess/EOModelGroup.h>
#include <EOAccess/EOEntity.h>
#include <EOAccess/EORelationship.h>
#include <EOAccess/EOAttribute.h>
#include <EOAccess/EOStoredProcedure.h>
#include <EOAccess/EOJoin.h>
#include <EOAccess/EODatabase.h>
#include <EOAccess/EODatabaseContext.h>
#include <EOAccess/EODatabaseContextPriv.h>
#include <EOAccess/EODatabaseChannel.h>
#include <EOAccess/EODatabaseOperation.h>
#include <EOAccess/EOAccessFault.h>
#include <EOAccess/EOAccessFaultPriv.h>
#include <EOAccess/EOExpressionArray.h>
#include <string.h>
#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<count; h++)
{
join = [joins objectAtIndex:h];
joinName = [[join sourceAttribute] name];
value = [snapshot objectForKey:joinName];
if (value)
[row setObject:value
forKey:joinName];
else
NSLog(@"warning: key name=%@ in entity=%@ not found in object %@", joinName, [entity name], object);
}
if ([self ownsObject:toManyObj] == YES)
{
EODatabaseOperation *dbOp;
dbOp = [self _dbOperationWithGlobalID:gid
object:object
entity:entity
operator:
EODatabaseUpdateOperator];
[[dbOp newRow] addEntriesFromDictionary:row];
}
else
{
[_coordinator
forwardUpdateForObject:toManyObj
changes:row];
}
}
else
[toManySnapshot removeObject:toManyGID];
}
[op recordToManySnapshot:toManyGIDArray
relationshipName:name];
#if 0 // TODO we have to clear foreign keys of a removed object ?
enumerator = [toManySnapshot objectEnumerator];
while ((toManyGID = [enumerator nextObject]))
{
}
#endif
}
else
{
//IN relayAttributesInRelationship:sourceObject:destinationObject:
[[op newRow] addEntriesFromDictionary:row];
}
}
*/
/*
#if 0
countProp = [classProperties count];
for (i=0; i<countProp; i++)
{
NSString *name;
property = [classProperties objectAtIndex:i];
if ([property isKindOfClass:[EOAttribute class]])
{
name = [(EOAttribute *)property name];
if ([property isFlattened] == YES)
{
}
}
}
#endif
*/
}
}
}
}
////FIN
// TODO
/*
NSArray *changedObjects;
NSArray *classProperties;
NSEnumerator *objsEnum, *enumerator;
EOEntity *entity;
EODatabaseOperation *op;
EOGlobalID *gid;
int i, countProp;
id object, property;
NSDictionary *snapshot;
EONull *null = [EONull null];
changedObjects = [_editingContext updatedObjects];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"*r* %@", changedObjects);
objsEnum = [changedObjects objectEnumerator];
while ((object = [objsEnum nextObject]))
{
if ([self ownsObject:object] == YES)
{
entity = [_database entityForObject:object];
gid = [_editingContext globalIDForObject:object];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object = %@", object);
//OK done
op = [self _dbOperationWithGlobalID:gid
object:object
entity:entity
operator:EODatabaseUpdateOperator];
///
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object2");
snapshot = [op newRow];
classProperties = [entity classProperties];
countProp = [classProperties count];
for (i=0; i<countProp; i++)
{
NSString *name;
property = [classProperties objectAtIndex:i];
if ([property isKindOfClass:[EORelationship class]])
{
}
}
}
*/
/*
//OK done
changedObjects = [_editingContext deletedObjects];
objsEnum = [changedObjects objectEnumerator];
while ((object = [objsEnum nextObject]))
{
if ([self ownsObject:object] == YES)
{
entity = [_database entityForObject:object];
gid = [_editingContext globalIDForObject:object];
// Turbocat
if (gid && ([gid isTemporary] == NO)) {
op = [self _dbOperationWithGlobalID:gid
object:object
entity:entity
operator:EODatabaseDeleteOperator];
}
}
}
*/
EOFLOGObjectFnStop();
}
/** Contructs a list of EODatabaseOperations for all changes in the EOEditingContext
that are owned by this context. Forward any relationship changes discovered
but not owned by this context to the coordinator.
**/
- (void)recordUpdateForObject: (id)object
changes: (NSDictionary *)changes
{
//OK
EODatabaseOperation *dbOpe = nil;
EOFLOGObjectFnStart();
NSAssert(object, @"No object");
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@", object);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"changes=%@", changes);
[self _assertValidStateWithSelector:
@selector(recordUpdateForObject:changes:)];
dbOpe = [self databaseOperationForObject: object];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p dbOpe=%@",
object,dbOpe);
[dbOpe setDatabaseOperator:EODatabaseUpdateOperator];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p dbOpe=%@",
object,dbOpe);
if ([changes count])
{
[[dbOpe newRow] addEntriesFromDictionary: changes];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p dbOpe=%@",
object,dbOpe);
}
EOFLOGObjectFnStop();
}
-(void)recordInsertForObject: (id)object
{
NSDictionary *snapshot = nil;
EODatabaseOperation *dbOpe = nil;
EOFLOGObjectFnStart();
dbOpe = [self databaseOperationForObject: object];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p dbOpe=%@",
object,dbOpe);
[dbOpe setDatabaseOperator: EODatabaseInsertOperator];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p dbOpe=%@",
object, dbOpe);
snapshot = [dbOpe dbSnapshot]; //TODO: sowhat
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%p snapshot=%@",
object, snapshot);
//call snapshot count
EOFLOGObjectFnStop();
}
- (void) recordDeleteForObject: (id)object
{
NSDictionary *snapshot = nil;
EODatabaseOperation *dbOpe = nil;
EOFLOGObjectFnStart();
dbOpe = [self databaseOperationForObject: object];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe);
[dbOpe setDatabaseOperator: EODatabaseDeleteOperator];
snapshot = [dbOpe dbSnapshot]; //TODO: sowhat
EOFLOGObjectFnStop();
}
/** Constructs EOAdaptorOperations for all EODatabaseOperations constructed in
during recordChangesInEditingContext and recordUpdateForObject:changes:.
Performs the EOAdaptorOperations on an available adaptor channel.
If the save is OK, updates the snapshots in the EODatabaseContext to reflect
the new state of the server.
Raises an exception is the adaptor is unable to perform the operations.
**/
- (void)performChanges
{
NSMapEnumerator dbOpeEnum;
EOGlobalID *gid = nil;
EODatabaseOperation *dbOpe = nil;
NSArray *orderedAdaptorOperations = nil;
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"self=%p preparingForSave=%d beganTransaction=%d",
self,
(int)_flags.preparingForSave,
(int)_flags.beganTransaction);
[self _assertValidStateWithSelector: @selector(performChanges)];
dbOpeEnum = NSEnumerateMapTable(_dbOperationsByGlobalID);
while (NSNextMapEnumeratorPair(&dbOpeEnum, (void **)&gid, (void **)&dbOpe))
{
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe);
//REVOIR
if ([dbOpe databaseOperator] == EODatabaseNothingOperator)
{
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"Db Ope %@ for Nothing !!!",
dbOpe);
}
else
{
[self _verifyNoChangesToReadonlyEntity: dbOpe];
//MIRKO snapshot = [op dbSnapshot];
[self createAdaptorOperationsForDatabaseOperation: dbOpe];
}
}
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"orderedAdaptorOperations A=%@",
orderedAdaptorOperations);
orderedAdaptorOperations = [self orderAdaptorOperations];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"orderedAdaptorOperations B=%@",
orderedAdaptorOperations);
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"self=%p preparingForSave=%d beganTransaction=%d",
self,
(int)_flags.preparingForSave,
(int)_flags.beganTransaction);
if ([orderedAdaptorOperations count] > 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<count; h++)
{
join = [joins objectAtIndex:h];
value = [objectTo
storedValueForKey:
[[join destinationAttribute] name]];
if (value == nil)
value = null;
[row setObject:value
forKey:[[join sourceAttribute] name]];
}
#if 0
inverse = [property inverseRelationship];
if (inverse)
{
toManySnapshot = AUTORELEASE([[self snapshotForSourceGlobalID:gid
relationshipName:[inverse name]]
mutableCopy]);
if (toManySnapshot == nil)
toManySnapshot = [NSMutableArray array];
[toManySnapshot addObject:gid];
[op recordToManySnapshot:toManySnapshot
relationshipName:name];
}
#endif
}
else
{
keys = [NSMutableArray arrayWithCapacity:count];
for (h=0; h<count; h++)
{
join = [joins objectAtIndex:h];
[keys addObject:[[join destinationAttribute]
name]];
}
values = [_coordinator valuesForKeys:keys
object:objectTo];
for (h=0; h<count; h++)
{
join = [joins objectAtIndex:h];
value = [values
objectForKey:
[[join destinationAttribute] name]];
if (value == nil)
value = null;
[row setObject:value
forKey:[[join sourceAttribute] name]];
}
}
*/
EOFLOGObjectFnStop();
};
- (void)recordDatabaseOperation: (EODatabaseOperation*)databaseOpe
{
//OK
EOGlobalID *gid = nil;
EOFLOGObjectFnStart();
NSAssert(databaseOpe, @"No database operation");
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"databaseOpe=%@", databaseOpe);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_dbOperationsByGlobalID=%p",
_dbOperationsByGlobalID);
if (_dbOperationsByGlobalID)
{
// EOFLOGObjectLevelArgs(@"EODatabaseContext",@"_dbOperationsByGlobalID=%@",NSStringFromMapTable(_dbOperationsByGlobalID));
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
[NSDictionary dictionaryWithObjects:
NSAllMapTableValues(_dbOperationsByGlobalID)
forKeys:
NSAllMapTableKeys(_dbOperationsByGlobalID)]);
/*
// doesn't do this so some db operation are not recorded (when selecting objects)
if (!_dbOperationsByGlobalID)
_dbOperationsByGlobalID = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSObjectMapValueCallBacks,
32);
*/
gid = [databaseOpe globalID];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid);
NSMapInsert(_dbOperationsByGlobalID, gid, databaseOpe);
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"_dbOperationsByGlobalID=%p",
_dbOperationsByGlobalID);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
[NSDictionary dictionaryWithObjects:
NSAllMapTableValues(_dbOperationsByGlobalID)
forKeys:
NSAllMapTableKeys(_dbOperationsByGlobalID)]);
}
else
{
EOFLOGObjectLevel(@"EODatabaseContext",
@"No _dbOperationsByGlobalID");
}
EOFLOGObjectFnStop();
}
- (EODatabaseOperation*)databaseOperationForGlobalID: (EOGlobalID*)gid
{
//OK
EODatabaseOperation *dbOpe = nil;
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_dbOperationsByGlobalID=%p",
_dbOperationsByGlobalID);
if (_dbOperationsByGlobalID)
{
// EOFLOGObjectLevelArgs(@"EODatabaseContext",@"_dbOperationsByGlobalID=%@",NSStringFromMapTable(_dbOperationsByGlobalID));
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"_dbOperationsByGlobalID=%@",
[NSDictionary dictionaryWithObjects:
NSAllMapTableValues(_dbOperationsByGlobalID)
forKeys:
NSAllMapTableKeys(_dbOperationsByGlobalID)]);
dbOpe = (EODatabaseOperation*)NSMapGet(_dbOperationsByGlobalID,
(const void*)gid);
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"dbOpe=%@",
dbOpe);
}
EOFLOGObjectFnStop();
return dbOpe;
}
- (EODatabaseOperation*)databaseOperationForObject: (id)object
{
//OK
EODatabaseOperation *databaseOpe = nil;
EOGlobalID *gid = nil;
EOFLOGObjectFnStart();
NS_DURING // for trace purpose
{
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@", object);
if ([object isKindOfClass: [EOGenericRecord class]])
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dictionary=%@ ",
[object debugDictionaryDescription]);
gid = [self _globalIDForObject: object]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"gid=%@", gid);
databaseOpe = [self databaseOperationForGlobalID: gid]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"databaseOpe=%@",
databaseOpe);
if (!databaseOpe)//OK
{
NSDictionary *snapshot = nil;
NSArray *classPropertyNames = nil;
NSArray *dbSnapshotKeys = nil;
int i = 0;
int propNamesCount = 0;
int snapKeyCount = 0;
NSMutableDictionary *row = nil;
NSMutableDictionary *newRow = nil;
EOEntity *entity = nil;
NSArray *primaryKeyAttributes = nil;
entity = [_database entityForObject: object]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity name=%@",
[entity name]);
primaryKeyAttributes = [entity primaryKeyAttributes]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"primaryKeyAttributes=%@",
primaryKeyAttributes);
databaseOpe = [EODatabaseOperation
databaseOperationWithGlobalID: gid
object: object
entity: entity]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"CREATED databaseOpe=%@\nfor object %p %@",
databaseOpe, object, object);
snapshot = [self snapshotForGlobalID: gid];//OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"snapshot %p=%@",
snapshot, snapshot);
if (!snapshot)
snapshot = [NSDictionary dictionary];
[databaseOpe setDBSnapshot: snapshot];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object=%p databaseOpe=%@",
object,databaseOpe);
classPropertyNames = [entity classPropertyNames]; //OK (code, a3code, numcode, toLabel)
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"classPropertyNames=%@", classPropertyNames);
propNamesCount = [classPropertyNames count];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"propNamesCount=%d",
(int)propNamesCount);
//TODO: rewrite code: don't use temporary "row"
row = (NSMutableDictionary*)[NSMutableDictionary dictionary];
EOFLOGObjectLevelArgs(@"EODatabaseContext",@"object %p (class %@)=%@ ",
object,[object class],object);
/*if ([object isKindOfClass: [EOGenericRecord class]])
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dictionary=%@ ",
[object debugDictionaryDescription]);*/
for (i = 0; i < propNamesCount; i++)
{
id value = nil;
NSString *key = [classPropertyNames objectAtIndex: i];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"key=%@", key);
/*NO !!
if ([attribute isKindOfClass:[EOAttribute class]] == NO)
continue;
// if ([attribute isFlattened] == NO)
*/
value = [object storedValueForKey: key]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"key=%@ value=%@",
key, value);
if (!value)
{
value = [EONull null];
[[[entity attributeNamed: key] validateValue: &value]
raise];
}
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"key=%@ value=%@",
key, value);
[row setObject: value
forKey: key];
}
newRow = [[NSMutableDictionary alloc]
initWithDictionary: snapshot
copyItems: NO];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%@", newRow);
dbSnapshotKeys = [entity dbSnapshotKeys]; //OK (numcode, code, a3code)
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbSnapshotKeys=%@",
dbSnapshotKeys);
snapKeyCount = [dbSnapshotKeys count];
for (i = 0; i < snapKeyCount; i++)
{
id key = [dbSnapshotKeys objectAtIndex: i];
id value = [row objectForKey: key]; //Really this key ?
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"key=%@ value=%@",
key, value);
// NSAssert1(value,@"No value for %@",key);
if (value)
[newRow setObject: value
forKey: key];
}
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"newRow=%@", newRow);
[databaseOpe setNewRow: newRow];
[self recordDatabaseOperation: databaseOpe];
RELEASE(newRow);
}
}
NS_HANDLER
{
NSLog(@"EXCEPTION %@", localException);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"EXCEPTION %@",
localException);
[localException raise];
}
NS_ENDHANDLER;
EOFLOGObjectFnStop();
return databaseOpe;
}
- (void)relayPrimaryKey: (NSDictionary*)pk
sourceObject: (id)sourceObject
destObject: (id)destObject
relationship: (EORelationship*)relationship
{
//OK
NSDictionary *relayedAttributes = nil;
EOEntity *destEntity = nil;
NSArray *destAttributes = nil;
NSArray *destAttributeNames = nil;
NSDictionary *keyValues = nil;
NSArray *values = nil;
int i, count;
BOOL nullPKValues = YES;
EOFLOGObjectFnStart();
NSAssert3(destObject,
@"No destinationObject. pk=%@ relationship=%@ sourceObject=%@",
pk,relationship,sourceObject);
destAttributes = [relationship destinationAttributes];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destAttributes=%@",
destAttributes);
destAttributeNames = [destAttributes resultsOfPerformingSelector:
@selector(name)];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"destAttributeNames=%@",
destAttributeNames);
keyValues = [self valuesForKeys: destAttributeNames
object: destObject];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"keyValues=%@", keyValues);
values = [keyValues allValues];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"values=%@", values);
//Now test if null values
count = [values count];
for (i = 0; nullPKValues && i < count; i++)
nullPKValues = isNilOrEONull([values objectAtIndex:i]);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"nullPKValues=%s",
(nullPKValues ? "YES" : "NO"));
if (nullPKValues)
{
relayedAttributes = [self relayAttributesInRelationship: relationship
sourceObject: sourceObject
destinationObject: destObject];
destEntity = [relationship destinationEntity];
[self relayPrimaryKey: relayedAttributes
object: destObject
entity: destEntity];
}
EOFLOGObjectFnStop();
}
- (void)relayPrimaryKey: (NSDictionary*)pk
object: (id)object
entity: (EOEntity*)entity
{
//TODO finish
NSArray *relationships = nil;
NSArray *classPropertyNames = nil;
EODatabaseOperation *dbOpe = nil;
NSDictionary *dbSnapshot = nil;
int i, count;
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"pk=%@", pk);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"object=%@", object);
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"entity=%@", [entity name]);
relationships = [entity relationships]; //OK
classPropertyNames = [entity classPropertyNames];
dbOpe = [self databaseOperationForObject: object];
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbOpe=%@", dbOpe);
dbSnapshot = [dbOpe dbSnapshot]; //OK
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"dbSnapshot=%@", dbSnapshot);
count = [relationships count];
for (i = 0; i < count; i++)
{
EORelationship *relationship = [relationships objectAtIndex: i];
EORelationship *substRelationship = nil;
BOOL propagatesPrimaryKey = NO;
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"relationship=%@",
relationship);
substRelationship = [relationship _substitutionRelationshipForRow:
dbSnapshot];
propagatesPrimaryKey = [substRelationship propagatesPrimaryKey]; //substRelationship or relationship?
EOFLOGObjectLevelArgs(@"EODatabaseContext",
@"object=%p relationship name=%@ ==> 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 = (<EOEntity name: Customer>); }
// 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<count; i++)
{
id gid = [gids objectAtIndex:i];
[_snapshots removeObjectForKey:gid];
}
*/
EOFLOGObjectFnStop();
}
- (void)recordSnapshots: (NSDictionary *)snapshots
{
EOFLOGObjectFnStart();
NSEmitTODO();
[self notImplemented: _cmd]; //TODO
/*
[_snapshots addEntriesFromDictionary:snapshots];
*/
EOFLOGObjectFnStop();
}
- (void)recordToManySnapshots: (NSDictionary *)snapshots
{
EOFLOGObjectFnStart();
//OK
if ([_uniqueArrayStack count] > 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<count; h++)
{
join = [joins objectAtIndex:h];
value = [snapshot objectForKey:[[join sourceAttribute]
name]];
if (value == null)
realValue = nil;
else
realValue = value;
[[prop validateValue:&realValue] raise];
[row setObject:value
forKey:[[join destinationAttribute]
name]];
}
if (realValue || [prop isMandatory] == YES)
{
faultGID = [[(EORelationship *)prop destinationEntity]
globalIDForRow:row];
fault = [context objectForGlobalID:faultGID];
if (fault == nil)
fault = [context faultForGlobalID:faultGID
editingContext:context];
}
else
fault = nil;
*/
}
}
EOFLOGObjectLevel(@"EODatabaseContext", @"TakeStoredValue");
[object takeStoredValue: relObject
forKey: relName];
}
EOFLOGObjectFnStop();
}
- (void)forgetAllLocks
{
// TODO
NSEmitTODO();
// [self notImplemented:_cmd];
}
- (void)forgetLocksForObjectsWithGlobalIDs: (NSArray *)gids
{ // TODO
NSEmitTODO();
[self notImplemented: _cmd];
}
- (void) _rollbackTransaction
{ // TODO
EOFLOGObjectFnStart();
NSEmitTODO();
[self notImplemented: _cmd];
EOFLOGObjectFnStop();
}
- (void) _commitTransaction
{
EOFLOGObjectFnStart();
NSEmitTODO();
EOFLOGObjectLevelArgs(@"EODatabaseContext", @"self=%p _uniqueStack %p=%@",
self, _uniqueStack, _uniqueStack);
if ([_uniqueStack count] > 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