1/*
2  Copyright (C) 2000-2005 SKYRIX Software AG
3
4  This file is part of SOPE.
5
6  SOPE is free software; you can redistribute it and/or modify it under
7  the terms of the GNU Lesser General Public License as published by the
8  Free Software Foundation; either version 2, or (at your option) any
9  later version.
10
11  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12  WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14  License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with SOPE; see the file COPYING.  If not, write to the
18  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19  02111-1307, USA.
20*/
21
22#include <EOControl/EOQualifier.h>
23#include <EOControl/EONull.h>
24#include "common.h"
25
26@implementation EOKeyComparisonQualifier
27
28static EONull *null = nil;
29
30+ (void)initialize {
31  if (null == nil)
32    null = [[EONull null] retain];
33}
34
35- (id)initWithLeftKey:(NSString *)_leftKey
36  operatorSelector:(SEL)_selector
37  rightKey:(NSString *)_rightKey;
38{
39  self->leftKey  = [_leftKey  copyWithZone:NULL];
40  self->rightKey = [_rightKey copyWithZone:NULL];
41  self->operator = _selector;
42  return self;
43}
44
45- (void)dealloc {
46  [self->leftKey  release];
47  [self->rightKey release];
48  [super dealloc];
49}
50
51/* accessors */
52
53- (NSString *)leftKey {
54  return self->leftKey;
55}
56- (NSString *)rightKey {
57  return self->rightKey;
58}
59- (SEL)selector {
60  return self->operator;
61}
62
63/* bindings */
64
65- (EOQualifier *)qualifierWithBindings:(NSDictionary *)_bindings
66  requiresAllVariables:(BOOL)_reqAll
67{
68  static Class VarClass = Nil;
69  NSString *newLeftKey;
70  id       newRightKey;
71  BOOL     needNew;
72
73  if (VarClass == Nil) VarClass = [EOQualifierVariable class];
74  needNew = NO;
75
76  if ([self->leftKey class] == VarClass) {
77    newLeftKey =
78      [_bindings objectForKey:[(EOQualifierVariable *)self->leftKey key]];
79    if (newLeftKey == nil) {
80      if (_reqAll)
81        // throw exception
82        ;
83      else
84        newLeftKey = self->leftKey;
85    }
86    else
87      needNew = YES;
88  }
89  else
90    newLeftKey = self->leftKey;
91
92  if ([self->rightKey class] == VarClass) {
93    newRightKey =
94      [_bindings objectForKey:[(EOQualifierVariable *)self->rightKey key]];
95    if (newRightKey == nil) {
96      if (_reqAll)
97        // throw exception
98        ;
99      else
100        newRightKey = self->rightKey;
101    }
102    else
103      needNew = YES;
104  }
105  else
106    newRightKey = self->rightKey;
107
108  if (!needNew)
109    return self;
110
111  return [[[[self class] alloc]
112                         initWithLeftKey:newLeftKey
113                         operatorSelector:self->operator
114                         rightKey:newRightKey]
115                         autorelease];
116}
117
118- (NSArray *)bindingKeys {
119  static Class VarClass = Nil;
120  Class lkClass, rkClass;
121  if (VarClass == Nil) VarClass = [EOQualifierVariable class];
122
123  lkClass = [self->leftKey  class];
124  rkClass = [self->rightKey class];
125
126  if ((lkClass == VarClass) && (rkClass == VarClass)) {
127    id o[2];
128    o[0] = [(EOQualifierVariable *)self->leftKey  key];
129    o[1] = [(EOQualifierVariable *)self->rightKey key];
130    return [NSArray arrayWithObjects:o count:2];
131  }
132
133  if (lkClass == VarClass)
134    return [NSArray arrayWithObject:[(EOQualifierVariable *)self->leftKey key]];
135  if (rkClass == VarClass) {
136    return [NSArray arrayWithObject:
137                      [(EOQualifierVariable *)self->rightKey key]];
138  }
139  return [NSArray array];
140}
141
142/* keys */
143
144- (void)addQualifierKeysToSet:(NSMutableSet *)_keys {
145  /* new in WO 4.5 */
146  [_keys addObject:self->leftKey];
147  [_keys addObject:self->rightKey];
148}
149
150/* evaluation */
151
152- (BOOL)evaluateWithObject:(id)_object inEvalContext:(id)_ctx {
153  id   lv, rv;
154  BOOL (*m)(id, SEL, id);
155
156  if (_ctx == nil)
157    _ctx = [NSMutableDictionary dictionaryWithCapacity:16];
158
159  if ((lv = [(NSDictionary *)_ctx objectForKey:self->leftKey]) == nil) {
160    lv = [_object valueForKeyPath:self->leftKey];
161    if (lv == nil) lv = null;
162    [(NSMutableDictionary *)_ctx setObject:lv forKey:self->leftKey];
163  }
164  if ((rv = [(NSDictionary *)_ctx objectForKey:self->rightKey]) == nil) {
165    rv = [_object valueForKeyPath:self->rightKey];
166    if (rv == nil) rv = null;
167    [(NSMutableDictionary *)_ctx setObject:rv forKey:self->rightKey];
168  }
169
170  if ((m = (void *)[lv methodForSelector:self->operator]) == NULL) {
171    /* no such operator method ! */
172    [lv doesNotRecognizeSelector:self->operator];
173    return NO;
174  }
175
176  return m(lv, self->operator, rv);
177}
178- (BOOL)evaluateWithObject:(id)_object {
179  return [self evaluateWithObject:_object inEvalContext:nil];
180}
181
182/* NSCoding */
183
184- (void)encodeWithCoder:(NSCoder *)_coder {
185  [_coder encodeObject:self->leftKey];
186  [_coder encodeObject:self->rightKey];
187  [_coder encodeValueOfObjCType:@encode(SEL) at:&(self->operator)];
188}
189- (id)initWithCoder:(NSCoder *)_coder {
190  self->leftKey  = [[_coder decodeObject] copyWithZone:[self zone]];
191  self->rightKey = [[_coder decodeObject] copyWithZone:[self zone]];
192  [_coder decodeValueOfObjCType:@encode(SEL) at:&(self->operator)];
193  return self;
194}
195
196/* Comparing */
197
198- (BOOL)isEqualToQualifier:(EOQualifier *)_qual {
199  if (![self->leftKey isEqual:[(EOKeyComparisonQualifier *)_qual leftKey]])
200    return NO;
201  if (![self->rightKey isEqual:[(EOKeyComparisonQualifier *)_qual rightKey]])
202    return NO;
203  if (sel_isEqual(self->operator, [(EOKeyComparisonQualifier *)_qual selector]))
204    return YES;
205  return NO;
206}
207
208/* remapping keys */
209
210- (EOQualifier *)qualifierByApplyingTransformer:(id)_transformer
211  inContext:(id)_ctx
212{
213  if ([_transformer respondsToSelector:
214                      @selector(transformKeyComparisonQualifier:inContext:)]) {
215    return [_transformer transformKeyComparisonQualifier:self inContext:_ctx];
216  }
217  else
218    return [[self retain] autorelease];
219}
220
221- (EOQualifier *)qualifierByApplyingKeyMap:(NSDictionary *)_map {
222  EOKeyComparisonQualifier *kcq;
223  NSString *l, *r;
224
225  l = [_map objectForKey:self->leftKey];
226  if (l == nil) l = self->leftKey;
227  r = [_map objectForKey:self->rightKey];
228  if (r == nil) r = self->rightKey;
229
230  kcq = [[EOKeyComparisonQualifier alloc]
231	  initWithLeftKey:l operatorSelector:self->operator rightKey:r];
232  return [kcq autorelease];
233}
234
235/* key/value archiving */
236
237- (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
238  if ((self = [super initWithKeyValueUnarchiver:_unarchiver]) != nil) {
239    NSString *s;
240
241    self->leftKey  = [[_unarchiver decodeObjectForKey:@"leftKey"]  retain];
242    self->rightKey = [[_unarchiver decodeObjectForKey:@"rightKey"] retain];
243
244    if ((s = [_unarchiver decodeObjectForKey:@"selectorName"]) != nil) {
245      if (![s hasSuffix:@":"]) s = [s stringByAppendingString:@":"];
246      self->operator = NSSelectorFromString(s);
247    }
248    else if ((s = [_unarchiver decodeObjectForKey:@"selector"]) != nil)
249      self->operator = NSSelectorFromString(s);
250  }
251  return self;
252}
253- (void)encodeWithKeyValueArchiver:(EOKeyValueArchiver *)_archiver {
254  NSString *s;
255
256  [super encodeWithKeyValueArchiver:_archiver];
257
258  [_archiver encodeObject:[self leftKey]  forKey:@"leftKey"];
259  [_archiver encodeObject:[self rightKey] forKey:@"rightKey"];
260
261  s = NSStringFromSelector([self selector]);
262  if ([s hasSuffix:@":"]) s = [s substringToIndex:[s length] - 1];
263  [_archiver encodeObject:s forKey:@"selectorName"];
264}
265
266/* description */
267
268- (NSString *)description {
269  NSMutableString *s;
270
271  s = [NSMutableString stringWithCapacity:64];
272  [s appendString:self->leftKey];
273  [s appendString:@" "];
274  [s appendString:[EOQualifier stringForOperatorSelector:self->operator]];
275  [s appendString:@" "];
276  [s appendString:self->rightKey];
277  return s;
278}
279
280@end /* EOKeyComparisonQualifier */
281