/** EORelationship.m EORelationship Copyright (C) 2000-2002,2003,2004,2005 Free Software Foundation, Inc. Author: Mirko Viviani Date: February 2000 Author: Manuel Guesdon Date: October 2000 $Revision$ $Date$ This file is part of the GNUstep Database Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 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. **/ #include "config.h" RCS_ID("$Id$") #ifdef GNUSTEP #include #include #include #else #include #endif #ifndef GNUSTEP #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "EOPrivate.h" #include "EOAttributePriv.h" #include "EOEntityPriv.h" @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 { DESTROY(_name); DESTROY(_qualifier); DESTROY(_sourceNames); DESTROY(_destinationNames); DESTROY(_userInfo); DESTROY(_internalInfo); DESTROY(_docComment); DESTROY(_sourceToDestinationKeyMap); DESTROY(_sourceRowToForeignKeyMapping); [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"]; [self setName: relationshipName]; [self setEntity: owner]; [self setCreateMutableObjects: YES]; destinationEntityName = [propertyList objectForKey: @"destination"]; if (destinationEntityName) //If not, this is because it's a definition { destinationEntity = [model entityNamed: destinationEntityName]; GDL2DestinationEntitiesRemoveRelationship(_destination, self); GDL2DestinationEntitiesAddRelationship(destinationEntity, self); _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 }; */ [self setCreateMutableObjects: NO]; //?? tc say yes, mirko no 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 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; } - (NSArray *)componentRelationships { 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 = [self sourceAttributes]; NSArray *destAttribs = [self destinationAttributes]; NSArray *compRels = [self componentRelationships]; NSEmitTODO(); //TODO EOFLOGObjectLevelArgs(@"EORelationship", @"in referencesProperty:%@", property); referencesProperty = ((srcAttribs && [srcAttribs indexOfObject: property] != NSNotFound) || (destAttribs && [destAttribs indexOfObject: property] != NSNotFound) || (compRels && [compRels indexOfObject: property] != NSNotFound)); 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; int i, count; EOFLOGObjectFnStart(); NSAssert(![self isFlattened], @"Flatten Relationship"); inverseRelationship = [[EORelationship new] autorelease]; [inverseRelationship setEntity: _destination]; 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]; 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] anyAttributeNamed: 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]; [self willChange]; _flags.isToMany = flag; } - (void)setName: (NSString *)name { //OK [[self validateName: name] raise]; [self willChange]; [_entity _setIsEdited]; ASSIGN(_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]); GDL2DestinationEntitiesRemoveRelationship(_destination, self); _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(); } - (void)setEntity: (EOEntity *)entity { //OK if (entity != _entity) { [self _flushCache]; [self willChange]; /* FIXME docs say we should... but currently -removeRelationship * calls us, so it would cause an infinite loop */ // [_entity removeRelationship:self]; [_entity _setIsEdited]; [entity _setIsEdited]; _entity = 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]; ASSIGN(_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 ([self createsMutableObjects]) { if (!_joins) _joins = [NSMutableArray new]; [(NSMutableArray *)_joins addObject: join]; EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@", _joins, [_joins class]); } else { if (_joins) _joins = RETAIN([[_joins autorelease] arrayByAddingObject: join]); else _joins = RETAIN([NSArray arrayWithObject: join]); EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@", _joins, [_joins class]); /*NO: will be recomputed _sourceAttributes = [[[_sourceAttributes autorelease] arrayByAddingObject:[join sourceAttribute]] retain]; _destinationAttributes = [[[_destinationAttributes autorelease] arrayByAddingObject: [join destinationAttribute]] retain]; */ } 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]; if ([self createsMutableObjects]) { [(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]); } else { NSMutableArray *ma = [_joins mutableCopy]; NSArray *a = _joins; [ma removeObject: join]; _joins = ma; [a release]; EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@", _joins, [_joins class]); /*NO: will be recomputed _sourceAttributes = [[_sourceAttributes autorelease] mutableCopy]; [(NSMutableArray *)_sourceAttributes removeObject:[join sourceAttribute]]; _sourceAttributes = [[_sourceAttributes autorelease] copy]; _destinationAttributes = [[_destinationAttributes autorelease] mutableCopy]; [(NSMutableArray *)_destinationAttributes removeObject:[join destinationAttribute]]; _destinationAttributes = [[_destinationAttributes autorelease] copy]; */ } [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) - (void)setCreateMutableObjects: (BOOL)flag { if (_flags.createsMutableObjects != flag) { _flags.createsMutableObjects = flag; if (_flags.createsMutableObjects) { _joins = [[_joins autorelease] mutableCopy]; EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@", _joins, [_joins class]); DESTROY(_sourceAttributes); DESTROY(_destinationAttributes); /*Will be recomputed later _sourceAttributes = [[_sourceAttributes autorelease] mutableCopy]; _destinationAttributes = [[_destinationAttributes autorelease] mutableCopy]; */ } else { _joins = [[NSArray alloc] initWithArray:[_joins autorelease] copyItems:NO]; EOFLOGObjectLevelArgs(@"EORelationship", @"XXjoins %p class%@", _joins, [_joins class]); DESTROY(_sourceAttributes); DESTROY(_destinationAttributes); /*Will be recomputed later _sourceAttributes = [[_sourceAttributes autorelease] copy]; _destinationAttributes = [[_destinationAttributes autorelease] copy]; */ } } } - (BOOL)createsMutableObjects { return _flags.createsMutableObjects; } /* TODO this method should probably be private. */ - (void)setInverseRelationship: (EORelationship*)relationship { [self willChange]; // TODO: verify 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: 0]; 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); } - (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 { //TODO VERIFY int count; EOFLOGObjectFnStart(); count = [_joins count]; EOFLOGObjectLevelArgs(@"EORelationship", @"_joinsChanged:%@\nin %@", _joins, self); if (count > 0) { int i; for (i = 0; i < count; i++) { EOJoin *join = [_joins objectAtIndex: i]; EOAttribute *destinationAttribute = [join destinationAttribute]; EOEntity *destinationEntity = [destinationAttribute entity]; GDL2DestinationEntitiesRemoveRelationship(_destination, self); GDL2DestinationEntitiesAddRelationship(destinationEntity, self); _destination = destinationEntity; } } else { _destination = nil; } //_joins count //[self notImplemented:_cmd]; // TODO-NOW /* join destinationAttribute attr entity */ EOFLOGObjectFnStop(); } @end