]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EORelationship.m
renamed PostgreSQL72 to PostgreSQL, install in Library/GDLAdaptors-1.1
[sope] / sope-gdl1 / GDLAccess / EORelationship.m
1 /* 
2    EORelationship.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
7    Date: August 1996
8
9    This file is part of the GNUstep Database Library.
10
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.
15
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.
20
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.
25 */
26
27 #import "common.h"
28 #import "EOModel.h"
29 #import "EOAttribute.h"
30 #import "EOEntity.h"
31 #import "EORelationship.h"
32 #import "EOExpressionArray.h"
33 #import "EOFExceptions.h"
34 #import <EOControl/EONull.h>
35
36 static EONull *null = nil;
37
38 @interface EOJoin : EORelationship // for adaptor compability
39 @end
40
41 @implementation EORelationship
42
43 + (void)initialize {
44   if (null == nil) null = [[EONull null] retain];
45 }
46
47 - (id)init {
48   if ((self = [super init])) {
49     self->flags.createsMutableObjects = YES;
50     self->entity = nil;
51     self->destinationEntity = nil;
52   }
53   return self;
54 }
55
56 - (id)initWithName:(NSString*)_name {
57   if ((self = [self init])) {
58     self->name = _name;
59   }
60   return self;
61 }
62
63 - (void)dealloc {
64   RELEASE(self->name);
65   RELEASE(self->definition);
66   RELEASE(self->userDictionary);
67   self->entity            = nil;
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);
75   [super dealloc];
76 }
77
78 // These methods should be here to let the library work with NeXT foundation
79 - (id)copy {
80   return RETAIN(self);
81 }
82 - (id)copyWithZone:(NSZone *)_zone {
83   return RETAIN(self);
84 }
85
86 // Is equal only if same name; used to make aliasing ordering stable
87 - (unsigned)hash {
88   return [self->name hash];
89 }
90
91 + (BOOL)isValidName:(NSString*)_name {
92   return [EOEntity isValidName:_name];
93 }
94
95 - (void)setDefinition:(NSString *)def {
96   if (def == nil) {
97     [NSException raise:NSInvalidArgumentException
98                  format:@"invalid (nil) definition argument ..."];
99   }
100
101   if ([def isNameOfARelationshipPath]) {
102     NSArray *defArray = nil;
103     int     count;
104     
105     defArray               = [def componentsSeparatedByString:@"."];
106     count                  = [defArray count];
107     
108     RELEASE(self->componentRelationships);
109     self->componentRelationships =
110       [[NSMutableArray alloc] initWithCapacity:count];
111     
112     flags.isFlattened      = YES;
113     
114     NS_DURING {
115       EOEntity *currentEntity = self->entity;
116       id       relationship   = nil;
117       int      i;
118       
119       for (i = 0; i < count; i++) {
120         id relationshipName = [defArray objectAtIndex:i];
121     
122         /* Take the address of `relationship' to force the compiler
123            to not allocate it into a register. */
124         *(&relationship) = [currentEntity relationshipNamed:
125                                           relationshipName];
126         if (!relationship)
127           [[[InvalidPropertyException alloc]
128                                            initWithName:relationshipName
129                                            entity:currentEntity] raise];
130         [self->componentRelationships addObject:relationship];
131         flags.isToMany |= [relationship isToMany];
132         currentEntity = [relationship destinationEntity];
133       }
134       if (self->destinationEntity &&
135           ![self->destinationEntity isEqual:currentEntity])
136         [[[DestinationEntityDoesntMatchDefinitionException alloc]
137                                       initForDestination:self->destinationEntity
138                                       andDefinition:def
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);
145     }
146     NS_HANDLER {
147       RELEASE(self->componentRelationships);
148       self->componentRelationships = nil;
149       [localException raise];
150     }
151     NS_ENDHANDLER;
152   }
153   else
154     [[[InvalidNameException alloc] initWithName:def] raise];
155
156   ASSIGN(self->definition, def);
157 }
158
159 - (BOOL)setToMany:(BOOL)_flag {
160   if ([self isFlattened]) return NO;
161   self->flags.isToMany = _flag;
162   return YES;
163 }
164 - (BOOL)isToMany {
165   return self->flags.isToMany;
166 }
167
168 - (BOOL)setName:(NSString *)_name {
169   if ([self->entity referencesProperty:_name])
170     return NO;
171   ASSIGN(self->name, _name);
172   return NO;
173 }
174 - (NSString *)name {
175   return self->name;
176 }
177
178 - (BOOL)isCompound {
179   return NO;
180 }
181
182 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)_ctx {
183   return self->name;
184 }
185
186 - (void)setEntity:(EOEntity *)_entity {
187   self->entity = _entity; /* non-retained */
188 }
189 - (EOEntity *)entity {
190   return self->entity;
191 }
192 - (void)resetEntities {
193   self->entity = nil;
194   self->destinationEntity = nil;
195 }
196 - (BOOL)hasEntity {
197   return (self->entity != nil) ? YES : NO;
198 }
199 - (BOOL)hasDestinationEntity {
200   return (self->destinationEntity != nil) ? YES : NO;
201 }
202
203 - (void)setUserDictionary:(NSDictionary *)dict {
204   ASSIGN(self->userDictionary, dict);
205 }
206 - (NSDictionary *)userDictionary {
207   return self->userDictionary;
208 }
209
210 - (NSArray *)joins {
211   return self->sourceAttribute ? [NSArray arrayWithObject:self] : nil;
212 }
213
214 - (NSString *)definition {
215   return self->definition;
216 }
217 - (NSArray *)sourceAttributes {
218   return [NSArray arrayWithObject:self->sourceAttribute];
219 }
220 - (NSArray *)destinationAttributes {
221   return [NSArray arrayWithObject:self->destinationAttribute];
222 }
223 - (EOEntity *)destinationEntity {
224   return self->destinationEntity;
225 }
226 - (BOOL)isFlattened {
227   return self->flags.isFlattened;
228 }
229 - (NSArray *)componentRelationships {
230   return self->componentRelationships;
231 }
232
233 - (BOOL)referencesProperty:(id)_property {
234   if ([self->sourceAttribute isEqual:_property])
235     return YES;
236   if ([self->destinationAttribute isEqual:_property])
237     return YES;
238
239   if ([self->componentRelationships indexOfObject:_property] != NSNotFound)
240     return YES;
241   return NO;
242 }
243
244 - (NSDictionary *)foreignKeyForRow:(NSDictionary *)_row {
245   int j, i, n = [_row count];
246   id  keys[n], vals[n];
247     
248   for (i = j = 0, n = 1; j < n; j++) {
249     EOAttribute *keyAttribute  = self->sourceAttribute;
250     EOAttribute *fkeyAttribute = self->destinationAttribute;
251     NSString    *key  = nil;
252     NSString    *fkey = nil;
253     id          value = nil;
254
255     key   = [keyAttribute  name];
256     fkey  = [fkeyAttribute name];
257     value = [_row objectForKey:key];
258
259     if (value) {
260       vals[i] = value;
261       keys[i] = fkey;
262       i++;
263     }
264     else {
265       NSLog(@"%s: could not get value of key %@ (foreignKey=%@)",
266             __PRETTY_FUNCTION__, key, fkey);
267     }
268   }
269     
270   return AUTORELEASE([[NSDictionary alloc]
271                                     initWithObjects:vals
272                                     forKeys:keys count:i]);
273 }
274
275 - (NSString *)description {
276   return [[self propertyList] description];
277 }
278
279 @end /* EORelationship */
280
281
282 @implementation EORelationship (EORelationshipPrivate)
283
284 + (EORelationship *)relationshipFromPropertyList:(id)_plist
285   model:(EOModel *)model
286 {
287   EORelationship *relationship = nil;
288   NSArray        *array      = nil;
289   NSEnumerator   *enumerator = nil;
290   id             joinPList   = nil;
291
292   relationship = AUTORELEASE([EORelationship new]);
293   [relationship setCreateMutableObjects:YES];
294   [relationship setName:[_plist objectForKey:@"name"]];
295   [relationship setUserDictionary:
296                 [_plist objectForKey:@"userDictionary"]];
297
298   if ((array = [_plist objectForKey:@"joins"])) {
299     enumerator = [array objectEnumerator];
300
301     joinPList = [enumerator nextObject];
302     [relationship loadJoinPropertyList:joinPList];
303     joinPList = [enumerator nextObject];
304     NSAssert(joinPList == nil, @"a relationship only supports one join !");
305   }
306   else {
307     [relationship loadJoinPropertyList:_plist];
308   }
309   
310   relationship->destinationEntity =
311     RETAIN([_plist objectForKey:@"destination"]);
312   // retained string
313
314   relationship->flags.isToMany =
315     [[_plist objectForKey:@"isToMany"] isEqual:@"Y"];
316
317   relationship->flags.isMandatory =
318     [[_plist objectForKey:@"isMandatory"] isEqual:@"Y"];
319   
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"]);
324
325   return relationship;
326 }
327
328 - (void)replaceStringsWithObjects {
329   EOModel *model = [self->entity model];
330   
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
336     
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];
342     }
343   }
344
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];
349   }
350
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];
355   }
356   
357   if (self->sourceAttribute) {
358     EOEntity    *attributeEntity;
359     EOAttribute *attribute = nil;
360     
361 #if 0
362     attributeEntity = self->flags.isToMany
363       ? self->destinationEntity
364       : self->entity;
365 #endif
366     attributeEntity = self->entity;
367     attribute =
368       [attributeEntity attributeNamed:(NSString*)self->sourceAttribute];
369     
370     if (attribute)
371       [self setSourceAttribute:attribute];
372     else {
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]);
378     }
379
380 #if 0
381     attributeEntity = self->flags.isToMany
382       ? self->entity
383       : self->destinationEntity;
384 #endif
385     attributeEntity = self->destinationEntity;
386     attribute = [attributeEntity attributeNamed:(NSString*)destinationAttribute];
387     
388     if (attribute)
389       [self setDestinationAttribute:attribute];
390     else {
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]);
396     }
397   }
398   [self setCreateMutableObjects:NO];
399 }
400
401 - (void)initFlattenedRelationship {
402   if (self->definition) {
403     NS_DURING
404       [self setDefinition:self->definition];
405     NS_HANDLER {
406       NSLog([localException reason]);
407       [[self->entity model] errorInReading];
408     }
409     NS_ENDHANDLER;
410   }
411 }
412
413 - (id)propertyList {
414   NSMutableDictionary *propertyList = nil;
415
416   propertyList = [NSMutableDictionary dictionary];
417   [self encodeIntoPropertyList:propertyList];
418   return propertyList;
419 }
420
421 - (void)setCreateMutableObjects:(BOOL)flag {
422   if (self->flags.createsMutableObjects == flag)
423     return;
424
425   self->flags.createsMutableObjects = flag;
426 }
427
428 - (BOOL)createsMutableObjects {
429   return self->flags.createsMutableObjects;
430 }
431
432 - (int)compareByName:(EORelationship *)_other {
433   return [[(EORelationship *)self name] compare:[_other name]];
434 }
435
436 @end /* EORelationship (EORelationshipPrivate) */
437
438 @implementation EORelationship(EOJoin)
439
440 - (void)loadJoinPropertyList:(id)propertyList {
441   NSString *joinOperatorPList;
442   NSString *joinSemanticPList;
443   id tmp;
444
445   tmp = [propertyList objectForKey:@"sourceAttribute"];
446   [self setSourceAttribute:tmp];
447   tmp = [propertyList objectForKey:@"destinationAttribute"];
448   [self setDestinationAttribute:tmp];
449   
450   if ((joinOperatorPList = [propertyList objectForKey:@"joinOperator"])) {
451     NSAssert([joinOperatorPList isEqual:@"EOJoinEqualTo"],
452              @"only EOJoinEqualTo is supported as the join operator !");
453   }
454
455   if ((joinSemanticPList = [propertyList objectForKey:@"joinSemantic"])) {
456     NSAssert([joinSemanticPList isEqual:@"EOInnerJoin"],
457              @"only EOInnerJoin is supported as the join semantic !");
458   }
459 }
460
461 - (void)setDestinationAttribute:(EOAttribute*)attribute {
462   ASSIGN(self->destinationAttribute, attribute);
463 }
464 - (EOAttribute*)destinationAttribute {
465   return self->destinationAttribute;
466 }
467
468 - (void)setSourceAttribute:(EOAttribute *)attribute {
469   ASSIGN(self->sourceAttribute, attribute);
470 }
471 - (EOAttribute*)sourceAttribute {
472   return self->sourceAttribute;
473 }
474
475 - (EOJoinOperator)joinOperator {
476   return EOJoinEqualTo;
477 }
478 - (EOJoinSemantic)joinSemantic {
479   return EOInnerJoin;
480 }
481
482 - (EORelationship*)relationship {
483   return self;
484 }
485
486 // misc
487
488 - (void)addJoin:(EOJoin *)_join {
489   [self setSourceAttribute:[_join sourceAttribute]];
490   [self setDestinationAttribute:[_join destinationAttribute]];
491 }
492
493 @end /* EORelationship(EOJoin) */
494
495 @implementation EORelationship(PropertyListCoding)
496
497 static inline void _addToPropList(NSMutableDictionary *_plist,
498                                   id _value, NSString *key) {
499   if (_value) [_plist setObject:_value forKey:key];
500 }
501
502 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
503   _addToPropList(_plist, self->name,           @"name");
504   _addToPropList(_plist, self->definition,     @"definition");
505   _addToPropList(_plist, self->userDictionary, @"userDictionary");
506
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],
512                    @"destination");
513   }
514   
515   if (![self isFlattened] && self->destinationEntity) {
516     _addToPropList(_plist, [self->destinationEntity name], @"destination");
517   }
518
519   if (![self isFlattened])
520     _addToPropList(_plist, flags.isToMany ? @"Y" : @"N", @"isToMany");
521   
522   if (![self isMandatory])
523     _addToPropList(_plist, flags.isMandatory ? @"Y" : @"N", @"isMandatory");
524 }
525
526 @end /* EORelationship(PropertyListCoding) */
527
528 @implementation EORelationship(EOF2Additions)
529
530 /* constraints */
531
532 - (void)setIsMandatory:(BOOL)_flag {
533   self->flags.isMandatory = _flag ? 1 : 0;
534 }
535 - (BOOL)isMandatory {
536   return self->flags.isMandatory ? YES : NO;
537 }
538
539 - (NSException *)validateValue:(id *)_value {
540   if (_value == NULL) return nil;
541   
542   /* check 'mandatory' constraint */
543   
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);
550       }
551     }
552     else {
553       if ((*_value == nil) || (*_value == null)) {
554         NSLog(@"WARNING(%s): tried to use value %@"
555               @"with mandatory toOne relationship %@",
556               __PRETTY_FUNCTION__, *_value, self);
557       }
558     }
559   }
560   
561   return nil;
562 }
563
564 @end /* EORelationship(EOF2Additions) */
565
566 @implementation EOJoin
567 @end /* EOJoin */