libs-gdl2/EOAccess/EORelationship.m
Sebastian Reitenbach 2d60697be7 * nearly every file
get rid of RCSID/RCS_ID

as shortly discussed with David and Manuel



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@37928 72102866-910b-0410-8b05-ffd578937521
2014-05-30 17:37:46 +00:00

2255 lines
62 KiB
Objective-C

/**
EORelationship.m <title>EORelationship</title>
Copyright (C) 2000-2002,2003,2004,2005 Free Software Foundation, Inc.
Author: Mirko Viviani <mirko.viviani@gmail.com>
Date: February 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 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</license>
**/
#include "config.h"
#ifdef GNUSTEP
#include <Foundation/NSArray.h>
#include <Foundation/NSException.h>
#include <Foundation/NSDebug.h>
#else
#include <Foundation/Foundation.h>
#include <GNUstepBase/GNUstep.h>
#include <GNUstepBase/GSObjCRuntime.h>
#include <GNUstepBase/NSDebug+GNUstepBase.h>
#include <GNUstepBase/NSObject+GNUstepBase.h>
#endif
#include <EOControl/EOObserver.h>
#include <EOControl/EOMutableKnownKeyDictionary.h>
#include <EOControl/EONSAddOns.h>
#include <EOControl/EODebug.h>
#include <EOAccess/EOModel.h>
#include <EOAccess/EOModelGroup.h>
#include <EOAccess/EOAttribute.h>
#include <EOAccess/EOEntity.h>
#include <EOAccess/EOStoredProcedure.h>
#include <EOAccess/EORelationship.h>
#include <EOAccess/EOJoin.h>
#include <EOAccess/EOExpressionArray.h>
#include "EOPrivate.h"
#include "EOAttributePriv.h"
#include "EOEntityPriv.h"
@interface EORelationship (EORelationshipPrivate)
- (void)_setInverseRelationship: (EORelationship *)relationship;
@end
@implementation EORelationship
+ (void)initialize
{
static BOOL initialized = NO;
if (!initialized)
{
initialized = YES;
GDL2_EOAccessPrivateInit();
}
}
/*
this is used for key-value observing.
*/
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
{
if ([theKey isEqualToString:@"joins"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:theKey];
}
+ (id) relationshipWithPropertyList: (NSDictionary *)propertyList
owner: (id)owner
{
return AUTORELEASE([[self alloc] initWithPropertyList: propertyList
owner: owner]);
}
+ (EOJoinSemantic) _joinSemanticForName:(NSString*) semanticName
{
if ([semanticName isEqual: @"EOInnerJoin"])
return EOInnerJoin;
else if ([semanticName isEqual: @"EOFullOuterJoin"])
return EOFullOuterJoin;
else if ([semanticName isEqual: @"EOLeftOuterJoin"])
return EOLeftOuterJoin;
else if ([semanticName isEqual: @"EORightOuterJoin"])
return EORightOuterJoin;
else
{
[NSException raise: NSInvalidArgumentException
format: @"%s: Unknown joinSemantic '%@'", __PRETTY_FUNCTION__, semanticName];
}
// make the compiler happy
return EOInnerJoin;
}
+ (NSString *) _nameForJoinSemantic:(EOJoinSemantic) semantic
{
switch (semantic)
{
case EOInnerJoin:
return @"EOInnerJoin";
case EOFullOuterJoin:
return @"EOFullOuterJoin";
case EOLeftOuterJoin:
return @"EOLeftOuterJoin";
case EORightOuterJoin:
return @"EORightOuterJoin";
}
[NSException raise: NSInvalidArgumentException
format: @"%s: Unknown joinSemantic '%d'", __PRETTY_FUNCTION__, semantic];
// make the compiler happy
return nil;
}
- (id)init
{
//OK
if ((self = [super init]))
{
/*
_sourceNames = [NSMutableDictionary new];
_destinationNames = [NSMutableDictionary new];
_userInfo = [NSDictionary new];
_sourceToDestinationKeyMap = [NSDictionary new];
*/
_joins = [NSMutableArray new];
}
return self;
}
- (void)dealloc
{
[self _flushCache];
DESTROY(_name);
DESTROY(_qualifier);
DESTROY(_sourceNames);
DESTROY(_destinationNames);
DESTROY(_userInfo);
DESTROY(_internalInfo);
DESTROY(_docComment);
DESTROY(_joins);
DESTROY(_sourceToDestinationKeyMap);
DESTROY(_sourceRowToForeignKeyMapping);
DESTROY(_definitionArray);
_entity = nil;
_destination = nil;
[super dealloc];
}
- (NSUInteger)hash
{
return [_name hash];
}
- (id) initWithPropertyList: (NSDictionary *)propertyList
owner: (id)owner
{
//Near OK
if ((self = [self init]))
{
EOModel* model = [owner model];
NSString* relationshipName = [propertyList objectForKey: @"name"];
NSString* joinSemanticString = nil;
NSString* destinationEntityName = nil;
EOEntity* destinationEntity = nil;
NSString* deleteRuleString = nil;
/* so setName: can validate against the owner */
[self setEntity: owner];
[self setName: relationshipName];
destinationEntityName = [propertyList objectForKey: @"destination"];
if (destinationEntityName) //If not, this is because it's a definition
{
destinationEntity = [model entityNamed: destinationEntityName];
_destination = destinationEntity;
}
[self setToMany: [[propertyList objectForKey: @"isToMany"]
isEqual: @"Y"]];
[self setIsMandatory: [[propertyList objectForKey: @"isMandatory"]
isEqual:@"Y"]];
[self setOwnsDestination: [[propertyList
objectForKey: @"ownsDestination"]
isEqual: @"Y"]];
[self setPropagatesPrimaryKey: [[propertyList
objectForKey: @"propagatesPrimaryKey"]
isEqual: @"Y"]];
[self setIsBidirectional: [[propertyList objectForKey: @"isBidirectional"]
isEqual: @"Y"]];
[self setUserInfo: [propertyList objectForKey: @"userInfo"]];
if(!_userInfo)
[self setUserInfo: [propertyList objectForKey: @"userDictionary"]];
[self setInternalInfo: [propertyList objectForKey: @"internalInfo"]];
[self setDocComment: [propertyList objectForKey: @"docComment"]];
joinSemanticString = [propertyList objectForKey: @"joinSemantic"];
if (joinSemanticString)
{
[self setJoinSemantic: [[self class] _joinSemanticForName:joinSemanticString]];
}
else
{
if (destinationEntityName)
{
EOFLOGObjectLevelArgs(@"EORelationship", @"!joinSemanticString but destinationEntityName. entityName=%@ relationshipName=%@",
[(EOEntity*)owner name],
relationshipName);
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; //TODO
}
}
deleteRuleString = [propertyList objectForKey: @"deleteRule"];
EOFLOGObjectLevelArgs(@"EORelationship", @"entityName=%@ relationshipName=%@ deleteRuleString=%@",
[(EOEntity*)owner name],
relationshipName,
deleteRuleString);
if (deleteRuleString)
{
EODeleteRule deleteRule = [self _deleteRuleFromString:
deleteRuleString];
EOFLOGObjectLevelArgs(@"EORelationship",
@"entityName=%@ relationshipName=%@ deleteRule=%d",
[(EOEntity*)owner name],
relationshipName,
(int)deleteRule);
NSAssert2(deleteRule >= 0 && deleteRule <= 3,
@"Bad deleteRule numeric value: %@ (%d)",
deleteRuleString,
deleteRule);
[self setDeleteRule: deleteRule];
}
}
return self;
}
- (void)awakeWithPropertyList: (NSDictionary *)propertyList //TODO
{
NSString *definition = [propertyList objectForKey: @"definition"];
EOFLOGObjectLevelArgs(@"EORelationship", @"definition=%@", definition);
if (definition)
{
[self setDefinition: definition];
}
else
{
NSString *dataPath = [propertyList objectForKey: @"dataPath"];
EOFLOGObjectLevelArgs(@"EORelationship", @"dataPath=%@", dataPath);
if (dataPath)
{
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; // TODO
}
else
{
NSArray *joins = [propertyList objectForKey: @"joins"];
int count = [joins count];
EOFLOGObjectLevelArgs(@"EORelationship", @"joins=%@", joins);
if (count > 0)
{
int i;
for (i = 0; i < count; i++)
{
NSDictionary *joinPList =
[joins objectAtIndex: i];
/*NSString *joinSemantic =
[joinPList objectForKey: @"joinSemantic"];*/
NSString *sourceAttributeName =
[joinPList objectForKey:@"sourceAttribute"];
EOAttribute *sourceAttribute =
[_entity attributeNamed:sourceAttributeName];
EOEntity *destinationEntity =
[self destinationEntity];
NSString *destinationAttributeName =
[joinPList objectForKey:@"destinationAttribute"];
EOAttribute *destinationAttribute =
[destinationEntity attributeNamed:destinationAttributeName];
EOJoin *join = nil;
NSAssert4(sourceAttribute, @"No sourceAttribute named \"%@\" in entity \"%@\" in relationship %@\nEntity: %@",
sourceAttributeName,
[_entity name],
self,
_entity);
NSAssert3(destinationEntity,@"No destination entity for relationship named '%@' in entity named '%@': %@",
[self name],
[[self entity]name],
self);
NSAssert4(destinationAttribute, @"No destinationAttribute named \"%@\" in entity \"%@\" in relationship %@\nEntity: %@",
destinationAttributeName,
[destinationEntity name],
self,
destinationEntity);
NS_DURING
{
join = [EOJoin joinWithSourceAttribute: sourceAttribute
destinationAttribute: destinationAttribute];
}
NS_HANDLER
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: cannot create join for relationship '%@': %@",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
[localException reason]];
}
NS_ENDHANDLER;
EOFLOGObjectLevelArgs(@"EORelationship", @"join=%@", join);
[self addJoin: join];
}
}
/*
NSArray *array;
NSEnumerator *enumerator;
EOModel *model = [_entity model];
id joinPList;
if(_destination)
{
id destinationEntityName = [_destination autorelease];
_destination = [[model entityNamed:destinationEntityName] retain];
if(!_destination)
{
NSEmitTODO(); //TODO
[self notImplemented:_cmd]; // TODO
}
}
*/
}
}
/* ??
if(!(_destination || _definitionArray))
{
NSEmitTODO(); //TODO
[self notImplemented:_cmd]; // TODO
};
*/
}
- (void)encodeIntoPropertyList: (NSMutableDictionary *)propertyList
{
NS_DURING //Just for debugging
{
//VERIFY
[propertyList setObject: [self name]
forKey: @"name"];
if ([self isFlattened])
{
NSString *definition = [self definition];
NSAssert(definition,@"No definition");
[propertyList setObject: definition
forKey: @"definition"];
}
else
{
[propertyList setObject: ([self isToMany] ? @"Y" : @"N")
forKey: @"isToMany"];
if ([self destinationEntity])
{
NSAssert2([[self destinationEntity] name],
@"No entity name in relationship named %@ entity named %@",
[self name],
[[self entity]name]);
[propertyList setObject: [[self destinationEntity] name] // if we put entity, it loops !!
forKey: @"destination"];
};
}
if ([self isMandatory])
{
[propertyList setObject: @"Y"
forKey: @"isMandatory"];
}
if ([self ownsDestination])
{
[propertyList setObject: @"Y"
forKey: @"ownsDestination"];
}
if ([self propagatesPrimaryKey])
{
[propertyList setObject: @"Y"
forKey: @"propagatesPrimaryKey"];
}
{
int joinsCount = [_joins count];
if (joinsCount > 0)
{
NSMutableArray *joinsArray = [NSMutableArray array];
int i = 0;
for(i = 0; i < joinsCount; i++)
{
NSMutableDictionary *joinDict = [NSMutableDictionary dictionary];
EOJoin *join = [_joins objectAtIndex: i];
NSAssert([[join sourceAttribute] name],
@"No source attribute name");
[joinDict setObject: [[join sourceAttribute] name]
forKey: @"sourceAttribute"];
NSAssert([[join destinationAttribute] name],
@"No destination attribute name");
[joinDict setObject: [[join destinationAttribute] name]
forKey: @"destinationAttribute"];
[joinsArray addObject: joinDict];
}
[propertyList setObject: joinsArray
forKey: @"joins"];
}
NSAssert([self joinSemanticString],
@"No joinSemanticString");
[propertyList setObject: [self joinSemanticString]
forKey: @"joinSemantic"];
}
}
NS_HANDLER
{
NSLog(@"exception in EORelationship encodeIntoPropertyList: self=%p class=%@",
self, [self class]);
NSDebugMLog(@"exception in EORelationship encodeIntoPropertyList: self=%p class=%@",
self, [self class]);
NSLog(@"exception=%@", localException);
NSDebugMLog(@"exception=%@", localException);
[localException raise];
}
NS_ENDHANDLER;
}
- (NSString *)description
{
NSString *dscr = nil;
NS_DURING //Just for debugging
{
dscr = [NSString stringWithFormat: @"<%s %p - name=%@ entity=%@ destinationEntity=%@ definition=%@",
object_getClassName(self),
(void*)self,
[self name],
[[self entity]name],
[[self destinationEntity] name],
[self definition]];
dscr = [dscr stringByAppendingFormat: @" userInfo=%@",
[self userInfo]];
dscr = [dscr stringByAppendingFormat: @" joinSemantic=%@",
[[self class] _nameForJoinSemantic:_joinSemantic]];
dscr = [dscr stringByAppendingFormat: @" joins=%@",
[self joins]];
dscr = [dscr stringByAppendingFormat: @" sourceAttributes=%@",
[self sourceAttributes]];
dscr = [dscr stringByAppendingFormat: @" destinationAttributes=%@",
[self destinationAttributes]];
/*TODO dscr = [dscr stringByAppendingFormat:@" componentRelationships=%@",
[self componentRelationships]];*/
dscr = [dscr stringByAppendingFormat: @" isCompound=%s isFlattened=%s isToMany=%s isBidirectional=%s>",
([self isCompound] ? "YES" : "NO"),
([self isFlattened] ? "YES" : "NO"),
([self isToMany] ? "YES" : "NO"),
([self isBidirectional] ? "YES" : "NO")];
}
NS_HANDLER
{
NSLog(@"exception in EORelationship description: self=%p class=%@",
self, [self class]);
NSDebugMLog(@"exception in EORelationship description: self=%p class=%@",
self, [self class]);
NSLog(@"exception=%@", localException);
NSDebugMLog(@"exception=%@", localException);
[localException raise];
}
NS_ENDHANDLER;
return dscr;
}
- (NSString *)name
{
return _name;
}
/** Returns the relationship's source entity. **/
- (EOEntity *)entity
{
return _entity;
}
/** Returns the relationship's destination entity (direct destination entity or
destination entity of the last relationship in definition. **/
- (EOEntity *)destinationEntity
{
//OK
// May be we could cache destination ? Hard to do because klast relationship may have its destination entity change.
EOEntity *destinationEntity = _destination;
if (!destinationEntity)
{
if ([self isFlattened])
{
EORelationship *lastRelationship = [_definitionArray lastObject];
destinationEntity = [lastRelationship destinationEntity];
NSAssert3(destinationEntity, @"No destinationEntity in last relationship: %@ of relationship %@ in entity %@",
lastRelationship, self, [_entity name]);
}
else
{
[self _joinsChanged];
destinationEntity = _destination;
}
}
else if ([destinationEntity isKindOfClass: [NSString class]] == YES)
destinationEntity = [[_entity model]
entityNamed: (NSString*)destinationEntity];
return destinationEntity;
}
- (BOOL) isParentRelationship
{
BOOL isParentRelationship=NO;
EOEntity *destinationEntity = [self destinationEntity];
if(destinationEntity != nil
&& destinationEntity == [_entity parentEntity])
{
NSArray* attributes = [self sourceAttributes];
NSArray* pkAttributes = [_entity primaryKeyAttributes];
if([attributes containsIdenticalObjectsWithArray:pkAttributes])
{
attributes = [self destinationAttributes];
pkAttributes = [_destination primaryKeyAttributes];
isParentRelationship=[attributes containsIdenticalObjectsWithArray:pkAttributes];
}
}
return isParentRelationship;
}
/** Returns YES when the relationship traverses at least two entities
(exemple: aRelationship.anotherRelationship), NO otherwise.
**/
- (BOOL)isFlattened
{
return (_definitionArray==nil ? NO : YES);
}
/** return YES if the relation if a to-many one, NO otherwise (please read books
to know what to-many mean :-) **/
- (BOOL)isToMany
{
return _flags.isToMany;
}
/** Returns YES if the relationship have more than 1 join (i.e. join on more that one (sourceAttribute/destinationAttribute), NO otherwise (1 or less join) **/
- (BOOL)isCompound
{
//OK
return [_joins count] > 1;
}
- (NSArray *)joins
{
return _joins;
}
- (NSArray *)sourceAttributes
{
if (!_sourceAttributes)
{
int i, count = [_joins count];
_sourceAttributes = [NSMutableArray new];
for (i = 0; i < count; i++)
{
EOJoin *join = [_joins objectAtIndex: i];
[(NSMutableArray*)_sourceAttributes addObject:
[join sourceAttribute]];
}
}
return _sourceAttributes;
}
- (NSArray *)destinationAttributes
{
if (!_destinationAttributes)
{
int i, count = [_joins count];
_destinationAttributes = [NSMutableArray new];
for (i = 0; i < count; i++)
{
EOJoin *join = [_joins objectAtIndex: i];
[(NSMutableArray *)_destinationAttributes addObject:
[join destinationAttribute]];
}
}
return _destinationAttributes;
}
- (EOJoinSemantic)joinSemantic
{
return _joinSemantic;
}
/*
this seems to be GNUstep only -- dw
*/
- (NSString*)joinSemanticString
{
return [[self class] _nameForJoinSemantic:[self joinSemantic]];
}
/**
* Returns the array of relationships composing this flattend relationship.
* Returns nil of the reciever isn't flattend.
*/
- (NSArray *)componentRelationships
{
return _definitionArray;
}
- (NSDictionary *)userInfo
{
return _userInfo;
}
- (NSString *)docComment
{
return _docComment;
}
- (NSString *)definition
{
//OK
NSString *definition = nil;
NS_DURING //Just for debugging
{
definition = [_definitionArray valueForSQLExpression: nil];
}
NS_HANDLER
{
NSLog(@"exception in EORelationship definition: self=%p class=%@",
self, [self class]);
NSLog(@"exception in EORelationship definition: self=%@ _definitionArray=%@",
self, _definitionArray);
NSLog(@"exception=%@", localException);
[localException raise];
}
NS_ENDHANDLER;
return definition;
}
/** Returns the value to use in an EOSQLExpression. **/
- (NSString*) valueForSQLExpression: (EOSQLExpression*)sqlExpression
{
return [self name];
}
- (BOOL)referencesProperty: (id)property
{
if (property == nil)
return NO;
if ([self isFlattened])
{
return [_definitionArray referencesObject:property];
}
if (_joins) {
NSEnumerator *joinEnumer = [_joins objectEnumerator];
EOJoin *join;
while ((join = [joinEnumer nextObject])) {
if (([join sourceAttribute] == property) || ([join destinationAttribute] == property))
{
return YES;
}
}
}
return NO;
}
- (EODeleteRule)deleteRule
{
return _flags.deleteRule;
}
- (BOOL)isMandatory
{
return _flags.isMandatory;
}
- (BOOL)propagatesPrimaryKey
{
return _flags.propagatesPrimaryKey;
}
- (BOOL)isBidirectional
{
return _flags.isBidirectional;
}
- (BOOL)isReciprocalToRelationship: (EORelationship *)relationship
{
BOOL isReciprocal = NO;
EOEntity *entity = [self entity];
EOEntity *relationshipDestinationEntity = [relationship destinationEntity];
EOFLOGObjectLevelArgs(@"EORelationship", @"entity %p name=%@",
entity, [entity name]);
EOFLOGObjectLevelArgs(@"EORelationship",
@"relationshipDestinationEntity %p name=%@",
relationshipDestinationEntity,
[relationshipDestinationEntity name]);
if (entity == relationshipDestinationEntity)
{
if ([self isFlattened])
{
if ([relationship isFlattened])
{
//Now compare each components in reversed order
NSArray *selfComponentRelationships =
[self componentRelationships];
NSArray *relationshipComponentRelationships =
[relationship componentRelationships];
int selfComponentRelationshipsCount =
[selfComponentRelationships count];
int relationshipComponentRelationshipsCount =
[relationshipComponentRelationships count];
//May be we can imagine that they may not have the same number of components //TODO
if (selfComponentRelationshipsCount
== relationshipComponentRelationshipsCount)
{
int i, j;
BOOL foundEachInverseComponent = YES;
for(i = (selfComponentRelationshipsCount - 1), j = 0;
foundEachInverseComponent && i >= 0;
i--, j++)
{
EORelationship *selfRel =
[selfComponentRelationships objectAtIndex: i];
EORelationship *relationshipRel =
[relationshipComponentRelationships objectAtIndex: j];
foundEachInverseComponent =
[selfRel isReciprocalToRelationship: relationshipRel];
}
if (foundEachInverseComponent)
isReciprocal = YES;
}
}
}
else
{
//WO doesn't test inverses entity; we does.
EOEntity *relationshipEntity = [relationship entity];
EOEntity *destinationEntity = [self destinationEntity];
EOFLOGObjectLevelArgs(@"EORelationship",
@"relationshipEntity %p name=%@",
relationshipEntity, [relationshipEntity name]);
EOFLOGObjectLevelArgs(@"EORelationship",
@"destinationEntity %p name=%@",
destinationEntity, [destinationEntity name]);
if (relationshipEntity == destinationEntity)
{
NSArray *joins = [self joins];
NSArray *relationshipJoins = [relationship joins];
int joinsCount = [joins count];
int relationshipJoinsCount = [relationshipJoins count];
EOFLOGObjectLevelArgs(@"EORelationship",
@"joinsCount=%d,relationshipJoinsCount=%d",
joinsCount, relationshipJoinsCount);
if (joinsCount == relationshipJoinsCount)
{
BOOL foundEachInverseJoin = YES;
int iJoin;
for (iJoin = 0;
foundEachInverseJoin && iJoin < joinsCount;
iJoin++)
{
EOJoin *join = [joins objectAtIndex: iJoin];
int iRelationshipJoin;
BOOL foundInverseJoin = NO;
EOFLOGObjectLevelArgs(@"EORelationship", @"%d join=%@",
iJoin, join);
for (iRelationshipJoin = 0;
!foundInverseJoin && iRelationshipJoin < joinsCount;
iRelationshipJoin++)
{
EOJoin *relationshipJoin =
[relationshipJoins objectAtIndex:iRelationshipJoin];
EOFLOGObjectLevelArgs(@"EORelationship",
@"%d relationshipJoin=%@",
iRelationshipJoin,
relationshipJoin);
foundInverseJoin = [relationshipJoin
isReciprocalToJoin: join];
EOFLOGObjectLevelArgs(@"EORelationship",
@"%d foundInverseJoin=%s",
iRelationshipJoin,
(foundInverseJoin ? "YES" : "NO"));
}
if (!foundInverseJoin)
foundEachInverseJoin = NO;
EOFLOGObjectLevelArgs(@"EORelationship",
@"%d foundEachInverseJoin=%s",
iJoin,
(foundEachInverseJoin ? "YES" : "NO"));
}
EOFLOGObjectLevelArgs(@"EORelationship",
@"foundEachInverseJoin=%s",
(foundEachInverseJoin ? "YES" : "NO"));
if (foundEachInverseJoin)
isReciprocal = YES;
}
}
}
}
return isReciprocal;
}
/** "Search only already created inverse relationship in destination entity
relationships. Nil if none" **/
- (EORelationship *)inverseRelationship
{
if (!_inverseRelationship)
{
EOEntity* destinationEntity = [self destinationEntity];
NSArray* destinationEntityRelationships =
[destinationEntity relationships];
if ([destinationEntityRelationships count] > 0)
{
int i, count = [destinationEntityRelationships count];
for (i = 0; !_inverseRelationship && i < count; i++)
{
EORelationship *testRelationship =
[destinationEntityRelationships objectAtIndex: i];
if ([self isReciprocalToRelationship: testRelationship])
{
ASSIGN(_inverseRelationship, testRelationship);
}
}
}
}
return _inverseRelationship;
}
- (EORelationship *) _makeFlattenedInverseRelationship
{
//OK
EORelationship *inverseRelationship = nil;
NSMutableString *invDefinition = nil;
NSString *name = nil;
int i, count;
NSAssert([self isFlattened], @"Not Flatten Relationship");
EOFLOGObjectLevel(@"EORelationship", @"add joins");
count = [_definitionArray count];
for (i = count - 1; i >= 0; i--)
{
EORelationship *rel = [_definitionArray objectAtIndex: i];
EORelationship *invRel = [rel anyInverseRelationship];
NSString *invRelName = [invRel name];
if (invDefinition)
{
if (i < (count - 1))
[invDefinition appendString: @"."];
[invDefinition appendString: invRelName];
}
else
invDefinition = [NSMutableString stringWithString: invRelName];
}
inverseRelationship = [[EORelationship new] autorelease];
[inverseRelationship setEntity: [self destinationEntity]];
name = [NSString stringWithFormat: @"_eofInv_%@_%@",
[_entity name],
_name];
[inverseRelationship setName: name];
[inverseRelationship setDefinition: invDefinition];
EOFLOGObjectLevel(@"EORelationship", @"add inverse rel");
[(NSMutableArray*)[[self destinationEntity] _hiddenRelationships]
addObject: inverseRelationship]; //not very clean !!!
EOFLOGObjectLevel(@"EORelationship", @"set inverse rel");
[inverseRelationship _setInverseRelationship: self];
return inverseRelationship;
}
- (EORelationship*) _makeInverseRelationship
{
EORelationship *inverseRelationship;
NSString *name;
NSArray *joins = nil;
unsigned int i, count;
NSAssert(![self isFlattened], @"Flatten Relationship");
inverseRelationship = [[EORelationship new] autorelease];
name = [NSString stringWithFormat: @"_eofInv_%@_%@",
[_entity name],
_name];
[inverseRelationship setName: name];
joins = [self joins];
count = [joins count];
EOFLOGObjectLevel(@"EORelationship", @"add joins");
for (i = 0; i < count; i++)
{
EOJoin *join = [joins objectAtIndex: i];
EOAttribute *sourceAttribute = [join sourceAttribute];
EOAttribute *destinationAttribute = [join destinationAttribute];
EOJoin *inverseJoin = [EOJoin joinWithSourceAttribute:
destinationAttribute //inverse souce<->destination attributes
destinationAttribute: sourceAttribute];
[inverseRelationship addJoin: inverseJoin];
}
EOFLOGObjectLevel(@"EORelationship",@"add inverse rel");
[(NSMutableArray*)[[self destinationEntity] _hiddenRelationships]
addObject: inverseRelationship]; //not very clean !!!
EOFLOGObjectLevel(@"EORelationship", @"set inverse rel");
[inverseRelationship _setInverseRelationship: self];
/* call this last to avoid calls to [_destination _setIsEdited] */
[inverseRelationship setEntity: _destination];
return inverseRelationship;
}
- (EORelationship*) hiddenInverseRelationship
{
if (!_hiddenInverseRelationship)
{
if ([self isFlattened])
_hiddenInverseRelationship = [self _makeFlattenedInverseRelationship];
else
_hiddenInverseRelationship = [self _makeInverseRelationship];
}
return _hiddenInverseRelationship;
}
- (EORelationship *)anyInverseRelationship
{
EORelationship *inverseRelationship = [self inverseRelationship];
if (!inverseRelationship)
inverseRelationship = [self hiddenInverseRelationship];
return inverseRelationship;
}
- (unsigned int)numberOfToManyFaultsToBatchFetch
{
return _batchCount;
}
- (BOOL)ownsDestination
{
return _flags.ownsDestination;
}
- (EOQualifier *)qualifierWithSourceRow: (NSDictionary *)sourceRow
{
EOQualifier* q = nil;
EOQualifier* q1 = [self qualifierOmittingAuxiliaryQualifierWithSourceRow:sourceRow];
EOQualifier* q2 = [self auxiliaryQualifier];
if (q1 != nil)
{
if (q2!=nil)
q=[EOAndQualifier qualifierWithQualifiers:q1,q2,nil];
else
q=q1;
}
else
q=q2;
return q;
}
@end /* EORelationship */
@implementation EORelationship (EORelationshipEditing)
- (NSException *)validateName: (NSString *)name
{
const char *p, *s = [name cString];
int exc = 0;
NSArray *storedProcedures = nil;
if ([_name isEqual:name])
return nil;
if (!name || ![name length])
exc++;
if (!exc)
{
p = s;
while (*p)
{
if(!isalnum(*p) &&
*p != '@' && *p != '#' && *p != '_' && *p != '$')
{
exc++;
break;
}
p++;
}
if (!exc && *s == '$')
exc++;
if (exc)
return [NSException exceptionWithName: NSInvalidArgumentException
reason: [NSString stringWithFormat: @"%@ -- %@ 0x%p: argument \"%@\" contains invalid char '%c'",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
name,
*p]
userInfo: nil];
if ([[self entity] _hasAttributeNamed: name])
exc++;
else if ([[self entity] anyRelationshipNamed: name])
exc++;
else if ((storedProcedures = [[[self entity] model] storedProcedures]))
{
NSEnumerator *stEnum = [storedProcedures objectEnumerator];
EOStoredProcedure *st;
while ((st = [stEnum nextObject]))
{
NSEnumerator *attrEnum;
EOAttribute *attr;
attrEnum = [[st arguments] objectEnumerator];
while ((attr = [attrEnum nextObject]))
{
if ([name isEqualToString: [attr name]])
{
exc++;
break;
}
}
if (exc)
break;
}
}
}
if (exc)
{
return [NSException exceptionWithName: NSInvalidArgumentException
reason: [NSString stringWithFormat: @"%@ -- %@ 0x%p: \"%@\" already used in the model",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
name]
userInfo: nil];
}
return nil;
}
- (void)setToMany: (BOOL)flag
{
if ([self isFlattened])
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: receiver is a flattened relationship",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
if (_flags.isToMany != flag)
{
[self willChange];
[_entity _setIsEdited];
_flags.isToMany = flag;
}
}
- (void)setName: (NSString *)name
{
[[self validateName: name] raise];
[self willChange];
[_entity _setIsEdited];
ASSIGNCOPY(_name, name);
}
- (void)setDefinition: (NSString *)definition
{
EOFLOGObjectLevelArgs(@"EORelationship", @"definition=%@", definition);
[self _flushCache];
[self willChange];
if (definition)
{
_flags.isToMany = NO;
NSAssert1(_entity,@"No entity for relationship %@",
self);
ASSIGN(_definitionArray, [_entity _parseRelationshipPath: definition]);
EOFLOGObjectLevelArgs(@"EORelationship", @"_definitionArray=%@", _definitionArray);
EOFLOGObjectLevelArgs(@"EORelationship", @"[self definition]=%@", [self definition]);
_destination = nil;
{
//TODO VERIFY
//TODO better ?
int i, count = [_definitionArray count];
EOFLOGObjectLevelArgs(@"EORelationship", @"==> _definitionArray=%@",
_definitionArray);
for (i = 0; !_flags.isToMany && i < count; i++)
{
EORelationship *rel = [_definitionArray objectAtIndex: i];
if ([rel isKindOfClass: GDL2_EORelationshipClass])
{
if ([rel isToMany])
_flags.isToMany = YES;
}
else
break;
}
}
}
else /* definition == nil */
{
DESTROY(_definitionArray);
}
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
/**
* <p>Sets the entity of the reciever.</p>
* <p>If the receiver already has an entity assigned to it the old relationship
* will will be removed first.</p>
* <p>This method is used by [EOEntity-addRelationship:] and
* [EOEntity-removeRelationship:] which should be used for general relationship
* manipulations. This method should only be useful
* when creating flattend relationships programmatically.</p>
*/
- (void)setEntity: (EOEntity *)entity
{
if (entity != _entity)
{
[self _flushCache];
[self willChange];
if (_entity)
{
/* Check if we are still in the entities arrays to
avoid recursive loop when removeRelationship:
calls this method. */
NSString *relationshipName = [self name];
if (self == [_entity relationshipNamed: relationshipName])
[_entity removeRelationship: self];
}
_entity = entity;
}
/* This method is used by EOEntity's remove/addRelatinship: and is not
responsible for calling _setIsEdited on the entity. */
}
- (void)setUserInfo: (NSDictionary *)dictionary
{
[self willChange];
ASSIGN(_userInfo, dictionary);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setInternalInfo: (NSDictionary *)dictionary
{
[self willChange];
ASSIGN(_internalInfo, dictionary);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setDocComment: (NSString *)docComment
{
[self willChange];
ASSIGNCOPY(_docComment, docComment);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setPropagatesPrimaryKey: (BOOL)flag
{
if (_flags.propagatesPrimaryKey != flag)
[self willChange];
_flags.propagatesPrimaryKey = flag;
}
- (void)setIsBidirectional: (BOOL)flag
{
if (_flags.isBidirectional != flag)
[self willChange];
_flags.isBidirectional = flag;
}
- (void)setOwnsDestination: (BOOL)flag
{
if (_flags.ownsDestination != flag)
[self willChange];
_flags.ownsDestination = flag;
}
- (void)addJoin: (EOJoin *)join
{
EOFLOGObjectLevelArgs(@"EORelationship", @"Add join: %@\nto %@", join, self);
if ([self isFlattened] == YES)
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: receiver is a flattened relationship",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
}
else
{
EOEntity *destinationEntity = [self destinationEntity];
EOEntity *sourceEntity = [self entity];
EOAttribute *sourceAttribute = [join sourceAttribute];
EOAttribute *destinationAttribute = [join destinationAttribute];
EOFLOGObjectLevelArgs(@"EORelationship", @"destinationEntity=%@", destinationEntity);
NSAssert3(sourceAttribute, @"No source attribute in join %@ in relationship %@ of entity %@",
join,
self,
sourceEntity);
NSAssert3(destinationAttribute, @"No destination attribute in join %@ in relationship %@ of entity %@",
join,
self,
sourceEntity);
if ([sourceAttribute isFlattened] == YES
|| [destinationAttribute isFlattened] == YES)
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: join's attributes are flattened",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
}
else
{
EOEntity *joinDestinationEntity = [destinationAttribute entity];
EOEntity *joinSourceEntity = [sourceAttribute entity];
/* if (destinationEntity && ![[destinationEntity name] isEqual:[joinSourceEntity name]])
{
[NSException raise:NSInvalidArgumentException
format:@"%@ -- %@ 0x%x: join source entity (%@) is not equal to last join entity (%@)",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[joinSourceEntity name],
[destinationEntity name]];
}*/
if (sourceEntity
&& ![[joinSourceEntity name] isEqual: [sourceEntity name]])
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p (%@): join source entity (%@) is not equal to relationship entity (%@)",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
[joinSourceEntity name],
[sourceEntity name]];
}
else if (destinationEntity
&& ![[joinDestinationEntity name]
isEqual: [destinationEntity name]])
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p (%@): join destination entity (%@) is not equal to relationship destination entity (%@)",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
[joinDestinationEntity name],
[destinationEntity name]];
}
else
{
if ([_sourceAttributes count])
{
EOAttribute *sourceAttribute = [join sourceAttribute];
EOAttribute *destinationAttribute;
destinationAttribute = [join destinationAttribute];
if (([_sourceAttributes indexOfObject: sourceAttribute]
!= NSNotFound)
&& ([_destinationAttributes
indexOfObject: destinationAttribute]
!= NSNotFound))
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: TODO",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
}
}
[self _flushCache];
// do we still need willChange when we are not putting EORelationships into ECs? -- dw
[self willChange];
// needed for KV bbserving
[self willChangeValueForKey:@"joins"];
EOFLOGObjectLevel(@"EORelationship", @"really add");
EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@",
_joins, [_joins class]);
if (!_joins)
_joins = [NSMutableArray new];
[(NSMutableArray *)_joins addObject: join];
EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@",
_joins, [_joins class]);
EOFLOGObjectLevel(@"EORelationship", @"added");
[self _joinsChanged];
[self didChangeValueForKey:@"joins"];
/* Ayers: Not sure what justifies this. MGuesdon: EOF seems to do it */
[_entity _setIsEdited];
}
}
}
}
- (void)removeJoin: (EOJoin *)join
{
if ([self isFlattened] == YES)
{
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: receiver is a flattened relationship",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
}
else
{
[self _flushCache];
[self willChangeValueForKey:@"joins"];
[self willChange];
[(NSMutableArray *)_joins removeObject: join];
EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@",
_joins, [_joins class]);
[self _joinsChanged];
/* Ayers: Not sure what justifies this. MGuesdon: EOF seems to do it */
[_entity _setIsEdited];
[self didChangeValueForKey:@"joins"];
}
}
- (void)setJoinSemantic: (EOJoinSemantic)joinSemantic
{
[self willChange];
_joinSemantic = joinSemantic;
}
- (void)beautifyName
{
/*+ Make the name conform to the Next naming style
NAME -> name, FIRST_NAME -> firstName +*/
NSArray *listItems;
NSString *newString = [NSString string];
int anz, i;
EOFLOGObjectFnStartOrCond2(@"ModelingClasses", @"EORelationship");
/* Makes the receiver's name conform to a standard convention. Names that
conform to this style are all lower-case except for the initial letter of
each embedded word other than the first, which is upper case. Thus, "NAME"
becomes "name", and "FIRST_NAME" becomes "firstName".*/
if ((_name) && ([_name length] > 0))
{
listItems = [_name componentsSeparatedByString: @"_"];
newString = [newString stringByAppendingString:
[[listItems objectAtIndex: 0] lowercaseString]];
anz = [listItems count];
for (i = 1; i < anz; i++)
{
newString = [newString stringByAppendingString:
[[listItems objectAtIndex: i]
capitalizedString]];
}
// Exception abfangen
NS_DURING
{
[self setName:newString];
}
NS_HANDLER
{
NSLog(@"%@ in Class: EORlationship , Method: beautifyName >> error : %@",
[localException name], [localException reason]);
}
NS_ENDHANDLER;
}
EOFLOGObjectFnStopOrCond2(@"ModelingClasses", @"EORelationship");
}
- (void)setNumberOfToManyFaultsToBatchFetch: (unsigned int)size
{
[self willChange];
_flags.useBatchFaulting=YES;
_batchCount = size;
}
- (void)setDeleteRule: (EODeleteRule)deleteRule
{
NSAssert1(deleteRule >= 0 && deleteRule <= 3,
@"Bad deleteRule numeric value: %d",
deleteRule);
[self willChange];
_flags.deleteRule = deleteRule;
}
- (void)setIsMandatory: (BOOL)isMandatory
{
if (_flags.isMandatory!=isMandatory)
[self willChange];
_flags.isMandatory = isMandatory;
}
@end
@implementation EORelationship (EORelationshipValueMapping)
/**
* If the reciever is a manditory relationship, this method
* returns an exception if the value pointed to by VALUEP is
* either nil or the EONull instance for to-one relationships
* or an empty NSArray for to-many relationships. Otherwise
* it returns nil. EOClassDescription adds further information
* to this exception before it gets passed to the application or
* user.
*/
- (NSException *)validateValue: (id*)valueP
{
NSException *exception = nil;
NSAssert(valueP, @"No value pointer");
if ([self isMandatory])
{
BOOL isToMany = [self isToMany];
if (isToMany == NO)
{
if (_isNilOrEONull(*valueP))
{
EOEntity *destinationEntity = [self destinationEntity];
EOEntity *entity = [self entity];
exception = [NSException validationExceptionWithFormat:
@"The %@ property of %@ must have a %@ assigned",
[self name],
[entity name],
[destinationEntity name]];
}
}
else
{
if ([*valueP count] == 0)
{
EOEntity *destinationEntity = [self destinationEntity];
EOEntity *entity = [self entity];
exception = [NSException validationExceptionWithFormat:
@"The %@ property of %@ must have at least one %@",
[self name],
[entity name],
[destinationEntity name]];
}
}
}
return exception;
}
@end
@implementation EORelationship (EORelationshipPrivate)
/*
This method is private to GDL2 to allow the inverse relationship
to be set from the original relationship. It exists to avoid the
ASSIGN(inverseRelationship->_inverseRelationship, self);
and to insure that associations will be updated if we ever display
inverse relationships in DBModeler.
*/
- (void)_setInverseRelationship: (EORelationship*)relationship
{
[self willChange];
ASSIGN(_inverseRelationship,relationship);
}
@end
@implementation EORelationship (EORelationshipXX)
- (NSArray*) _intermediateAttributes
{
NSMutableArray *intermediateAttributes=[NSMutableArray array];
NSArray* firstRelJoins=[[self firstRelationship] joins];
NSArray* lastRelJoins=[[self lastRelationship] joins];
[intermediateAttributes addObjectsFromArray:
[firstRelJoins resultsOfPerformingSelector:
@selector(destinationAttribute)]];
[intermediateAttributes addObjectsFromArray:
[lastRelJoins resultsOfPerformingSelector:
@selector(sourceAttribute)]];
return [NSArray arrayWithArray: intermediateAttributes];
}
/** Return the last relationship if self is flattened, self otherwise.
**/
- (EORelationship*) lastRelationship
{
EORelationship *lastRel;
if ([self isFlattened])
{
NSAssert(!_definitionArray || [_definitionArray count] > 0,
@"Definition array is empty");
lastRel = [[self _definitionArray] lastObject];
}
else
lastRel = self;
return lastRel;
}
/** Return the 1st relationship if self is flattened, self otherwise.
**/
- (EORelationship*) firstRelationship
{
EORelationship *firstRel;
if ([self isFlattened])
{
NSAssert(!_definitionArray || [_definitionArray count] > 0,
@"Definition array is empty");
firstRel = [[self _definitionArray] objectAtIndex: 0];
}
else
firstRel = self;
return firstRel;
}
- (EOEntity*) intermediateEntity
{
id intermediateEntity = nil;
if ([self isToManyToOne])
{
int i, count = [_definitionArray count];
for (i = (count - 1); !intermediateEntity && i >= 0; i--)
{
EORelationship *rel = [_definitionArray objectAtIndex: i];
if ([rel isToMany])
intermediateEntity = [rel destinationEntity];
}
}
return intermediateEntity;
}
- (BOOL) isMultiHop
{
return [self isFlattened];
}
- (void) _setSourceToDestinationKeyMap: (NSDictionary*)sourceToDestinationKeyMap
{
ASSIGN(_sourceToDestinationKeyMap,sourceToDestinationKeyMap);
}
- (EOQualifier*) qualifierForDBSnapshot: (NSDictionary*)dbSnapshot
{
EOQualifier* qualifier = nil;
EORelationship* relationship = self;
NSMutableArray* qualifiers = nil;
BOOL isFlattenedToMany = NO;
NSString* relationshipPath = nil;
if([self isFlattened])
{
if ([self isToMany])
{
relationshipPath = [[self anyInverseRelationship]relationshipPath];
isFlattenedToMany = true;
relationship = [self firstRelationship];
}
else
relationship = [self lastRelationship];
}
NSDictionary* sourceToDestinationKeyMap = [self _sourceToDestinationKeyMap];
NSArray* sourceKeys = [sourceToDestinationKeyMap objectForKey:@"sourceKeys"];
NSArray* destinationKeys = [sourceToDestinationKeyMap objectForKey:@"destinationKeys"];
NSArray* joins = [relationship joins];
int joinsCount=[joins count];
int i = 0;
for(i=0;i<joinsCount;i++)
{
EOJoin* join = [joins objectAtIndex:i];
NSString* attrName=nil;
NSString* qualifierKey=nil;
id qualifierValue=nil;
if (isFlattenedToMany)
{
attrName = [[join sourceAttribute] name];
qualifierKey = [[relationshipPath stringByAppendingString:@"."]stringByAppendingString:attrName];
}
else
{
qualifierKey = [[join destinationAttribute] name];
attrName = [sourceKeys objectAtIndex:[destinationKeys indexOfObject:qualifierKey]];
}
qualifierValue = [dbSnapshot objectForKey:attrName];
if(qualifierValue != nil)
{
EOQualifier* q = [EOKeyValueQualifier qualifierWithKey:qualifierKey
operatorSelector:EOQualifierOperatorEqual
value:qualifierValue];
if (qualifiers == nil)
qualifiers = [NSMutableArray array];
[qualifiers addObject:q];
}
}
if([qualifiers count]>0)
{
if ([qualifiers count] > 1)
qualifier = [EOAndQualifier qualifierWithQualifierArray:qualifiers];
else
qualifier = [qualifiers objectAtIndex:0];
}
return qualifier;
}
- (NSDictionary*) primaryKeyForTargetRowFromSourceDBSnapshot:(NSDictionary*)dbSnapshot
{
NSDictionary* sourceToDestinationKeyMap = [self _sourceToDestinationKeyMap];
NSArray* sourceKeys = [sourceToDestinationKeyMap objectForKey:@"sourceKeys"];
NSArray* destinationKeys = [sourceToDestinationKeyMap objectForKey:@"destinationKeys"];
NSMutableDictionary* pk = [NSMutableDictionary dictionaryWithDictionary:dbSnapshot
keys:sourceKeys];
[pk translateFromKeys:sourceKeys
toKeys:destinationKeys];
return pk;
}
/** Return relationship path (like toRel1.toRel2) if self is flattened, slef name otherwise.
**/
- (NSString*)relationshipPath
{
NSString *relationshipPath = nil;
if ([self isFlattened])
{
int i, count = [_definitionArray count];
for (i = 0; i < count; i++)
{
EORelationship *relationship = [_definitionArray objectAtIndex: i];
NSString *relationshipName = [relationship name];
if (relationshipPath)
[(NSMutableString*)relationshipPath appendString: @"."];
else
relationshipPath = [NSMutableString string];
[(NSMutableString*)relationshipPath appendString: relationshipName];
}
}
else
relationshipPath = [self name];
return relationshipPath;
}
-(BOOL)isToManyToOne
{
BOOL isToManyToOne = NO;
if ([self isFlattened])
{
int l=0;
int count = [_definitionArray count];
int i=0;
for(i=0;i<count;i++)
{
EORelationship* relationship = [_definitionArray objectAtIndex: i];
switch(l)
{
case 0:
if([relationship isToMany])
l = 1;
else if([relationship isParentRelationship])
return NO;
break;
case 1:
if([relationship isToMany]
|| [[relationship anyInverseRelationship]isParentRelationship])
return NO;
else
l = 2;
break;
case 2:
if([relationship isToMany]
|| ![[relationship anyInverseRelationship] isParentRelationship])
return NO;
break;
default:
break;
}
}
if (l==2)
isToManyToOne=YES;
}
return isToManyToOne;
}
-(NSDictionary*)_sourceToDestinationKeyMap
{
if (!_sourceToDestinationKeyMap)
{
NSString *relationshipPath = [self relationshipPath];
ASSIGN(_sourceToDestinationKeyMap,
[_entity _keyMapForRelationshipPath: relationshipPath]);
}
return _sourceToDestinationKeyMap;
}
- (BOOL)foreignKeyInDestination
{
BOOL foreignKeyInDestination = NO;
if([self isToMany])
foreignKeyInDestination=YES;
else
{
NSArray* sourceAttributes = [self sourceAttributes];
NSArray* pkAttributes = [_entity primaryKeyAttributes];
int sourceAttributesCount=[sourceAttributes count];
int pkAttributesCount=[pkAttributes count];
if (sourceAttributesCount==pkAttributesCount)
{
foreignKeyInDestination=YES;
int i=0;
for(i=0;foreignKeyInDestination && i<sourceAttributesCount;i++)
{
EOAttribute* attribute = [sourceAttributes objectAtIndex:i];
if ([pkAttributes indexOfObjectIdenticalTo:attribute] == NSNotFound)
foreignKeyInDestination=NO;
}
}
}
return foreignKeyInDestination;
}
@end
@implementation EORelationship (EORelationshipPrivate2)
- (BOOL) isPropagatesPrimaryKeyPossible
{
BOOL isPropagatesPrimaryKeyPossible=NO;
NSArray* joins = [self joins];
NSArray* sourceAttributes = [joins resultsOfPerformingSelector:@selector(sourceAttribute)];
//MGuesdon: why ordering names ?
NSSet* sourceAttributeNames = [NSSet setWithArray:[[sourceAttributes resultsOfPerformingSelector:@selector(name)]
sortedArrayUsingSelector:@selector(compare:)]];
NSSet* sourcePKAttributeNames = [NSSet setWithArray:[_entity primaryKeyAttributeNames]];
if([sourceAttributeNames isSubsetOfSet:sourcePKAttributeNames])
{
NSArray* destinationAttributes = [joins resultsOfPerformingSelector:@selector(destinationAttribute)];
//MGuesdon: why ordering names ?
NSSet* destinationAttributeNames = [NSSet setWithArray:[[destinationAttributes resultsOfPerformingSelector:@selector(name)]
sortedArrayUsingSelector:@selector(compare:)]];
NSSet* destinationPKAttributeNames = [NSSet setWithArray:[[self destinationEntity]primaryKeyAttributeNames]];
if ([destinationAttributeNames isSubsetOfSet:destinationPKAttributeNames])
{
isPropagatesPrimaryKeyPossible=[[self inverseRelationship] propagatesPrimaryKey];
}
}
return isPropagatesPrimaryKeyPossible;
};
- (EOQualifier*) qualifierOmittingAuxiliaryQualifierWithSourceRow: (NSDictionary*)sourceRow
{
return [self qualifierForDBSnapshot:sourceRow];
}
- (EOQualifier*) auxiliaryQualifier
{
return _qualifier;
}
- (void) setAuxiliaryQualifier: (EOQualifier*)qualifier
{
DESTROY(_qualifier);
if (qualifier)
_qualifier=[qualifier copy];
}
/** Return dictionary of key/value for destination object of source row/object **/
- (EOMutableKnownKeyDictionary *) _foreignKeyForSourceRow: (NSDictionary*)row
{
return [EOMutableKnownKeyDictionary dictionaryFromDictionary: row
subsetMapping:[self _sourceRowToForeignKeyMapping]];
}
- (EOMKKDSubsetMapping*) _sourceRowToForeignKeyMapping
{
if (!_sourceRowToForeignKeyMapping)
{
NSDictionary *sourceToDestinationKeyMap =
[self _sourceToDestinationKeyMap];
NSArray *sourceKeys =
[sourceToDestinationKeyMap objectForKey: @"sourceKeys"];
NSArray *destinationKeys =
[sourceToDestinationKeyMap objectForKey: @"destinationKeys"];
EOEntity *destinationEntity =
[self destinationEntity];
EOMKKDInitializer *destinationDictionaryInitializer =
[destinationEntity _adaptorDictionaryInitializer];
EOMKKDInitializer *adaptorDictionaryInitializer =
[_entity _adaptorDictionaryInitializer];
EOMKKDSubsetMapping *sourceRowToForeignKeyMapping =
[destinationDictionaryInitializer subsetMappingForSourceDictionaryInitializer: adaptorDictionaryInitializer
sourceKeys: sourceKeys
destinationKeys: destinationKeys];
NSAssert3(sourceRowToForeignKeyMapping!=nil,
@"Unable to map destination %@ for relationship %@ in entity %@. To one relationships must be joined on the primary key of the destination.",
[destinationEntity name],
[self name],
[_entity name]);
ASSIGN(_sourceRowToForeignKeyMapping, sourceRowToForeignKeyMapping);
}
return _sourceRowToForeignKeyMapping;
}
- (NSArray*) _sourceAttributeNames
{
//MGuesdon: if flattened, EOF returns firstRelationship sourceAttribute names
//which is incoherent with -sourceAttributes
//Here we return sourceAttribute names, flattened or not
return [[self sourceAttributes]
resultsOfPerformingSelector: @selector(name)];
}
- (EOJoin*) joinForAttribute: (EOAttribute*)attribute
{
EOJoin *join = nil;
int count = [_joins count];
if (count>0)
{
int i=0;
for (i = 0; !join && i < count; i++)
{
EOJoin *aJoin = [_joins objectAtIndex: i];
if ([attribute isEqual: [aJoin sourceAttribute]]
|| [attribute isEqual: [aJoin destinationAttribute]])
{
join = aJoin;
}
}
}
return join;
}
- (void) _flushCache
{
DESTROY(_sourceAttributes);
DESTROY(_destinationAttributes);
EORelationship* inverseRelationship=AUTORELEASE(RETAIN(_inverseRelationship));
DESTROY(_inverseRelationship);
if (inverseRelationship!=nil)
[inverseRelationship _flushCache];
if (_hiddenInverseRelationship!=nil)
{
[[[self destinationEntity]_hiddenRelationships] removeObjectIdenticalTo:_hiddenInverseRelationship];
DESTROY(_hiddenInverseRelationship);
}
DESTROY(_sourceRowToForeignKeyMapping);
_destination = nil;
}
- (EOExpressionArray*) _definitionArray
{
return _definitionArray;
}
- (NSString*) _stringFromDeleteRule: (EODeleteRule)deleteRule
{
NSString *deleteRuleString = nil;
switch(deleteRule)
{
case EODeleteRuleNullify:
deleteRuleString = @"EODeleteRuleNullify";
break;
case EODeleteRuleCascade:
deleteRuleString = @"EODeleteRuleCascade";
break;
case EODeleteRuleDeny:
deleteRuleString = @"EODeleteRuleDeny";
break;
case EODeleteRuleNoAction:
deleteRuleString = @"EODeleteRuleNoAction";
break;
default:
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: invalid deleteRule code for relationship '%@': %d",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
(int)deleteRule];
break;
}
return deleteRuleString;
}
- (EODeleteRule) _deleteRuleFromString: (NSString*)deleteRuleString
{
EODeleteRule deleteRule = 0;
if ([deleteRuleString isEqualToString: @"EODeleteRuleNullify"])
deleteRule = EODeleteRuleNullify;
else if ([deleteRuleString isEqualToString: @"EODeleteRuleCascade"])
deleteRule = EODeleteRuleCascade;
else if ([deleteRuleString isEqualToString: @"EODeleteRuleDeny"])
deleteRule = EODeleteRuleDeny;
else if ([deleteRuleString isEqualToString: @"EODeleteRuleNoAction"])
deleteRule = EODeleteRuleNoAction;
else
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%p: invalid deleteRule string for relationship '%@': %@",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
deleteRuleString];
return deleteRule;
}
- (NSDictionary*) _rightSideKeyMap
{
NSDictionary *keyMap = nil;
if ([self isToManyToOne])
{
int definitionArrayCount=[_definitionArray count];
EOEntity* entity = nil;
NSMutableString* relationshipPath = nil;
int k = 0;
int i = 0;
for(i=0; i < definitionArrayCount; i++)
{
EORelationship* relationship = [_definitionArray objectAtIndex:i];
switch(k)
{
case 0:
if([relationship isToMany])
{
k = 1;
entity=[relationship destinationEntity];
}
break;
case 1:
if (relationshipPath)
[relationshipPath appendString: @"."];
else
relationshipPath = [NSMutableString string];
[relationshipPath appendString: [relationship name]];
break;
default:
break;
}
}
keyMap=[entity _keyMapForIdenticalKeyRelationshipPath:relationshipPath];
}
return keyMap;
}
- (NSDictionary *) _leftSideKeyMap
{
NSDictionary *keyMap = nil;
if ([self isToManyToOne])
{
int definitionArrayCount=[_definitionArray count];
NSMutableString* relationshipPath = nil;
int i = 0;
for(i=0; i < definitionArrayCount; i++)
{
EORelationship* relationship = [_definitionArray objectAtIndex:i];
if (relationshipPath)
[relationshipPath appendString: @"."];
else
relationshipPath = [NSMutableString string];
[relationshipPath appendString: [relationship name]];
if ([relationship isToMany])
break;
}
keyMap=[[self entity]_keyMapForIdenticalKeyRelationshipPath:relationshipPath];
}
return keyMap;
}
- (EORelationship*)_substitutionRelationshipForRow: (NSDictionary*)row
{
EOEntity* entity = [self entity];
EOModelGroup* modelGroup = [[entity model]modelGroup];
EORelationship* relationship = self;
if(modelGroup != nil
&& modelGroup->_delegateRespondsTo.relationshipForRow)
{
relationship = [[modelGroup delegate] entity:entity
relationshipForRow:row
relationship:self];
}
return relationship;
}
- (void) _joinsChanged
{
if ([_joins count] > 0)
{
EOJoin *join = [_joins objectAtIndex: 0];
EOAttribute *destinationAttribute = [join destinationAttribute];
EOEntity *destinationEntity = [destinationAttribute entity];
_destination = destinationEntity;
}
else
{
_destination = nil;
}
}
@end