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 {
96 // TODO: do we need this?
98 [NSException raise:NSInvalidArgumentException
99 format:@"invalid (nil) definition argument ..."];
102 if ([def isNameOfARelationshipPath]) {
103 NSArray *defArray = nil;
106 defArray = [def componentsSeparatedByString:@"."];
107 count = [defArray count];
109 RELEASE(self->componentRelationships);
110 self->componentRelationships =
111 [[NSMutableArray alloc] initWithCapacity:count];
113 flags.isFlattened = YES;
116 EOEntity *currentEntity = self->entity;
117 id relationship = nil;
120 for (i = 0; i < count; i++) {
121 id relationshipName = [defArray objectAtIndex:i];
123 /* Take the address of `relationship' to force the compiler
124 to not allocate it into a register. */
125 *(&relationship) = [currentEntity relationshipNamed:
128 [[[InvalidPropertyException alloc]
129 initWithName:relationshipName
130 entity:currentEntity] raise];
131 [self->componentRelationships addObject:relationship];
132 flags.isToMany |= [relationship isToMany];
133 currentEntity = [relationship destinationEntity];
135 if (self->destinationEntity &&
136 ![self->destinationEntity isEqual:currentEntity])
137 [[[DestinationEntityDoesntMatchDefinitionException alloc]
138 initForDestination:self->destinationEntity
140 relationship:self] raise];
141 if ([self->destinationEntity isKindOfClass:[NSString class]])
142 RELEASE(self->destinationEntity);
143 self->destinationEntity = currentEntity; /* non-retained */
144 if ([self->destinationEntity isKindOfClass:[NSString class]])
145 RETAIN(self->destinationEntity);
148 RELEASE(self->componentRelationships);
149 self->componentRelationships = nil;
150 [localException raise];
155 [[[InvalidNameException alloc] initWithName:def] raise];
157 ASSIGN(self->definition, def);
160 - (BOOL)setToMany:(BOOL)_flag {
161 if ([self isFlattened]) return NO;
162 self->flags.isToMany = _flag;
166 return self->flags.isToMany;
169 - (BOOL)setName:(NSString *)_name {
170 if ([self->entity referencesProperty:_name])
172 ASSIGN(self->name, _name);
183 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)_ctx {
187 - (void)setEntity:(EOEntity *)_entity {
188 self->entity = _entity; /* non-retained */
190 - (EOEntity *)entity {
193 - (void)resetEntities {
195 self->destinationEntity = nil;
198 return (self->entity != nil) ? YES : NO;
200 - (BOOL)hasDestinationEntity {
201 return (self->destinationEntity != nil) ? YES : NO;
204 - (void)setUserDictionary:(NSDictionary *)dict {
205 ASSIGN(self->userDictionary, dict);
207 - (NSDictionary *)userDictionary {
208 return self->userDictionary;
212 return self->sourceAttribute ? [NSArray arrayWithObject:self] : nil;
215 - (NSString *)definition {
216 return self->definition;
218 - (NSArray *)sourceAttributes {
219 return [NSArray arrayWithObject:self->sourceAttribute];
221 - (NSArray *)destinationAttributes {
222 return [NSArray arrayWithObject:self->destinationAttribute];
224 - (EOEntity *)destinationEntity {
225 return self->destinationEntity;
227 - (BOOL)isFlattened {
228 return self->flags.isFlattened;
230 - (NSArray *)componentRelationships {
231 return self->componentRelationships;
234 - (BOOL)referencesProperty:(id)_property {
235 if ([self->sourceAttribute isEqual:_property])
237 if ([self->destinationAttribute isEqual:_property])
240 if ([self->componentRelationships indexOfObject:_property] != NSNotFound)
245 - (NSDictionary *)foreignKeyForRow:(NSDictionary *)_row {
246 int j, i, n = [_row count];
249 for (i = j = 0, n = 1; j < n; j++) {
250 EOAttribute *keyAttribute = self->sourceAttribute;
251 EOAttribute *fkeyAttribute = self->destinationAttribute;
253 NSString *fkey = nil;
256 key = [keyAttribute name];
257 fkey = [fkeyAttribute name];
258 value = [_row objectForKey:key];
266 NSLog(@"%s: could not get value of key %@ (foreignKey=%@)",
267 __PRETTY_FUNCTION__, key, fkey);
271 return AUTORELEASE([[NSDictionary alloc]
273 forKeys:keys count:i]);
276 - (NSString *)description {
277 return [[self propertyList] description];
280 @end /* EORelationship */
283 @implementation EORelationship (EORelationshipPrivate)
285 + (EORelationship *)relationshipFromPropertyList:(id)_plist
286 model:(EOModel *)model
288 NSDictionary *plist = _plist;
289 EORelationship *relationship = nil;
290 NSArray *array = nil;
291 NSEnumerator *enumerator = nil;
294 relationship = [[[EORelationship alloc] init] autorelease];
295 [relationship setCreateMutableObjects:YES];
296 [relationship setName:[plist objectForKey:@"name"]];
297 [relationship setUserDictionary:
298 [plist objectForKey:@"userDictionary"]];
300 if ((array = [plist objectForKey:@"joins"])) {
301 enumerator = [array objectEnumerator];
303 joinPList = [enumerator nextObject];
304 [relationship loadJoinPropertyList:joinPList];
305 joinPList = [enumerator nextObject];
306 NSAssert(joinPList == nil, @"a relationship only supports one join !");
309 [relationship loadJoinPropertyList:_plist];
312 relationship->destinationEntity =
313 RETAIN([plist objectForKey:@"destination"]);
316 relationship->flags.isToMany =
317 [[plist objectForKey:@"isToMany"] isEqual:@"Y"];
319 relationship->flags.isMandatory =
320 [[plist objectForKey:@"isMandatory"] isEqual:@"Y"];
322 /* Do not send here the -setDefinition: message because the relationships
323 are not yet created from the model file. */
324 relationship->definition
325 = RETAIN([plist objectForKey:@"definition"]);
330 - (void)replaceStringsWithObjects {
331 EOModel *model = [self->entity model];
333 if (self->destinationEntity) {
334 // now self->destinationEntity is NSString and retained !!
335 id destinationEntityName = AUTORELEASE(self->destinationEntity);
336 self->destinationEntity = [model entityNamed:destinationEntityName];
337 // now hold entity non-retained
339 if (self->destinationEntity == nil) {
340 NSLog(@"invalid entity name '%@' specified as destination entity "
341 @"for relationship '%@' in entity '%@'",
342 destinationEntityName, name, [self->entity name]);
343 [model errorInReading];
347 if (!(self->destinationEntity || self->definition)) {
348 NSLog(@"relationship '%@' in entity '%@' is incompletely specified: "
349 @"no destination entity or definition.", name, [self->entity name]);
350 [model errorInReading];
353 if (self->definition && (self->sourceAttribute != nil)) {
354 NSLog(@"relationship '%@' in entity '%@': flattened relationships "
355 @"cannot have joins", name, [self->entity name]);
356 [model errorInReading];
359 if (self->sourceAttribute) {
360 EOEntity *attributeEntity;
361 EOAttribute *attribute = nil;
364 attributeEntity = self->flags.isToMany
365 ? self->destinationEntity
368 attributeEntity = self->entity;
370 [attributeEntity attributeNamed:(NSString*)self->sourceAttribute];
373 [self setSourceAttribute:attribute];
375 [model errorInReading];
376 NSLog(@"invalid attribute name '%@' specified as source attribute for "
377 @"join in relationship '%@' in entity '%@' (dest='%@')",
378 sourceAttribute, [self name],
379 [self->entity name], [self->destinationEntity name]);
383 attributeEntity = self->flags.isToMany
385 : self->destinationEntity;
387 attributeEntity = self->destinationEntity;
388 attribute = [attributeEntity attributeNamed:(NSString*)destinationAttribute];
391 [self setDestinationAttribute:attribute];
393 [model errorInReading];
394 NSLog(@"invalid attribute name '%@' specified as destination "
395 @"attribute for join in relationship '%@' in entity '%@' (dest='%@')",
396 destinationAttribute, [self name],
397 [self->entity name], [self->destinationEntity name]);
400 [self setCreateMutableObjects:NO];
403 - (void)initFlattenedRelationship {
404 if (self->definition) {
406 [self setDefinition:self->definition];
408 NSLog([localException reason]);
409 [[self->entity model] errorInReading];
416 NSMutableDictionary *propertyList = nil;
418 propertyList = [NSMutableDictionary dictionary];
419 [self encodeIntoPropertyList:propertyList];
423 - (void)setCreateMutableObjects:(BOOL)flag {
424 if (self->flags.createsMutableObjects == flag)
427 self->flags.createsMutableObjects = flag;
430 - (BOOL)createsMutableObjects {
431 return self->flags.createsMutableObjects;
434 - (int)compareByName:(EORelationship *)_other {
435 return [[(EORelationship *)self name] compare:[_other name]];
440 - (void)loadJoinPropertyList:(id)propertyList {
441 NSDictionary *plist = propertyList;
442 NSString *joinOperatorPList;
443 NSString *joinSemanticPList;
446 tmp = [plist objectForKey:@"sourceAttribute"];
447 [self setSourceAttribute:tmp];
448 tmp = [plist objectForKey:@"destinationAttribute"];
449 [self setDestinationAttribute:tmp];
451 if ((joinOperatorPList = [plist objectForKey:@"joinOperator"])) {
452 NSAssert([joinOperatorPList isEqual:@"EOJoinEqualTo"],
453 @"only EOJoinEqualTo is supported as the join operator !");
456 if ((joinSemanticPList = [plist objectForKey:@"joinSemantic"])) {
457 NSAssert([joinSemanticPList isEqual:@"EOInnerJoin"],
458 @"only EOInnerJoin is supported as the join semantic !");
462 - (void)setDestinationAttribute:(EOAttribute*)attribute {
463 ASSIGN(self->destinationAttribute, attribute);
465 - (EOAttribute*)destinationAttribute {
466 return self->destinationAttribute;
469 - (void)setSourceAttribute:(EOAttribute *)attribute {
470 ASSIGN(self->sourceAttribute, attribute);
472 - (EOAttribute*)sourceAttribute {
473 return self->sourceAttribute;
476 - (EOJoinOperator)joinOperator {
477 return EOJoinEqualTo;
479 - (EOJoinSemantic)joinSemantic {
483 - (EORelationship*)relationship {
489 - (void)addJoin:(EOJoin *)_join {
490 [self setSourceAttribute:[_join sourceAttribute]];
491 [self setDestinationAttribute:[_join destinationAttribute]];
494 /* PropertyListCoding */
496 static inline void _addToPropList(NSMutableDictionary *_plist,
497 id _value, NSString *key) {
498 if (_value) [_plist setObject:_value forKey:key];
501 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
502 _addToPropList(_plist, self->name, @"name");
503 _addToPropList(_plist, self->definition, @"definition");
504 _addToPropList(_plist, self->userDictionary, @"userDictionary");
506 if (self->sourceAttribute) { // has join ?
507 _addToPropList(_plist, [self->sourceAttribute name], @"sourceAttribute");
508 _addToPropList(_plist, [self->destinationAttribute name],
509 @"destinationAttribute");
510 _addToPropList(_plist, [[self->sourceAttribute entity] name],
514 if (![self isFlattened] && self->destinationEntity) {
515 _addToPropList(_plist, [self->destinationEntity name], @"destination");
518 if (![self isFlattened])
519 _addToPropList(_plist, flags.isToMany ? @"Y" : @"N", @"isToMany");
521 if (![self isMandatory])
522 _addToPropList(_plist, flags.isMandatory ? @"Y" : @"N", @"isMandatory");
529 - (void)setIsMandatory:(BOOL)_flag {
530 self->flags.isMandatory = _flag ? 1 : 0;
532 - (BOOL)isMandatory {
533 return self->flags.isMandatory ? YES : NO;
536 - (NSException *)validateValue:(id *)_value {
537 if (_value == NULL) return nil;
539 /* check 'mandatory' constraint */
541 if (self->flags.isMandatory) {
542 if (self->flags.isToMany) {
543 if ([*_value count] == 0) {
544 NSLog(@"WARNING(%s): tried to use value %@"
545 @"with mandatory toMany relationship %@",
546 __PRETTY_FUNCTION__, *_value, self);
550 if ((*_value == nil) || (*_value == null)) {
551 NSLog(@"WARNING(%s): tried to use value %@"
552 @"with mandatory toOne relationship %@",
553 __PRETTY_FUNCTION__, *_value, self);
561 @end /* EORelationship */
563 @implementation EOJoin