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 "EOQualifier+CtxEval.h"
23#import <EOControl/EOKeyValueCoding.h>
24#import <EOControl/EONull.h>
25#include "common.h"
26
27#if LIB_FOUNDATION_LIBRARY
28#  import <objc/objc-api.h>
29#  import <objc/objc.h>
30#  import <extensions/objc-runtime.h>
31#elif GNUSTEP_BASE_LIBRARY
32#if __GNU_LIBOBJC__ >= 20100911
33#  import <objc/runtime.h>
34#else
35#  import <objc/objc-api.h>
36#endif
37#else
38#  import <objc/objc.h>
39#endif
40
41static inline int countSelArgs(SEL _sel) {
42  register const char *selName;
43
44  if ((selName = sel_getName(_sel))) {
45    register int count;
46
47    for (count = 0; *selName; selName++) {
48      if (*selName == ':')
49        count++;
50    }
51    return count + 2;
52  }
53  else
54    return -1;
55}
56
57@implementation EOQualifier(ContextEvaluation)
58
59- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
60  [self doesNotRecognizeSelector:_cmd]; /* subclass */
61  return NO;
62}
63
64@end /* EOQualifier(ContextEvaluation) */
65
66@implementation NSArray(ContextEvaluation)
67
68- (NSArray *)filteredArrayUsingQualifier:(EOQualifier *)_qualifier
69  context:(id)_context
70{
71  NSMutableArray *a = nil;
72  unsigned i, count;
73
74  for (i = 0, count = [self count]; i < count; i++) {
75    id o;
76
77    o = [self objectAtIndex:i];
78
79    if ([_qualifier evaluateWithObject:o context:_context]) {
80      if (a == nil) a = [NSMutableArray arrayWithCapacity:count];
81      [a addObject:o];
82    }
83  }
84  return a ? [[a copy] autorelease] : [NSArray array];
85}
86
87@end /* NSArray(ContextEvaluation) */
88
89@implementation EOAndQualifier(ContextEvaluation)
90
91- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
92  unsigned i;
93  IMP objAtIdx;
94  NSArray *qs;
95
96  qs       = [self qualifiers];
97  objAtIdx = [qs methodForSelector:@selector(objectAtIndex:)];
98
99  for (i = 0; i < [qs count]; i++) {
100    EOQualifier *q;
101
102    q = objAtIdx(qs, @selector(objectAtIndex:), i);
103
104    if (![q evaluateWithObject:_object context:_context])
105      return NO;
106  }
107  return YES;
108}
109
110@end /* EOAndQualifier(ContextEvaluation) */
111
112@implementation EOOrQualifier(ContextEvaluation)
113
114- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
115  unsigned i;
116  IMP objAtIdx;
117  NSArray *qs;
118
119  qs       = [self qualifiers];
120  objAtIdx = [qs methodForSelector:@selector(objectAtIndex:)];
121
122  for (i = 0; i < [qs count]; i++) {
123    EOQualifier *q;
124
125    q = objAtIdx(qs, @selector(objectAtIndex:), i);
126
127    if ([q evaluateWithObject:_object context:_context])
128      return YES;
129  }
130  return NO;
131}
132
133@end /* EOOrQualifier(ContextEvaluation) */
134
135@implementation EONotQualifier(ContextEvaluation)
136
137- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
138  return
139    [[self qualifier] evaluateWithObject:_object context:_context]
140    ? NO : YES;
141}
142
143@end /* EONotQualifier(ContextEvaluation) */
144
145@implementation EOKeyValueQualifier(ContextEvaluation)
146
147- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
148  static EONull *null = nil;
149  id lv, rv;
150  union {
151    IMP  m;
152    BOOL (*unary)(id, SEL);
153    BOOL (*binary)(id, SEL, id);
154    BOOL (*ctx)(id, SEL, id, id);
155  } m;
156  SEL op;
157
158  op = [self selector];
159  lv = [_object valueForKeyPath:[self key]];
160  rv = [self value];
161
162  if (null == nil) null = [EONull null];
163  if (lv == nil) lv = null;
164  if (rv == nil) rv = null;
165
166  if ((m.m = [lv methodForSelector:op]) == NULL) {
167    /* no such operator method ! */
168    [lv doesNotRecognizeSelector:op];
169    return NO;
170  }
171  switch (countSelArgs(op)) {
172    case 0:
173    case 1:
174      NSLog(@"%s: called with invalid selector %@", __PRETTY_FUNCTION__,
175            NSStringFromSelector(op));
176      return NO;
177
178    case 2:
179      return m.unary(lv, op);
180    case 3:
181      return m.binary(lv, op, rv);
182    default:
183      return m.ctx(lv, op, rv, _context);
184  }
185}
186
187@end /* EOKeyValueQualifier(ContextEvaluation) */
188
189@implementation EOKeyComparisonQualifier(ContextEvaluation)
190
191- (BOOL)evaluateWithObject:(id)_object context:(id)_context {
192  static EONull *null = nil;
193  id lv, rv;
194  union {
195    IMP  m;
196    BOOL (*unary)(id, SEL);
197    BOOL (*binary)(id, SEL, id);
198    BOOL (*ctx)(id, SEL, id, id);
199  } m;
200  SEL op;
201
202  lv = [_object valueForKeyPath:[self leftKey]];
203  rv = [_object valueForKeyPath:[self rightKey]];
204  if (null == nil) null = [EONull null];
205  if (lv == nil) lv = null;
206  if (rv == nil) rv = null;
207
208  op = [self selector];
209
210  if ((m.m = (void *)[lv methodForSelector:op]) == NULL) {
211    /* no such operator method ! */
212    [lv doesNotRecognizeSelector:op];
213    return NO;
214  }
215  switch (countSelArgs(op)) {
216    case 0:
217    case 1:
218      NSLog(@"%s: called with invalid selector %@", __PRETTY_FUNCTION__,
219            NSStringFromSelector(op));
220      return NO;
221
222    case 2:
223      return m.unary(lv, op);
224    case 3:
225      return m.binary(lv, op, rv);
226    default:
227      return m.ctx(lv, op, rv, _context);
228  }
229}
230
231@end /* EOKeyComparisonQualifier(ContextEvaluation) */
232
233@implementation NSObject(ImplementedQualifierComparisons2)
234
235- (BOOL)isEqualTo:(id)_object inContext:(id)_context {
236  return [self isEqualTo:_object];
237}
238- (BOOL)isNotEqualTo:(id)_object inContext:(id)_context {
239  return [self isNotEqualTo:_object];
240}
241
242- (BOOL)isLessThan:(id)_object inContext:(id)_context {
243  return [self isLessThan:_object];
244}
245- (BOOL)isGreaterThan:(id)_object inContext:(id)_context {
246  return [self isGreaterThan:_object];
247}
248- (BOOL)isLessThanOrEqualTo:(id)_object inContext:(id)_context {
249  return [self isLessThanOrEqualTo:_object];
250}
251- (BOOL)isGreaterThanOrEqualTo:(id)_object inContext:(id)_context {
252  return [self isGreaterThanOrEqualTo:_object];
253}
254
255- (BOOL)doesContain:(id)_object inContext:(id)_context {
256  return [self doesContain:_object];
257}
258
259- (BOOL)isLike:(NSString *)_object inContext:(id)_context {
260  return [self isLike:_object];
261}
262- (BOOL)isCaseInsensitiveLike:(NSString *)_object inContext:(id)_context {
263  return [self isCaseInsensitiveLike:_object];
264}
265
266@end
267