mirror of
https://github.com/gnustep/libs-gdl2.git
synced 2025-02-16 00:11:15 +00:00
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
2255 lines
62 KiB
Objective-C
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
|