]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOSQLQualifier.m
more directory hierarchy reorganisations,
[sope] / sope-gdl1 / GDLAccess / EOSQLQualifier.m
1 /* 
2    EOSQLQualifier.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@apache.org>
7            Helge Hess <helge.hess@opengroupware.org>
8    Date:   September 1996
9            November  1999
10
11    This file is part of the GNUstep Database Library.
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Library General Public
15    License as published by the Free Software Foundation; either
16    version 2 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Library General Public License for more details.
22
23    You should have received a copy of the GNU Library General Public
24    License along with this library; see the file COPYING.LIB.
25    If not, write to the Free Software Foundation,
26    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 #include <stdio.h>
30 #import "common.h"
31 #import "EOSQLQualifier.h"
32 #import "EOAdaptor.h"
33 #import "EOAttribute.h"
34 #import "EOEntity.h"
35 #import "EOExpressionArray.h"
36 #import "EOFExceptions.h"
37 #import "EORelationship.h"
38 #import "EOSQLExpression.h"
39 #include <EOControl/EOKeyValueCoding.h>
40 #include <EOControl/EONull.h>
41 #import "EOQualifierScanner.h"
42
43 #if LIB_FOUNDATION_LIBRARY
44 #  include <extensions/DefaultScannerHandler.h>
45 #  include <extensions/PrintfFormatScanner.h>
46 #else
47 #  include "DefaultScannerHandler.h"
48 #  include "PrintfFormatScanner.h"
49 #endif
50
51 @interface EOQualifierJoinHolder : NSObject
52 {
53   id source;
54   id destination;
55 }
56 + (id)valueForSource:(id)source destination:(id)destination;
57 - (NSString*)expressionValueForContext:(EOSQLExpression*)context;
58 - (id)source;
59 - (id)destination;
60 @end
61
62 @implementation EOQualifierJoinHolder
63
64 static Class  AttributeClass = Nil;
65 static EONull *null          = nil;
66
67 + (void)initialize {
68   AttributeClass = [EOAttribute class];
69   null           = [[NSNull null] retain];
70 }
71
72 + (id)valueForSource:(id)_source destination:(id)_destination {
73   EOQualifierJoinHolder *value;
74   
75   value              = [[[self alloc] init] autorelease];
76   value->source      = [_source      retain];
77   value->destination = [_destination retain];
78   return value;
79 }
80
81 - (NSString *)expressionValueForContext:(EOSQLExpression *)context {
82   NSMutableString *result         = nil;
83   EOAdaptor       *adaptor        = nil;
84   NSString        *formattedLeft  = nil;
85   NSString        *formattedRight = nil;
86   BOOL            checkNull       = NO;
87
88   adaptor = [context adaptor];
89   
90   if ([source isKindOfClass:AttributeClass]) {
91     formattedLeft = [source expressionValueForContext:context];
92   }
93   else {
94     NSAssert([destination isKindOfClass:AttributeClass],
95              @"either one of source or destination should be EOAttribute");
96     if ([source isEqual:null] || (source == nil))
97       checkNull = YES;
98     
99     formattedLeft = [adaptor formatValue:source?source:null
100                              forAttribute:destination];
101   }
102
103   if ([destination isKindOfClass:AttributeClass]) {
104     NSString *tmp = formattedLeft;
105     
106     formattedLeft  = [destination expressionValueForContext:context];
107     formattedRight = tmp;
108   }
109   else {
110     NSAssert([source isKindOfClass:AttributeClass],
111              @"either one of source or destination should be EOAttribute");
112     
113     if ([destination isEqual:null] || (destination == nil))
114       checkNull = YES;
115     
116     formattedRight = [adaptor formatValue:destination?destination:null
117                               forAttribute:source];
118   }
119
120   result = [NSMutableString stringWithCapacity:64];
121   [result appendString:formattedLeft];
122   [result appendString:checkNull ? @" IS " : @"="];
123   [result appendString:formattedRight];
124   return result;
125 }
126
127 - (id)source {
128     return self->source;
129 }
130 - (id)destination {
131     return self->destination;
132 }
133
134 @end /* EOQualifierJoinHolder */
135
136
137 @implementation EOSQLQualifier
138
139 + (EOSQLQualifier*)qualifierForRow:(NSDictionary*)row
140   entity:(EOEntity*)_entity
141 {
142   EOSQLQualifier  *qualifier     = nil;
143   NSEnumerator    *enumerator    = nil;
144   NSString        *attributeName = nil;
145   EOAttribute     *attribute     = nil;
146   id              value          = nil;
147   BOOL            first          = YES;
148
149   enumerator = [row keyEnumerator];    
150   qualifier  = [[[EOSQLQualifier alloc] init] autorelease];
151
152   while ((attributeName = [enumerator nextObject])) {
153     attribute = [_entity attributeNamed:attributeName];
154     value = [row objectForKey:attributeName];
155
156     if ((value == nil) || (attribute == nil))
157       /* return nil when is unable to build a qualifier for all keys
158          in the given row
159       */
160       return nil;
161
162     if (first)
163       first = NO;
164     else
165       [qualifier->content addObject:@" AND "];
166
167     [qualifier->content addObject:
168               [EOQualifierJoinHolder valueForSource:attribute destination:value]];
169   }
170
171   qualifier->entity = RETAIN(_entity);
172   [qualifier _computeRelationshipPaths];
173
174   return qualifier;
175 }
176
177 + (EOSQLQualifier*)qualifierForPrimaryKey:(NSDictionary*)dictionary
178   entity:(EOEntity*)_entity
179 {
180   NSDictionary *pkey = nil;
181
182   pkey = [_entity primaryKeyForRow:dictionary];
183     /* return nil when is unable to build a complete qualifier
184        for all primary key attributes
185     */
186   return pkey ? [self qualifierForRow:pkey entity:_entity] : nil;
187 }
188
189 + (EOSQLQualifier*)qualifierForRow:(NSDictionary*)row 
190   relationship:(EORelationship*)relationship
191 {
192   NSArray        *componentRelationships = nil;
193   EOSQLQualifier *qualifier              = nil;
194   NSArray        *sourceAttributes       = nil;
195   id             tmpRelationship         = nil;
196   EOAttribute    *sourceAttribute        = nil;
197   EOAttribute    *destinationAttribute   = nil;
198   id             value                   = nil;
199   int            j                       = 0;
200   int            count2                  = 0;
201
202   componentRelationships = [relationship componentRelationships];
203   tmpRelationship        = relationship;
204   qualifier              = [[[EOSQLQualifier alloc] init] autorelease];
205
206   /* Make a qualifier string in the following manner. If the relationship is
207      not flattened we must join using the join operator the values from `row'
208      and the foreign keys taken from the destination entity of relatioship.
209      If the relationship is flattend we must append then joins between the
210      components of relationship. */
211
212   if (componentRelationships) {
213     tmpRelationship = [componentRelationships objectAtIndex:0];
214         
215     sourceAttributes =
216       [NSArray arrayWithObject:[tmpRelationship sourceAttribute]];
217   }
218   else {
219     sourceAttributes =
220       [NSArray arrayWithObject:[relationship sourceAttribute]];
221   }
222
223   sourceAttribute = [tmpRelationship sourceAttribute];
224   value           = [row objectForKey:[sourceAttribute name]];
225   if (value == nil)
226     /* Returns nil if `row' does not contain all the values needed to 
227        create a complete qualifier
228     */
229     return nil;
230
231   destinationAttribute = [tmpRelationship destinationAttribute];
232   [qualifier->content addObject:
233             [EOQualifierJoinHolder valueForSource:destinationAttribute
234                                    destination:value]];
235
236   if (componentRelationships) {
237     EOEntity *tempEntity = [tmpRelationship destinationEntity];
238
239     /* The relationship is flattened. Iterate over the components and 
240        add joins that `link' the components between them.
241     */
242     count2 = [componentRelationships count];
243     for (j = 1; j < count2; j++) {
244       relationship = [componentRelationships objectAtIndex:j];
245             
246       if ([relationship sourceAttribute]) {
247         [qualifier->content addObject:@" AND "];
248         [qualifier->content addObject:
249                   [EOQualifierJoinHolder valueForSource:
250                                          [relationship sourceAttribute]
251                                          destination:
252                                          [relationship destinationAttribute]]];
253       }
254     }
255
256     /* Here we make a hack because later we need to use this qualifier in
257        a SELECT expression in which the qualifier's entity should be the
258        final destination entity of the flattened relationship. In addition
259        we need in the FROM clause all the entities corresponding to the
260        components of the relationship to be able to insert the joins
261        between the values given in row and the final attributes from the
262        destination entity of the last component of relationship. */
263     ASSIGN(qualifier->entity, tempEntity);
264     [qualifier _computeRelationshipPaths];
265     ASSIGN(qualifier->entity, [relationship destinationEntity]);
266     return qualifier;
267   }
268   else {
269     ASSIGN(qualifier->entity, [relationship destinationEntity]);
270     return qualifier;
271   }
272 }
273
274 + (EOSQLQualifier *)qualifierForObject:sourceObject 
275   relationship:(EORelationship *)relationship
276 {
277   return [self qualifierForRow:
278                  [sourceObject valueForKey:[[relationship sourceAttribute] name]]
279                relationship:relationship];
280 }
281
282 - (id)init {
283   NSZone *z = [self zone];
284     
285   RELEASE(self->content);            self->content            = nil;
286   RELEASE(self->relationshipPaths);  self->relationshipPaths  = nil;
287   RELEASE(self->additionalEntities); self->additionalEntities = nil;
288
289   self->content            = [[EOExpressionArray allocWithZone:z] init];
290   self->relationshipPaths  = [[NSMutableSet allocWithZone:z] init];
291   self->additionalEntities = [[NSMutableSet allocWithZone:z] init];
292   return self;
293 }
294
295 - (id)initWithEntity:(EOEntity *)_entity 
296   qualifierFormat:(NSString *)_qualifierFormat
297   argumentsArray:(NSArray *)_args
298 {
299   PrintfFormatScanner           *formatScanner       = nil;
300   EOQualifierEnumScannerHandler *scannerHandler      = nil;
301   NSString                      *qualifierString     = nil;
302   NSMutableArray                *myRelationshipPaths = nil;
303   NSEnumerator                  *args                = nil;
304
305   myRelationshipPaths = [[NSMutableArray allocWithZone:[self zone]] init];
306     
307   [self init];
308   ASSIGN(self->entity, _entity);
309
310   if (_qualifierFormat == nil)
311     return self;
312
313   formatScanner  = [[PrintfFormatScanner alloc] init];
314   scannerHandler = [[EOQualifierEnumScannerHandler alloc] init];
315   [formatScanner setAllowOnlySpecifier:YES];
316
317   args = [_args objectEnumerator];
318   [scannerHandler setEntity:_entity];
319     
320   [formatScanner setFormatScannerHandler:scannerHandler];
321   /*
322     Note: This is an ugly hack. Arguments is supposed to be a va_args
323           structure, but an NSArray is passed in.
324           It works because the value is casted to -parseFormatString:context:
325           which gives control to the scannerHandler which casts the va_args
326           back to an array (the EOQualifierEnumScannerHandler does that).
327           Works on ix86, but *NOT* on iSeries or zServer !!
328   */
329 #if defined(__s390__)
330   qualifierString =
331     [formatScanner performSelector:@selector(stringWithFormat:arguments:)
332                    withObject:_qualifierFormat
333                    withObject:args];
334 #else
335   qualifierString = 
336     [formatScanner stringWithFormat:_qualifierFormat
337                    arguments:args];
338 #endif
339
340   [formatScanner  release]; formatScanner  = nil;
341   [scannerHandler release]; scannerHandler = nil;
342   
343   [self->content release]; self->content = nil;
344   self->content =
345          [[EOExpressionArray parseExpression:qualifierString
346                             entity:entity
347                             replacePropertyReferences:YES
348                             relationshipPaths:myRelationshipPaths]
349            retain];
350   [self _computeRelationshipPaths:myRelationshipPaths];
351   [myRelationshipPaths release]; myRelationshipPaths = nil;
352   return self;
353 }
354
355 - (id)initWithEntity:(EOEntity*)_entity 
356   qualifierFormat:(NSString *)qualifierFormat, ...
357 {
358   va_list        ap;
359   id             formatScanner        = nil;
360   id             scannerHandler       = nil;
361   NSString       *qualifierString     = nil;
362   NSMutableArray *myRelationshipPaths = nil;
363
364   if ((self = [self init]) == nil)
365     return nil;
366   
367   myRelationshipPaths = [[NSMutableArray alloc] init];
368   ASSIGN(self->entity, _entity);
369   
370   if (qualifierFormat == nil) {
371     return self;
372   }
373   
374   formatScanner  = [[PrintfFormatScanner alloc] init];
375   scannerHandler = [[EOQualifierScannerHandler alloc] init];
376   [formatScanner setAllowOnlySpecifier:YES];
377   
378   va_start(ap, qualifierFormat);
379   [scannerHandler setEntity:_entity];
380   [formatScanner setFormatScannerHandler:scannerHandler];
381   qualifierString = [formatScanner stringWithFormat:qualifierFormat
382                                    arguments:ap];
383   va_end(ap);
384
385   [formatScanner  release];
386   [scannerHandler release];
387
388   [self->content release]; self->content = nil;
389   self->content =
390          [[EOExpressionArray parseExpression:qualifierString
391                             entity:entity
392                             replacePropertyReferences:YES
393                             relationshipPaths:myRelationshipPaths] retain];
394   [self _computeRelationshipPaths:myRelationshipPaths];
395   [myRelationshipPaths release]; myRelationshipPaths = nil;
396   return self;
397 }
398
399 - (void)_computeRelationshipPaths {
400   [self _computeRelationshipPaths:nil];
401 }
402
403 static void
404 handle_attribute(EOSQLQualifier *self, id object, id _relationshipPaths)
405 {
406   if ([object isFlattened]) {
407     id      definitionArray = nil;
408     id      relsArray       = nil;    
409     NSRange range;
410
411     definitionArray = [object definitionArray];
412     range           = NSMakeRange(0, [definitionArray count] - 1);
413     relsArray       = [definitionArray subarrayWithRange:range];
414
415     [self->relationshipPaths addObject:relsArray];
416     [self->additionalEntities addObjectsFromArray:relsArray];
417   }
418   else {
419     [self->additionalEntities addObject:[object entity]];
420   }
421 }
422
423 - (void)_computeRelationshipPaths:(NSArray *)_relationshipPaths {
424   int i, count;
425
426   [relationshipPaths removeAllObjects];
427
428   if (_relationshipPaths) {
429     NSEnumerator *pathEnum = [_relationshipPaths objectEnumerator];
430     NSArray      *relPath  = nil;
431         
432     while ((relPath = [pathEnum nextObject])) {
433       NSEnumerator   *relEnum = nil;
434       EORelationship *rel     = nil;
435
436       relEnum = [relPath objectEnumerator];
437
438       while ((rel = [relEnum nextObject])) {
439         [additionalEntities addObject:[rel destinationEntity]];
440       }
441     }
442     [relationshipPaths addObjectsFromArray:_relationshipPaths];
443   }
444   for (i = 0, count = [content count]; i < count; i++) {
445     id object = [content objectAtIndex:i];
446
447     /* The objects from content can only be NSString, values or
448        EOAttribute. */
449     if ([object isKindOfClass:[EOAttribute class]]) {
450       handle_attribute (self, object, _relationshipPaths);
451     }
452     else if ([object isKindOfClass:[EOQualifierJoinHolder class]]) {
453       id source      = nil;
454       id destination = nil;
455
456       source      = [object source];
457       destination = [object destination];
458
459       if ([source isKindOfClass:[EOAttribute class]])
460         handle_attribute (self, source, _relationshipPaths);
461       if ([destination isKindOfClass:[EOAttribute class]])
462         handle_attribute (self, destination, _relationshipPaths);
463     }
464     else if ([object isKindOfClass:[EORelationship class]]) {
465       [[[InvalidPropertyException alloc]
466                                        initWithFormat:@"cannot refer a EORelat"
467                                        @"ionship in a EOSQLQualifier: '%@'",
468                                        [(EORelationship*)object name]] raise];
469     }
470   }
471 }
472
473 - (void)dealloc {
474   RELEASE(self->relationshipPaths);
475   RELEASE(self->additionalEntities);
476   
477   RELEASE(self->entity);
478   RELEASE(self->content);
479   [super dealloc];
480 }
481
482 - (id)copy {
483   return [self copyWithZone:NSDefaultMallocZone()];
484 }
485
486 - (id)copyWithZone:(NSZone*)zone {
487   EOSQLQualifier* copy = nil;
488
489   copy                    = [[self->isa allocWithZone:zone] init];
490   copy->entity            = RETAIN(self->entity);
491   copy->content           = [self->content           mutableCopyWithZone:zone];
492   copy->relationshipPaths = [self->relationshipPaths mutableCopyWithZone:zone];
493   copy->usesDistinct      = self->usesDistinct;
494   return copy;
495 }
496
497 - (void)negate {
498   [self->content insertObject:@"NOT (" atIndex:0];
499   [self->content addObject:@")"];
500 }
501
502 - (void)conjoinWithQualifier:(EOSQLQualifier*)qualifier {
503   if (![qualifier isKindOfClass:[EOSQLQualifier class]]) {
504     [NSException raise:NSInvalidArgumentException
505                  format:@"argument of conjoinWithQualifier: method must "
506                      @"be EOSQLQualifier"];
507   }
508
509   if (self->entity != qualifier->entity) {
510     [NSException raise:NSInvalidArgumentException
511                  format:@"qualifier argument of conjoinWithQualifier: "
512                              @"must have the same entity as receiver"];
513   }
514
515   [self->content insertObject:@"(" atIndex:0];
516   [self->content addObject:@") AND ("];
517   [self->content addObjectsFromExpressionArray:qualifier->content];
518   [self->content addObject:@")"];
519   [self->relationshipPaths unionSet:qualifier->relationshipPaths];
520 }
521
522 - (void)disjoinWithQualifier:(EOSQLQualifier*)qualifier {
523   if (![qualifier isKindOfClass:[EOSQLQualifier class]]) {
524     [NSException raise:NSInvalidArgumentException
525                  format:@"argument of disjoinWithQualifier: method must "
526                     @"be EOSQLQualifier"];
527   }
528
529   if (self->entity != qualifier->entity) {
530     [NSException raise:NSInvalidArgumentException
531                  format:@"qualifier argument of disjoinWithQualifier: "
532                     @"must have the same entity as receiver"];
533   }
534
535   [self->content insertObject:@"(" atIndex:0];
536   [self->content addObject:@") OR ("];
537   [self->content addObjectsFromExpressionArray:qualifier->content];
538   [self->content addObject:@")"];
539   [self->relationshipPaths unionSet:qualifier->relationshipPaths];
540 }
541
542 - (EOEntity*)entity {
543   return self->entity;
544 }
545 - (BOOL)isEmpty {
546   return (self->entity == nil) ? YES : NO;
547 }
548 - (void)setUsesDistinct:(BOOL)flag {
549   self->usesDistinct = flag;
550 }
551 - (BOOL)usesDistinct {
552   return self->usesDistinct;
553 }
554 - (NSMutableSet*)relationshipPaths {
555   return self->relationshipPaths;
556 }
557 - (NSMutableSet*)additionalEntities {
558   return self->additionalEntities;
559 }
560
561 - (NSString*)expressionValueForContext:(id<EOExpressionContext>)ctx {
562     return [self->content expressionValueForContext:ctx];
563 }
564
565 - (EOSQLQualifier *)sqlQualifierForEntity:(EOEntity *)_entity {
566   NSAssert3(self->entity == _entity,
567             @"passed invalid entity to %@ (contains %@, got %@)",
568             self, self->entity, _entity);
569   return (EOSQLQualifier *)self;
570 }
571
572 /* description */
573
574 - (NSString *)description {
575   NSMutableString *ms;
576
577   ms = [NSMutableString stringWithCapacity:128];
578   [ms appendFormat:@"<0x%08X[%@]:\n", self, NSStringFromClass([self class])];
579
580   [ms appendFormat:@" entity=%@", [self->entity name]];
581   
582   if (self->content)
583     [ms appendFormat:@" content=%@", self->content];
584   
585   if (self->usesDistinct)
586     [ms appendString:@" distinct"];
587   
588   [ms appendString:@">"];
589   return ms;
590 }
591
592 @end /* EOSQLQualifier */