libs-gdl2/EOAccess/EORelationship.m
David Ayers 5107816c46 * EOAccess/EORelationship.m (-[isToManyToOne]): Correct index
used to retrieved second relationship.
	Bug #29369


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@30080 72102866-910b-0410-8b05-ffd578937521
2010-03-30 09:38:12 +00:00

2352 lines
64 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"
RCS_ID("$Id$")
#ifdef GNUSTEP
#include <Foundation/NSArray.h>
#include <Foundation/NSException.h>
#include <Foundation/NSDebug.h>
#else
#include <Foundation/Foundation.h>
#endif
#ifndef GNUSTEP
#include <GNUstepBase/GNUstep.h>
#include <GNUstepBase/GSObjCRuntime.h>
#include <GNUstepBase/GSCategories.h>
#endif
#include <EOControl/EOObserver.h>
#include <EOControl/EOMutableKnownKeyDictionary.h>
#include <EOControl/EONSAddOns.h>
#include <EOControl/EODebug.h>
#include <EOAccess/EOModel.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();
}
}
+ (id) relationshipWithPropertyList: (NSDictionary *)propertyList
owner: (id)owner
{
return AUTORELEASE([[self alloc] initWithPropertyList: propertyList
owner: owner]);
}
- (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];
}
- (unsigned)hash
{
return [_name hash];
}
- (id) initWithPropertyList: (NSDictionary *)propertyList
owner: (id)owner
{
//Near OK
if ((self = [self init]))
{
NSString *joinSemanticString = nil;
EOModel *model;
NSString* destinationEntityName = nil;
EOEntity* destinationEntity = nil;
NSString* deleteRuleString = nil;
NSString* relationshipName;
EOFLOGObjectFnStart();
model = [owner model];
relationshipName = [propertyList objectForKey: @"name"];
/* 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)
{
if ([joinSemanticString isEqual: @"EOInnerJoin"])
[self setJoinSemantic: EOInnerJoin];
else if ([joinSemanticString isEqual: @"EOFullOuterJoin"])
[self setJoinSemantic: EOFullOuterJoin];
else if ([joinSemanticString isEqual: @"EOLeftOuterJoin"])
[self setJoinSemantic: EOLeftOuterJoin];
else if ([joinSemanticString isEqual: @"EORightOuterJoin"])
[self setJoinSemantic: EORightOuterJoin];
else
{
EOFLOGObjectLevelArgs(@"EORelationship", @"Unknown joinSemanticString=%@. entityName=%@ relationshipName=%@",
joinSemanticString,
[(EOEntity*)owner name],
relationshipName);
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; //TODO
}
}
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 < 4,
@"Bad deleteRule numeric value: %@ (%d)",
deleteRuleString,
deleteRule);
[self setDeleteRule: deleteRule];
}
}
EOFLOGObjectFnStop();
return self;
}
- (void)awakeWithPropertyList: (NSDictionary *)propertyList //TODO
{
//OK for definition
NSString *definition;
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EORelationship", @"self=%@", self);
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;
NSString *joinSemantic;
NSString *sourceAttributeName;
EOAttribute *sourceAttribute;
EOEntity *destinationEntity;
NSString *destinationAttributeName = nil;
EOAttribute *destinationAttribute = nil;
EOJoin *join = nil;
joinPList = [joins objectAtIndex: i];
joinSemantic = [joinPList objectForKey: @"joinSemantic"];
sourceAttributeName = [joinPList objectForKey:
@"sourceAttribute"];
sourceAttribute = [_entity attributeNamed:
sourceAttributeName];
NSAssert4(sourceAttribute, @"No sourceAttribute named \"%@\" in entity \"%@\" in relationship %@\nEntity: %@",
sourceAttributeName,
[_entity name],
self,
_entity);
destinationEntity = [self destinationEntity];
NSAssert3(destinationEntity,@"No destination entity for relationship named '%@' in entity named '%@': %@",
[self name],
[[self entity]name],
self);
destinationAttributeName = [joinPList
objectForKey:
@"destinationAttribute"];
destinationAttribute = [destinationEntity
attributeNamed:
destinationAttributeName];
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%x: 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
};
*/
EOFLOGObjectFnStop();
}
- (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])
{
NSEmitTODO(); //TODO
}
if ([self propagatesPrimaryKey])
{
NSEmitTODO(); //TODO
}
{
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_get_class_name(self),
(void*)self,
[self name],
[[self entity]name],
[[self destinationEntity] name],
[self definition]];
dscr = [dscr stringByAppendingFormat: @" userInfo=%@",
[self userInfo]];
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];
EOEntity *parentEntity = [_entity parentEntity];*///nil
NSEmitTODO(); //TODO
// [self notImplemented:_cmd]; //TODO...
return isParentRelationship;
}
/** Returns YES when the relationship traverses at least two entities
(exemple: aRelationship.anotherRelationship), NO otherwise.
**/
- (BOOL)isFlattened
{
if (_definitionArray)
return [_definitionArray isFlattened];
else
return NO;
}
/** 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
{
//OK
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
{
//OK
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;
}
- (NSString*)joinSemanticString
{
NSString *joinSemanticString = nil;
switch ([self joinSemantic])
{
case EOInnerJoin:
joinSemanticString = @"EOInnerJoin";
break;
case EOFullOuterJoin:
joinSemanticString = @"EOFullOuterJoin";
break;
case EOLeftOuterJoin:
joinSemanticString = @"EOLeftOuterJoin";
break;
case EORightOuterJoin:
joinSemanticString = @"EORightOuterJoin";
break;
default:
NSAssert1(NO, @"Unknwon join semantic code %d",
(int)[self joinSemantic]);
break;
}
return joinSemanticString;
}
/**
* Returns the array of relationships composing this flattend relationship.
* Returns nil of the reciever isn't flattend.
*/
- (NSArray *)componentRelationships
{
/* FIXME:TODO: Have this method deterimne the components dynamically
without caching them in the ivar. Possibly add some tracing code to
see if caching the values can actually improve performance.
(Unlikely that it's worth the trouble this may cause for entity
edititng). */
if (!_componentRelationships)
{
return _definitionArray; //OK ??????
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; //TODO
}
return _componentRelationships;
}
- (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
{
EOFLOGObjectLevelArgs(@"EORelationship", @"EORelationship %p", self);
NSEmitTODO(); //TODO
// return [self notImplemented:_cmd]; //TODO
//return name ??
return [self name];
}
- (BOOL)referencesProperty: (id)property
{
BOOL referencesProperty = NO;
NSArray *srcAttribs;
NSArray *destAttribs;
NSArray *compRels;
if (property == nil)
return NO;
destAttribs = [self destinationAttributes];
srcAttribs = [self sourceAttributes];
compRels = [self componentRelationships];
EOFLOGObjectLevelArgs(@"EORelationship", @"in referencesProperty:%@",
property);
referencesProperty =
((srcAttribs
&& [srcAttribs indexOfObject: property] != NSNotFound)
|| (destAttribs
&& [destAttribs indexOfObject: property] != NSNotFound)
|| (compRels
&& [compRels indexOfObject: property] != NSNotFound)
|| (_destination == property));
return referencesProperty;
}
- (EODeleteRule)deleteRule
{
EOFLOGObjectFnStart();
EOFLOGObjectFnStop();
return _flags.deleteRule;
}
- (BOOL)isMandatory
{
return _flags.isMandatory;
}
- (BOOL)propagatesPrimaryKey
{
return _flags.propagatesPrimaryKey;
}
- (BOOL)isBidirectional
{
return _flags.isBidirectional;
}
- (BOOL)isReciprocalToRelationship: (EORelationship *)relationship
{
//Should be OK
//Ayers: Review
BOOL isReciprocal = NO;
EOEntity *entity;
EOEntity *relationshipDestinationEntity = nil;
EOFLOGObjectFnStart();
entity = [self entity]; //OK
relationshipDestinationEntity = [relationship destinationEntity];
EOFLOGObjectLevelArgs(@"EORelationship", @"entity %p name=%@",
entity, [entity name]);
EOFLOGObjectLevelArgs(@"EORelationship",
@"relationshipDestinationEntity %p name=%@",
relationshipDestinationEntity,
[relationshipDestinationEntity name]);
if (entity == relationshipDestinationEntity) //Test like that ?
{
if ([self isFlattened]) //OK
{
if ([relationship isFlattened]) //OK
{
//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
{
//Just do nothing and try another relationship.
// Is it the good way ?
/*
NSEmitTODO(); //TODO
NSDebugMLog(@"entity %p name=%@ self name=%@ relationship name=%@ relationshipDestinationEntity %p name=%@",
entity, [entity name],
[self name],
[relationship name],
relationshipDestinationEntity,
[relationshipDestinationEntity name]);
[self notImplemented: _cmd]; //TODO
*/
}
}
else
{
//WO doens't test inverses entity
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;
}
}
}
}
EOFLOGObjectFnStop();
return isReciprocal;
}
/** "Search only already created inverse relationship in destination entity
relationships. Nil if none" **/
- (EORelationship *)inverseRelationship
{
//OK
EOFLOGObjectFnStart();
if (!_inverseRelationship)
{
EOEntity *destinationEntity;
NSArray *destinationEntityRelationships;
destinationEntity = [self destinationEntity];
NSDebugLog(@"destinationEntity name=%@", [destinationEntity name]);
destinationEntityRelationships = [destinationEntity relationships];
NSDebugLog(@"destinationEntityRelationships=%@",
destinationEntityRelationships);
if ([destinationEntityRelationships count] > 0)
{
int i, count = [destinationEntityRelationships count];
for (i = 0; !_inverseRelationship && i < count; i++)
{
EORelationship *testRelationship =
[destinationEntityRelationships objectAtIndex: i];
NSDebugLog(@"testRelationship=%@", testRelationship);
if ([self isReciprocalToRelationship: testRelationship])
{
ASSIGN(_inverseRelationship, testRelationship);
}
}
}
NSDebugLog(@"_inverseRelationship=%@", _inverseRelationship);
}
EOFLOGObjectFnStop();
return _inverseRelationship;
}
- (EORelationship *) _makeFlattenedInverseRelationship
{
//OK
EORelationship *inverseRelationship = nil;
NSMutableString *invDefinition = nil;
NSString *name = nil;
int i, count;
EOFLOGObjectFnStart();
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];
EOFLOGObjectFnStop();
return inverseRelationship;
}
- (EORelationship*) _makeInverseRelationship
{
//OK
EORelationship *inverseRelationship;
NSString *name;
NSArray *joins = nil;
unsigned int i, count;
EOFLOGObjectFnStart();
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];
EOFLOGObjectFnStop();
return inverseRelationship;
}
- (EORelationship*) hiddenInverseRelationship
{
//OK
EOFLOGObjectFnStart();
if (!_hiddenInverseRelationship)
{
if ([self isFlattened])
_hiddenInverseRelationship = [self _makeFlattenedInverseRelationship];
else
_hiddenInverseRelationship = [self _makeInverseRelationship];
}
EOFLOGObjectFnStop();
return _hiddenInverseRelationship;
}
- (EORelationship *)anyInverseRelationship
{
//OK
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
{
[self notImplemented: _cmd];//TODO
return nil;
}
@end /* EORelationship */
@implementation EORelationship (EORelationshipEditing)
- (NSException *)validateName: (NSString *)name
{
//Seems OK
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%x: 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%x: \"%@\" already used in the model",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
name]
userInfo: nil];
}
return nil;
}
- (void)setToMany: (BOOL)flag
{
//OK
if ([self isFlattened])
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: 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
{
//OK
[[self validateName: name] raise];
[self willChange];
[_entity _setIsEdited];
ASSIGNCOPY(_name, name);
}
- (void)setDefinition: (NSString *)definition
{
//Near OK
EOFLOGObjectFnStart();
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: [EORelationship class]])
{
if ([rel isToMany])
_flags.isToMany = YES;
}
else
break;
}
}
}
else /* definition == nil */
{
DESTROY(_definitionArray);
}
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
EOFLOGObjectFnStop();
}
/**
* <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
{
//OK
if (entity != _entity)
{
[self _flushCache];
[self willChange];
if (_entity)
{
NSString *relationshipName;
EORelationship *relationship;
/* Check if we are still in the entities arrays to
avoid recursive loop when removeRelationship:
calls this method. */
relationshipName = [self name];
relationship = [_entity relationshipNamed: relationshipName];
if (self == relationship)
{
[_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
{
//OK
[self willChange];
ASSIGN(_userInfo, dictionary);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setInternalInfo: (NSDictionary *)dictionary
{
//OK
[self willChange];
ASSIGN(_internalInfo, dictionary);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setDocComment: (NSString *)docComment
{
//OK
[self willChange];
ASSIGNCOPY(_docComment, docComment);
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
- (void)setPropagatesPrimaryKey: (BOOL)flag
{
//OK
if (_flags.propagatesPrimaryKey != flag)
[self willChange];
_flags.propagatesPrimaryKey = flag;
}
- (void)setIsBidirectional: (BOOL)flag
{
//OK
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
{
EOAttribute *sourceAttribute = nil;
EOAttribute *destinationAttribute = nil;
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EORelationship", @"Add join: %@\nto %@", join, self);
if ([self isFlattened] == YES)
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: receiver is a flattened relationship",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
else
{
EOEntity *destinationEntity = [self destinationEntity];
EOEntity *sourceEntity = [self entity];
EOFLOGObjectLevelArgs(@"EORelationship", @"destinationEntity=%@", destinationEntity);
if (!destinationEntity)
{
NSEmitTODO(); //TODO
EOFLOGObjectLevelArgs(@"EORelationship", @"self=%@", self);
//TODO ??
};
sourceAttribute = [join sourceAttribute];
NSAssert3(sourceAttribute, @"No source attribute in join %@ in relationship %@ of entity %@",
join,
self,
sourceEntity);
destinationAttribute = [join destinationAttribute];
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%x: 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%x (%@): 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%x (%@): 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%x: TODO",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
}
[self _flushCache];
[self willChange];
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];
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
}
}
EOFLOGObjectFnStop();
}
- (void)removeJoin: (EOJoin *)join
{
EOFLOGObjectFnStart();
[self _flushCache];
if ([self isFlattened] == YES)
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: receiver is a flattened relationship",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
else
{
[self willChange];
[(NSMutableArray *)_joins removeObject: join];
/*NO: will be recomputed [(NSMutableArray *)_sourceAttributes
removeObject:[join sourceAttribute]];
[(NSMutableArray *)_destinationAttributes
removeObject:[join destinationAttribute]];
*/
EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@",
_joins, [_joins class]);
[self _joinsChanged];
/* Ayers: Not sure what justifies this. */
[_entity _setIsEdited];
}
EOFLOGObjectFnStop();
}
- (void)setJoinSemantic: (EOJoinSemantic)joinSemantic
{
//OK
[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];
_batchCount = size;
}
- (void)setDeleteRule: (EODeleteRule)deleteRule
{
NSAssert1(deleteRule >= 0 && deleteRule < 4,
@"Bad deleteRule numeric value: %d",
deleteRule);
[self willChange];
_flags.deleteRule = deleteRule;
}
- (void)setIsMandatory: (BOOL)isMandatory
{
//OK
[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
{
//OK
NSException *exception = nil;
EOFLOGObjectFnStart();
NSAssert(valueP, @"No value pointer");
if ([self isMandatory])
{
BOOL isToMany = [self isToMany];
if ((isToMany == NO && _isNilOrEONull(*valueP))
|| (isToMany == YES && [*valueP count] == 0))
{
EOEntity *destinationEntity = [self destinationEntity];
EOEntity *entity = [self entity];
exception = [NSException validationExceptionWithFormat:
@"The %@ property of %@ must have a %@ assigned",
[self name],
[entity name],
[destinationEntity name]];
}
}
EOFLOGObjectFnStop();
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
{
//Verify !!
NSMutableArray *intermediateAttributes;
EORelationship *rel;
NSArray *joins;
//all this works on flattened and non flattened relationship.
intermediateAttributes = [NSMutableArray array];
rel = [self firstRelationship];
joins = [rel joins];
//??
[intermediateAttributes addObjectsFromArray:
[joins resultsOfPerformingSelector:
@selector(destinationAttribute)]];
rel = [self lastRelationship];
joins = [rel joins];
// attribute = [joins sourceAttribute];
//??
[intermediateAttributes addObjectsFromArray:
[joins 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
{
//TODO verify
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
{
//TODO verify
BOOL isMultiHop = NO;
if ([self isFlattened])
{
isMultiHop = YES;
}
return isMultiHop;
}
- (void) _setSourceToDestinationKeyMap: (id)param0
{
[self notImplemented: _cmd]; // TODO
}
- (id) qualifierForDBSnapshot: (id)param0
{
return [self notImplemented: _cmd]; // TODO
}
- (id) primaryKeyForTargetRowFromSourceDBSnapshot: (id)param0
{
return [self notImplemented:_cmd]; // TODO
}
/** Return relationship path (like toRel1.toRel2) if self is flattened, slef name otherwise.
**/
- (NSString*)relationshipPath
{
//Seems OK
NSString *relationshipPath = nil;
EOFLOGObjectFnStart();
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];
EOFLOGObjectFnStop();
return relationshipPath;
}
-(BOOL)isToManyToOne
{
BOOL isToManyToOne = NO;
EOFLOGObjectFnStart();
if ([self isFlattened])
{
BOOL isToMany = YES;
int count = [_definitionArray count];
if (count >= 2)
{
EORelationship *firstRelationship = [_definitionArray
objectAtIndex: 0];
isToMany = [firstRelationship isToMany];
if (!isToMany)
{
if ([firstRelationship isParentRelationship])
{
NSEmitTODO(); //TODO
EOFLOGObjectLevelArgs(@"EORelationship", @"self=%@", self);
EOFLOGObjectLevelArgs(@"EORelationship", @"firstRelationship=%@",
firstRelationship);
[self notImplemented: _cmd]; //TODO
}
}
if (isToMany)
{
EORelationship *secondRelationship = [_definitionArray
objectAtIndex: 1];
if (![secondRelationship isToMany])
{
EORelationship *invRel = [secondRelationship
anyInverseRelationship];
if (invRel)
secondRelationship = invRel;
isToManyToOne = YES;
if ([secondRelationship isParentRelationship])
{
NSEmitTODO(); //TODO
EOFLOGObjectLevelArgs(@"EORelationship", @"self=%@", self);
EOFLOGObjectLevelArgs(@"EORelationship", @"secondRelationship=%@",
secondRelationship);
[self notImplemented: _cmd]; //TODO
}
}
}
}
}
EOFLOGObjectFnStop();
return isToManyToOne;
}
-(NSDictionary*)_sourceToDestinationKeyMap
{
//OK
EOFLOGObjectFnStart();
if (!_sourceToDestinationKeyMap)
{
NSString *relationshipPath = [self relationshipPath];
ASSIGN(_sourceToDestinationKeyMap,
[_entity _keyMapForRelationshipPath: relationshipPath]);
}
EOFLOGObjectFnStop();
return _sourceToDestinationKeyMap;
}
- (BOOL)foreignKeyInDestination
{
NSArray *destAttributes = nil;
NSArray *primaryKeyAttributes = nil;
int destAttributesCount = 0;
int primaryKeyAttributesCount = 0;
BOOL foreignKeyInDestination = NO;
EOFLOGObjectFnStart();
destAttributes = [self destinationAttributes];
primaryKeyAttributes = [[self destinationEntity] primaryKeyAttributes];
destAttributesCount = [destAttributes count];
primaryKeyAttributesCount = [primaryKeyAttributes count];
EOFLOGObjectLevelArgs(@"EORelationship", @"destAttributes=%@",
destAttributes);
EOFLOGObjectLevelArgs(@"EORelationship", @"primaryKeyAttributes=%@",
primaryKeyAttributes);
if (destAttributesCount > 0 && primaryKeyAttributesCount > 0)
{
int i;
for (i = 0;
!foreignKeyInDestination && i < destAttributesCount;
i++)
{
EOAttribute *attribute = [destAttributes objectAtIndex: i];
int pkAttrIndex = [primaryKeyAttributes
indexOfObjectIdenticalTo: attribute];
foreignKeyInDestination = (pkAttrIndex == NSNotFound);
}
}
EOFLOGObjectFnStop();
EOFLOGObjectLevelArgs(@"EORelationship", @"foreignKeyInDestination=%s",
(foreignKeyInDestination ? "YES" : "NO"));
return foreignKeyInDestination;
}
@end
@implementation EORelationship (EORelationshipPrivate2)
- (BOOL) isPropagatesPrimaryKeyPossible
{
/*
NSArray* joins=[self joins];
NSArray* joinsSourceAttributes=[joins resultsOfPerformingSelector:@selector(sourceAttribute)];
NSArray* joinsDestinationAttributes=[joins resultsOfPerformingSelector:@selector(destinationAttribute)];
joinsSourceAttributes names
sortedArrayUsingSelector:compare:
result count
joinsDestinationAttributes names
sortedArrayUsingSelector:compare:
inverseRelationship
inv entity [EOEntity]:
inv ventity primaryKeyAttributeNames
count
dest entity
dst entity primaryKeyAttributeNames
*/
EOFLOGObjectFnStart();
[self notImplemented: _cmd]; // TODO
EOFLOGObjectFnStop();
return NO;
};
- (id) qualifierOmittingAuxiliaryQualifierWithSourceRow: (id)param0
{
return [self notImplemented: _cmd]; // TODO
}
- (id) auxiliaryQualifier
{
return nil; //[self notImplemented:_cmd]; // TODO
}
- (void) setAuxiliaryQualifier: (id)param0
{
[self notImplemented:_cmd]; // TODO
}
/** Return dictionary of key/value for destination object of source row/object **/
- (EOMutableKnownKeyDictionary *) _foreignKeyForSourceRow: (NSDictionary*)row
{
EOMutableKnownKeyDictionary *foreignKey = nil;
EOMKKDSubsetMapping *sourceRowToForeignKeyMapping = nil;
EOFLOGObjectFnStart();
sourceRowToForeignKeyMapping = [self _sourceRowToForeignKeyMapping];
EOFLOGObjectLevelArgs(@"EORelationship", @"self=%@",self);
EOFLOGObjectLevelArgs(@"EORelationship", @"sourceRowToForeignKeyMapping=%@",
sourceRowToForeignKeyMapping);
foreignKey = [EOMutableKnownKeyDictionary dictionaryFromDictionary: row
subsetMapping:
sourceRowToForeignKeyMapping];
EOFLOGObjectLevelArgs(@"EORelationship", @"row=%@\nforeignKey=%@", row, foreignKey);
EOFLOGObjectFnStop();
return foreignKey;
}
- (EOMKKDSubsetMapping*) _sourceRowToForeignKeyMapping
{
EOFLOGObjectFnStart();
if (!_sourceRowToForeignKeyMapping)
{
NSDictionary *sourceToDestinationKeyMap;
NSArray *sourceKeys;
NSArray *destinationKeys;
EOEntity *destinationEntity;
EOMKKDInitializer *destinationDictionaryInitializer = nil;
EOMKKDInitializer *adaptorDictionaryInitializer;
EOMKKDSubsetMapping *sourceRowToForeignKeyMapping;
sourceToDestinationKeyMap = [self _sourceToDestinationKeyMap];
EOFLOGObjectLevelArgs(@"EORelationship", @"rel=%@ sourceToDestinationKeyMap=%@",
[self name], sourceToDestinationKeyMap);
sourceKeys = [sourceToDestinationKeyMap objectForKey: @"sourceKeys"];
EOFLOGObjectLevelArgs(@"EORelationship", @"rel=%@ sourceKeys=%@",
[self name], sourceKeys);
destinationKeys = [sourceToDestinationKeyMap
objectForKey: @"destinationKeys"];
EOFLOGObjectLevelArgs(@"EORelationship", @"rel=%@ destinationKeys=%@",
[self name], destinationKeys);
destinationEntity = [self destinationEntity];
destinationDictionaryInitializer = [destinationEntity _adaptorDictionaryInitializer];
EOFLOGObjectLevelArgs(@"EORelationship", @"destinationEntity named %@ primaryKeyDictionaryInitializer=%@",
[destinationEntity name],
destinationDictionaryInitializer);
adaptorDictionaryInitializer = [_entity _adaptorDictionaryInitializer];
EOFLOGObjectLevelArgs(@"EORelationship",@"entity named %@ adaptorDictionaryInitializer=%@",
[_entity name],
adaptorDictionaryInitializer);
sourceRowToForeignKeyMapping =
[destinationDictionaryInitializer
subsetMappingForSourceDictionaryInitializer:
adaptorDictionaryInitializer
sourceKeys: sourceKeys
destinationKeys: destinationKeys];
ASSIGN(_sourceRowToForeignKeyMapping, sourceRowToForeignKeyMapping);
EOFLOGObjectLevelArgs(@"EORelationship",@"%@ to %@: _sourceRowToForeignKeyMapping=%@",
[_entity name],
[destinationEntity name],
_sourceRowToForeignKeyMapping);
}
EOFLOGObjectFnStop();
return _sourceRowToForeignKeyMapping;
}
- (NSArray*) _sourceAttributeNames
{
//Seems OK
return [[self sourceAttributes]
resultsOfPerformingSelector: @selector(name)];
}
- (EOJoin*) joinForAttribute: (EOAttribute*)attribute
{
//OK
EOJoin *join = nil;
int i, count = [_joins count];
for (i = 0; !join && i < count; i++)
{
EOJoin *aJoin = [_joins objectAtIndex: i];
EOAttribute *sourceAttribute = [aJoin sourceAttribute];
if ([attribute isEqual: sourceAttribute])
join = aJoin;
}
return join;
}
- (void) _flushCache
{
//VERIFY
//[self notImplemented:_cmd]; // TODO
DESTROY(_sourceAttributes);
DESTROY(_destinationAttributes);
DESTROY(_inverseRelationship);
DESTROY(_hiddenInverseRelationship);
DESTROY(_componentRelationships);
_destination = nil;
}
- (EOExpressionArray*) _definitionArray
{
//VERIFY
return _definitionArray;
}
- (NSString*) _stringFromDeleteRule: (EODeleteRule)deleteRule
{
NSString *deleteRuleString = nil;
switch(deleteRule)
{
case EODeleteRuleNullify:
deleteRuleString = @"";
break;
case EODeleteRuleCascade:
deleteRuleString = @"";
break;
case EODeleteRuleDeny:
deleteRuleString = @"";
break;
case EODeleteRuleNoAction:
deleteRuleString = @"";
break;
default:
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: 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%x: invalid deleteRule string for relationship '%@': %@",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
[self name],
deleteRuleString];
return deleteRule;
}
- (NSDictionary*) _rightSideKeyMap
{
NSDictionary *keyMap = nil;
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; // TODO
if ([self isToManyToOne])
{
int count = [_definitionArray count];
if (count >= 2) //??
{
EORelationship *rel0 = [_definitionArray objectAtIndex: 0];
if ([rel0 isToMany]) //??
{
EOEntity *entity = [rel0 destinationEntity];
EORelationship *rel1 = [_definitionArray objectAtIndex: 1];
keyMap = [entity _keyMapForIdenticalKeyRelationshipPath:
[rel1 name]];
}
}
}
return keyMap;
}
- (NSDictionary *) _leftSideKeyMap
{
NSDictionary *keyMap = nil;
NSEmitTODO(); //TODO
[self notImplemented: _cmd]; // TODO
if ([self isToManyToOne])
{
int count = [_definitionArray count];
if (count >= 2) //??
{
EORelationship *rel = [_definitionArray objectAtIndex: 0];
if ([rel isToMany]) //??
{
EOEntity *entity = [rel entity];
keyMap = [entity _keyMapForIdenticalKeyRelationshipPath:
[rel name]];
}
}
}
return keyMap;
}
- (EORelationship*)_substitutionRelationshipForRow: (NSDictionary*)row
{
EOEntity *entity = [self entity];
EOModel *model = [entity model];
EOModelGroup *modelGroup = [model modelGroup];
if (modelGroup)
{
//??
//NSEmitTODO(); //TODO
}
return self;
}
- (void) _joinsChanged
{
//VERIFIED DA
int count = [_joins count];
EOFLOGObjectFnStart();
EOFLOGObjectLevelArgs(@"EORelationship", @"_joinsChanged:%@\nin %@", _joins, self);
if (count > 0)
{
EOJoin *join = [_joins objectAtIndex: 0];
EOAttribute *destinationAttribute = [join destinationAttribute];
EOEntity *destinationEntity = [destinationAttribute entity];
_destination = destinationEntity;
}
else
{
_destination = nil;
}
EOFLOGObjectFnStop();
}
@end