]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EORelationship.m
added missing inline pathes
[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   // TODO: do we need this?
97   if (def == nil) {
98     [NSException raise:NSInvalidArgumentException
99                  format:@"invalid (nil) definition argument ..."];
100   }
101
102   if ([def isNameOfARelationshipPath]) {
103     NSArray *defArray = nil;
104     int     count;
105     
106     defArray               = [def componentsSeparatedByString:@"."];
107     count                  = [defArray count];
108     
109     RELEASE(self->componentRelationships);
110     self->componentRelationships =
111       [[NSMutableArray alloc] initWithCapacity:count];
112     
113     flags.isFlattened      = YES;
114     
115     NS_DURING {
116       EOEntity *currentEntity = self->entity;
117       id       relationship   = nil;
118       int      i;
119       
120       for (i = 0; i < count; i++) {
121         id relationshipName = [defArray objectAtIndex:i];
122     
123         /* Take the address of `relationship' to force the compiler
124            to not allocate it into a register. */
125         *(&relationship) = [currentEntity relationshipNamed:
126                                           relationshipName];
127         if (!relationship)
128           [[[InvalidPropertyException alloc]
129                                            initWithName:relationshipName
130                                            entity:currentEntity] raise];
131         [self->componentRelationships addObject:relationship];
132         flags.isToMany |= [relationship isToMany];
133         currentEntity = [relationship destinationEntity];
134       }
135       if (self->destinationEntity &&
136           ![self->destinationEntity isEqual:currentEntity])
137         [[[DestinationEntityDoesntMatchDefinitionException alloc]
138                                       initForDestination:self->destinationEntity
139                                       andDefinition:def
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);
146     }
147     NS_HANDLER {
148       RELEASE(self->componentRelationships);
149       self->componentRelationships = nil;
150       [localException raise];
151     }
152     NS_ENDHANDLER;
153   }
154   else
155     [[[InvalidNameException alloc] initWithName:def] raise];
156
157   ASSIGN(self->definition, def);
158 }
159
160 - (BOOL)setToMany:(BOOL)_flag {
161   if ([self isFlattened]) return NO;
162   self->flags.isToMany = _flag;
163   return YES;
164 }
165 - (BOOL)isToMany {
166   return self->flags.isToMany;
167 }
168
169 - (BOOL)setName:(NSString *)_name {
170   if ([self->entity referencesProperty:_name])
171     return NO;
172   ASSIGN(self->name, _name);
173   return NO;
174 }
175 - (NSString *)name {
176   return self->name;
177 }
178
179 - (BOOL)isCompound {
180   return NO;
181 }
182
183 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)_ctx {
184   return self->name;
185 }
186
187 - (void)setEntity:(EOEntity *)_entity {
188   self->entity = _entity; /* non-retained */
189 }
190 - (EOEntity *)entity {
191   return self->entity;
192 }
193 - (void)resetEntities {
194   self->entity = nil;
195   self->destinationEntity = nil;
196 }
197 - (BOOL)hasEntity {
198   return (self->entity != nil) ? YES : NO;
199 }
200 - (BOOL)hasDestinationEntity {
201   return (self->destinationEntity != nil) ? YES : NO;
202 }
203
204 - (void)setUserDictionary:(NSDictionary *)dict {
205   ASSIGN(self->userDictionary, dict);
206 }
207 - (NSDictionary *)userDictionary {
208   return self->userDictionary;
209 }
210
211 - (NSArray *)joins {
212   return self->sourceAttribute ? [NSArray arrayWithObject:self] : nil;
213 }
214
215 - (NSString *)definition {
216   return self->definition;
217 }
218 - (NSArray *)sourceAttributes {
219   return [NSArray arrayWithObject:self->sourceAttribute];
220 }
221 - (NSArray *)destinationAttributes {
222   return [NSArray arrayWithObject:self->destinationAttribute];
223 }
224 - (EOEntity *)destinationEntity {
225   return self->destinationEntity;
226 }
227 - (BOOL)isFlattened {
228   return self->flags.isFlattened;
229 }
230 - (NSArray *)componentRelationships {
231   return self->componentRelationships;
232 }
233
234 - (BOOL)referencesProperty:(id)_property {
235   if ([self->sourceAttribute isEqual:_property])
236     return YES;
237   if ([self->destinationAttribute isEqual:_property])
238     return YES;
239
240   if ([self->componentRelationships indexOfObject:_property] != NSNotFound)
241     return YES;
242   return NO;
243 }
244
245 - (NSDictionary *)foreignKeyForRow:(NSDictionary *)_row {
246   int j, i, n = [_row count];
247   id  keys[n], vals[n];
248     
249   for (i = j = 0, n = 1; j < n; j++) {
250     EOAttribute *keyAttribute  = self->sourceAttribute;
251     EOAttribute *fkeyAttribute = self->destinationAttribute;
252     NSString    *key  = nil;
253     NSString    *fkey = nil;
254     id          value = nil;
255
256     key   = [keyAttribute  name];
257     fkey  = [fkeyAttribute name];
258     value = [_row objectForKey:key];
259
260     if (value) {
261       vals[i] = value;
262       keys[i] = fkey;
263       i++;
264     }
265     else {
266       NSLog(@"%s: could not get value of key %@ (foreignKey=%@)",
267             __PRETTY_FUNCTION__, key, fkey);
268     }
269   }
270     
271   return AUTORELEASE([[NSDictionary alloc]
272                                     initWithObjects:vals
273                                     forKeys:keys count:i]);
274 }
275
276 - (NSString *)description {
277   return [[self propertyList] description];
278 }
279
280 @end /* EORelationship */
281
282
283 @implementation EORelationship (EORelationshipPrivate)
284
285 + (EORelationship *)relationshipFromPropertyList:(id)_plist
286   model:(EOModel *)model
287 {
288   NSDictionary   *plist = _plist;
289   EORelationship *relationship = nil;
290   NSArray        *array      = nil;
291   NSEnumerator   *enumerator = nil;
292   id             joinPList   = nil;
293
294   relationship = [[[EORelationship alloc] init] autorelease];
295   [relationship setCreateMutableObjects:YES];
296   [relationship setName:[plist objectForKey:@"name"]];
297   [relationship setUserDictionary:
298                 [plist objectForKey:@"userDictionary"]];
299
300   if ((array = [plist objectForKey:@"joins"])) {
301     enumerator = [array objectEnumerator];
302
303     joinPList = [enumerator nextObject];
304     [relationship loadJoinPropertyList:joinPList];
305     joinPList = [enumerator nextObject];
306     NSAssert(joinPList == nil, @"a relationship only supports one join !");
307   }
308   else {
309     [relationship loadJoinPropertyList:_plist];
310   }
311   
312   relationship->destinationEntity =
313     RETAIN([plist objectForKey:@"destination"]);
314   // retained string
315
316   relationship->flags.isToMany =
317     [[plist objectForKey:@"isToMany"] isEqual:@"Y"];
318
319   relationship->flags.isMandatory =
320     [[plist objectForKey:@"isMandatory"] isEqual:@"Y"];
321   
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"]);
326
327   return relationship;
328 }
329
330 - (void)replaceStringsWithObjects {
331   EOModel *model = [self->entity model];
332   
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
338     
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];
344     }
345   }
346
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];
351   }
352
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];
357   }
358   
359   if (self->sourceAttribute) {
360     EOEntity    *attributeEntity;
361     EOAttribute *attribute = nil;
362     
363 #if 0
364     attributeEntity = self->flags.isToMany
365       ? self->destinationEntity
366       : self->entity;
367 #endif
368     attributeEntity = self->entity;
369     attribute =
370       [attributeEntity attributeNamed:(NSString*)self->sourceAttribute];
371     
372     if (attribute)
373       [self setSourceAttribute:attribute];
374     else {
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]);
380     }
381
382 #if 0
383     attributeEntity = self->flags.isToMany
384       ? self->entity
385       : self->destinationEntity;
386 #endif
387     attributeEntity = self->destinationEntity;
388     attribute = [attributeEntity attributeNamed:(NSString*)destinationAttribute];
389     
390     if (attribute)
391       [self setDestinationAttribute:attribute];
392     else {
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]);
398     }
399   }
400   [self setCreateMutableObjects:NO];
401 }
402
403 - (void)initFlattenedRelationship {
404   if (self->definition) {
405     NS_DURING
406       [self setDefinition:self->definition];
407     NS_HANDLER {
408       NSLog([localException reason]);
409       [[self->entity model] errorInReading];
410     }
411     NS_ENDHANDLER;
412   }
413 }
414
415 - (id)propertyList {
416   NSMutableDictionary *propertyList = nil;
417
418   propertyList = [NSMutableDictionary dictionary];
419   [self encodeIntoPropertyList:propertyList];
420   return propertyList;
421 }
422
423 - (void)setCreateMutableObjects:(BOOL)flag {
424   if (self->flags.createsMutableObjects == flag)
425     return;
426
427   self->flags.createsMutableObjects = flag;
428 }
429
430 - (BOOL)createsMutableObjects {
431   return self->flags.createsMutableObjects;
432 }
433
434 - (int)compareByName:(EORelationship *)_other {
435   return [[(EORelationship *)self name] compare:[_other name]];
436 }
437
438 /* EOJoin */
439
440 - (void)loadJoinPropertyList:(id)propertyList {
441   NSDictionary *plist = propertyList;
442   NSString *joinOperatorPList;
443   NSString *joinSemanticPList;
444   id tmp;
445
446   tmp = [plist objectForKey:@"sourceAttribute"];
447   [self setSourceAttribute:tmp];
448   tmp = [plist objectForKey:@"destinationAttribute"];
449   [self setDestinationAttribute:tmp];
450   
451   if ((joinOperatorPList = [plist objectForKey:@"joinOperator"])) {
452     NSAssert([joinOperatorPList isEqual:@"EOJoinEqualTo"],
453              @"only EOJoinEqualTo is supported as the join operator !");
454   }
455
456   if ((joinSemanticPList = [plist objectForKey:@"joinSemantic"])) {
457     NSAssert([joinSemanticPList isEqual:@"EOInnerJoin"],
458              @"only EOInnerJoin is supported as the join semantic !");
459   }
460 }
461
462 - (void)setDestinationAttribute:(EOAttribute*)attribute {
463   ASSIGN(self->destinationAttribute, attribute);
464 }
465 - (EOAttribute*)destinationAttribute {
466   return self->destinationAttribute;
467 }
468
469 - (void)setSourceAttribute:(EOAttribute *)attribute {
470   ASSIGN(self->sourceAttribute, attribute);
471 }
472 - (EOAttribute*)sourceAttribute {
473   return self->sourceAttribute;
474 }
475
476 - (EOJoinOperator)joinOperator {
477   return EOJoinEqualTo;
478 }
479 - (EOJoinSemantic)joinSemantic {
480   return EOInnerJoin;
481 }
482
483 - (EORelationship*)relationship {
484   return self;
485 }
486
487 // misc
488
489 - (void)addJoin:(EOJoin *)_join {
490   [self setSourceAttribute:[_join sourceAttribute]];
491   [self setDestinationAttribute:[_join destinationAttribute]];
492 }
493
494 /* PropertyListCoding */
495
496 static inline void _addToPropList(NSMutableDictionary *_plist,
497                                   id _value, NSString *key) {
498   if (_value) [_plist setObject:_value forKey:key];
499 }
500
501 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
502   _addToPropList(_plist, self->name,           @"name");
503   _addToPropList(_plist, self->definition,     @"definition");
504   _addToPropList(_plist, self->userDictionary, @"userDictionary");
505
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],
511                    @"destination");
512   }
513   
514   if (![self isFlattened] && self->destinationEntity) {
515     _addToPropList(_plist, [self->destinationEntity name], @"destination");
516   }
517
518   if (![self isFlattened])
519     _addToPropList(_plist, flags.isToMany ? @"Y" : @"N", @"isToMany");
520   
521   if (![self isMandatory])
522     _addToPropList(_plist, flags.isMandatory ? @"Y" : @"N", @"isMandatory");
523 }
524
525 /* EOF2Additions */
526
527 /* constraints */
528
529 - (void)setIsMandatory:(BOOL)_flag {
530   self->flags.isMandatory = _flag ? 1 : 0;
531 }
532 - (BOOL)isMandatory {
533   return self->flags.isMandatory ? YES : NO;
534 }
535
536 - (NSException *)validateValue:(id *)_value {
537   if (_value == NULL) return nil;
538   
539   /* check 'mandatory' constraint */
540   
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);
547       }
548     }
549     else {
550       if ((*_value == nil) || (*_value == null)) {
551         NSLog(@"WARNING(%s): tried to use value %@"
552               @"with mandatory toOne relationship %@",
553               __PRETTY_FUNCTION__, *_value, self);
554       }
555     }
556   }
557   
558   return nil;
559 }
560
561 @end /* EORelationship */
562
563 @implementation EOJoin
564 @end /* EOJoin */