4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
9 This file is part of the GNUstep Database Library.
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this library; see the file COPYING.LIB.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #import "EOAttribute.h"
31 #import "EORelationship.h"
32 #import "EOExpressionArray.h"
33 #import "EOFExceptions.h"
34 #import <EOControl/EONull.h>
36 static EONull *null = nil;
38 @interface EOJoin : EORelationship // for adaptor compability
41 @implementation EORelationship
44 if (null == nil) null = [[EONull null] retain];
48 if ((self = [super init])) {
49 self->flags.createsMutableObjects = YES;
51 self->destinationEntity = nil;
56 - (id)initWithName:(NSString*)_name {
57 if ((self = [self init])) {
65 RELEASE(self->definition);
66 RELEASE(self->userDictionary);
68 if ([self->destinationEntity isKindOfClass:[NSString class]])
69 RELEASE(self->destinationEntity);
70 // else: non-retained EOEntity
71 self->destinationEntity = nil;
72 RELEASE(self->componentRelationships);
73 RELEASE(self->sourceAttribute);
74 RELEASE(self->destinationAttribute);
78 // These methods should be here to let the library work with NeXT foundation
82 - (id)copyWithZone:(NSZone *)_zone {
86 // Is equal only if same name; used to make aliasing ordering stable
88 return [self->name hash];
91 + (BOOL)isValidName:(NSString*)_name {
92 return [EOEntity isValidName:_name];
95 - (void)setDefinition:(NSString *)def {
97 [NSException raise:NSInvalidArgumentException
98 format:@"invalid (nil) definition argument ..."];
101 if ([def isNameOfARelationshipPath]) {
102 NSArray *defArray = nil;
105 defArray = [def componentsSeparatedByString:@"."];
106 count = [defArray count];
108 RELEASE(self->componentRelationships);
109 self->componentRelationships =
110 [[NSMutableArray alloc] initWithCapacity:count];
112 flags.isFlattened = YES;
115 EOEntity *currentEntity = self->entity;
116 id relationship = nil;
119 for (i = 0; i < count; i++) {
120 id relationshipName = [defArray objectAtIndex:i];
122 /* Take the address of `relationship' to force the compiler
123 to not allocate it into a register. */
124 *(&relationship) = [currentEntity relationshipNamed:
127 [[[InvalidPropertyException alloc]
128 initWithName:relationshipName
129 entity:currentEntity] raise];
130 [self->componentRelationships addObject:relationship];
131 flags.isToMany |= [relationship isToMany];
132 currentEntity = [relationship destinationEntity];
134 if (self->destinationEntity &&
135 ![self->destinationEntity isEqual:currentEntity])
136 [[[DestinationEntityDoesntMatchDefinitionException alloc]
137 initForDestination:self->destinationEntity
139 relationship:self] raise];
140 if ([self->destinationEntity isKindOfClass:[NSString class]])
141 RELEASE(self->destinationEntity);
142 self->destinationEntity = currentEntity; /* non-retained */
143 if ([self->destinationEntity isKindOfClass:[NSString class]])
144 RETAIN(self->destinationEntity);
147 RELEASE(self->componentRelationships);
148 self->componentRelationships = nil;
149 [localException raise];
154 [[[InvalidNameException alloc] initWithName:def] raise];
156 ASSIGN(self->definition, def);
159 - (BOOL)setToMany:(BOOL)_flag {
160 if ([self isFlattened]) return NO;
161 self->flags.isToMany = _flag;
165 return self->flags.isToMany;
168 - (BOOL)setName:(NSString *)_name {
169 if ([self->entity referencesProperty:_name])
171 ASSIGN(self->name, _name);
182 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)_ctx {
186 - (void)setEntity:(EOEntity *)_entity {
187 self->entity = _entity; /* non-retained */
189 - (EOEntity *)entity {
192 - (void)resetEntities {
194 self->destinationEntity = nil;
197 return (self->entity != nil) ? YES : NO;
199 - (BOOL)hasDestinationEntity {
200 return (self->destinationEntity != nil) ? YES : NO;
203 - (void)setUserDictionary:(NSDictionary *)dict {
204 ASSIGN(self->userDictionary, dict);
206 - (NSDictionary *)userDictionary {
207 return self->userDictionary;
211 return self->sourceAttribute ? [NSArray arrayWithObject:self] : nil;
214 - (NSString *)definition {
215 return self->definition;
217 - (NSArray *)sourceAttributes {
218 return [NSArray arrayWithObject:self->sourceAttribute];
220 - (NSArray *)destinationAttributes {
221 return [NSArray arrayWithObject:self->destinationAttribute];
223 - (EOEntity *)destinationEntity {
224 return self->destinationEntity;
226 - (BOOL)isFlattened {
227 return self->flags.isFlattened;
229 - (NSArray *)componentRelationships {
230 return self->componentRelationships;
233 - (BOOL)referencesProperty:(id)_property {
234 if ([self->sourceAttribute isEqual:_property])
236 if ([self->destinationAttribute isEqual:_property])
239 if ([self->componentRelationships indexOfObject:_property] != NSNotFound)
244 - (NSDictionary *)foreignKeyForRow:(NSDictionary *)_row {
245 int j, i, n = [_row count];
248 for (i = j = 0, n = 1; j < n; j++) {
249 EOAttribute *keyAttribute = self->sourceAttribute;
250 EOAttribute *fkeyAttribute = self->destinationAttribute;
252 NSString *fkey = nil;
255 key = [keyAttribute name];
256 fkey = [fkeyAttribute name];
257 value = [_row objectForKey:key];
265 NSLog(@"%s: could not get value of key %@ (foreignKey=%@)",
266 __PRETTY_FUNCTION__, key, fkey);
270 return AUTORELEASE([[NSDictionary alloc]
272 forKeys:keys count:i]);
275 - (NSString *)description {
276 return [[self propertyList] description];
279 @end /* EORelationship */
282 @implementation EORelationship (EORelationshipPrivate)
284 + (EORelationship *)relationshipFromPropertyList:(id)_plist
285 model:(EOModel *)model
287 EORelationship *relationship = nil;
288 NSArray *array = nil;
289 NSEnumerator *enumerator = nil;
292 relationship = AUTORELEASE([EORelationship new]);
293 [relationship setCreateMutableObjects:YES];
294 [relationship setName:[_plist objectForKey:@"name"]];
295 [relationship setUserDictionary:
296 [_plist objectForKey:@"userDictionary"]];
298 if ((array = [_plist objectForKey:@"joins"])) {
299 enumerator = [array objectEnumerator];
301 joinPList = [enumerator nextObject];
302 [relationship loadJoinPropertyList:joinPList];
303 joinPList = [enumerator nextObject];
304 NSAssert(joinPList == nil, @"a relationship only supports one join !");
307 [relationship loadJoinPropertyList:_plist];
310 relationship->destinationEntity =
311 RETAIN([_plist objectForKey:@"destination"]);
314 relationship->flags.isToMany =
315 [[_plist objectForKey:@"isToMany"] isEqual:@"Y"];
317 relationship->flags.isMandatory =
318 [[_plist objectForKey:@"isMandatory"] isEqual:@"Y"];
320 /* Do not send here the -setDefinition: message because the relationships
321 are not yet created from the model file. */
322 relationship->definition
323 = RETAIN([_plist objectForKey:@"definition"]);
328 - (void)replaceStringsWithObjects {
329 EOModel *model = [self->entity model];
331 if (self->destinationEntity) {
332 // now self->destinationEntity is NSString and retained !!
333 id destinationEntityName = AUTORELEASE(self->destinationEntity);
334 self->destinationEntity = [model entityNamed:destinationEntityName];
335 // now hold entity non-retained
337 if (self->destinationEntity == nil) {
338 NSLog(@"invalid entity name '%@' specified as destination entity "
339 @"for relationship '%@' in entity '%@'",
340 destinationEntityName, name, [self->entity name]);
341 [model errorInReading];
345 if (!(self->destinationEntity || self->definition)) {
346 NSLog(@"relationship '%@' in entity '%@' is incompletely specified: "
347 @"no destination entity or definition.", name, [self->entity name]);
348 [model errorInReading];
351 if (self->definition && (self->sourceAttribute != nil)) {
352 NSLog(@"relationship '%@' in entity '%@': flattened relationships "
353 @"cannot have joins", name, [self->entity name]);
354 [model errorInReading];
357 if (self->sourceAttribute) {
358 EOEntity *attributeEntity;
359 EOAttribute *attribute = nil;
362 attributeEntity = self->flags.isToMany
363 ? self->destinationEntity
366 attributeEntity = self->entity;
368 [attributeEntity attributeNamed:(NSString*)self->sourceAttribute];
371 [self setSourceAttribute:attribute];
373 [model errorInReading];
374 NSLog(@"invalid attribute name '%@' specified as source attribute for "
375 @"join in relationship '%@' in entity '%@' (dest='%@')",
376 sourceAttribute, [self name],
377 [self->entity name], [self->destinationEntity name]);
381 attributeEntity = self->flags.isToMany
383 : self->destinationEntity;
385 attributeEntity = self->destinationEntity;
386 attribute = [attributeEntity attributeNamed:(NSString*)destinationAttribute];
389 [self setDestinationAttribute:attribute];
391 [model errorInReading];
392 NSLog(@"invalid attribute name '%@' specified as destination "
393 @"attribute for join in relationship '%@' in entity '%@' (dest='%@')",
394 destinationAttribute, [self name],
395 [self->entity name], [self->destinationEntity name]);
398 [self setCreateMutableObjects:NO];
401 - (void)initFlattenedRelationship {
402 if (self->definition) {
404 [self setDefinition:self->definition];
406 NSLog([localException reason]);
407 [[self->entity model] errorInReading];
414 NSMutableDictionary *propertyList = nil;
416 propertyList = [NSMutableDictionary dictionary];
417 [self encodeIntoPropertyList:propertyList];
421 - (void)setCreateMutableObjects:(BOOL)flag {
422 if (self->flags.createsMutableObjects == flag)
425 self->flags.createsMutableObjects = flag;
428 - (BOOL)createsMutableObjects {
429 return self->flags.createsMutableObjects;
432 - (int)compareByName:(EORelationship *)_other {
433 return [[(EORelationship *)self name] compare:[_other name]];
436 @end /* EORelationship (EORelationshipPrivate) */
438 @implementation EORelationship(EOJoin)
440 - (void)loadJoinPropertyList:(id)propertyList {
441 NSString *joinOperatorPList;
442 NSString *joinSemanticPList;
445 tmp = [propertyList objectForKey:@"sourceAttribute"];
446 [self setSourceAttribute:tmp];
447 tmp = [propertyList objectForKey:@"destinationAttribute"];
448 [self setDestinationAttribute:tmp];
450 if ((joinOperatorPList = [propertyList objectForKey:@"joinOperator"])) {
451 NSAssert([joinOperatorPList isEqual:@"EOJoinEqualTo"],
452 @"only EOJoinEqualTo is supported as the join operator !");
455 if ((joinSemanticPList = [propertyList objectForKey:@"joinSemantic"])) {
456 NSAssert([joinSemanticPList isEqual:@"EOInnerJoin"],
457 @"only EOInnerJoin is supported as the join semantic !");
461 - (void)setDestinationAttribute:(EOAttribute*)attribute {
462 ASSIGN(self->destinationAttribute, attribute);
464 - (EOAttribute*)destinationAttribute {
465 return self->destinationAttribute;
468 - (void)setSourceAttribute:(EOAttribute *)attribute {
469 ASSIGN(self->sourceAttribute, attribute);
471 - (EOAttribute*)sourceAttribute {
472 return self->sourceAttribute;
475 - (EOJoinOperator)joinOperator {
476 return EOJoinEqualTo;
478 - (EOJoinSemantic)joinSemantic {
482 - (EORelationship*)relationship {
488 - (void)addJoin:(EOJoin *)_join {
489 [self setSourceAttribute:[_join sourceAttribute]];
490 [self setDestinationAttribute:[_join destinationAttribute]];
493 @end /* EORelationship(EOJoin) */
495 @implementation EORelationship(PropertyListCoding)
497 static inline void _addToPropList(NSMutableDictionary *_plist,
498 id _value, NSString *key) {
499 if (_value) [_plist setObject:_value forKey:key];
502 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
503 _addToPropList(_plist, self->name, @"name");
504 _addToPropList(_plist, self->definition, @"definition");
505 _addToPropList(_plist, self->userDictionary, @"userDictionary");
507 if (self->sourceAttribute) { // has join ?
508 _addToPropList(_plist, [self->sourceAttribute name], @"sourceAttribute");
509 _addToPropList(_plist, [self->destinationAttribute name],
510 @"destinationAttribute");
511 _addToPropList(_plist, [[self->sourceAttribute entity] name],
515 if (![self isFlattened] && self->destinationEntity) {
516 _addToPropList(_plist, [self->destinationEntity name], @"destination");
519 if (![self isFlattened])
520 _addToPropList(_plist, flags.isToMany ? @"Y" : @"N", @"isToMany");
522 if (![self isMandatory])
523 _addToPropList(_plist, flags.isMandatory ? @"Y" : @"N", @"isMandatory");
526 @end /* EORelationship(PropertyListCoding) */
528 @implementation EORelationship(EOF2Additions)
532 - (void)setIsMandatory:(BOOL)_flag {
533 self->flags.isMandatory = _flag ? 1 : 0;
535 - (BOOL)isMandatory {
536 return self->flags.isMandatory ? YES : NO;
539 - (NSException *)validateValue:(id *)_value {
540 if (_value == NULL) return nil;
542 /* check 'mandatory' constraint */
544 if (self->flags.isMandatory) {
545 if (self->flags.isToMany) {
546 if ([*_value count] == 0) {
547 NSLog(@"WARNING(%s): tried to use value %@"
548 @"with mandatory toMany relationship %@",
549 __PRETTY_FUNCTION__, *_value, self);
553 if ((*_value == nil) || (*_value == null)) {
554 NSLog(@"WARNING(%s): tried to use value %@"
555 @"with mandatory toOne relationship %@",
556 __PRETTY_FUNCTION__, *_value, self);
564 @end /* EORelationship(EOF2Additions) */
566 @implementation EOJoin