]> err.no Git - sope/blob - sope-core/EOControl/EOQualifierParser.m
fixed a few warnings when compiling against GNUstep
[sope] / sope-core / EOControl / EOQualifierParser.m
1 /*
2   Copyright (C) 2000-2007 SKYRIX Software AG
3   Copyright (C) 2007      Helge Hess
4
5   This file is part of SOPE.
6
7   SOPE is free software; you can redistribute it and/or modify it under
8   the terms of the GNU Lesser General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10   later version.
11
12   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15   License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with SOPE; see the file COPYING.  If not, write to the
19   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include "EOQualifier.h"
25 #include "EONull.h"
26 #include "common.h"
27
28 //#define USE_DESCRIPTION_FOR_AT 1
29
30 static int qDebug = 0;
31 static NSMutableDictionary *EOQualifierParserTypeMappings = nil;
32
33 /* 
34    The literals understood by the value parser.
35    
36    NOTE: Any literal used here can never be used as a key ! So add as little
37    as possible.
38 */
39 typedef struct {
40   const unsigned char *token;
41   id  value;
42   int scase;
43 } EOQPTokEntry;
44
45 static EOQPTokEntry toks[] = {
46   { (const unsigned char *)"NULL",  nil, 0 },
47   { (const unsigned char *)"nil",   nil, 1 },
48   { (const unsigned char *)"YES",   nil, 0 },
49   { (const unsigned char *)"NO",    nil, 0 },
50   { (const unsigned char *)"TRUE",  nil, 0 },
51   { (const unsigned char *)"FALSE", nil, 0 },
52   { (const unsigned char *)NULL,    nil, 0 }
53 };
54
55 static inline void _setupLiterals(void) {
56   static BOOL didSetup = NO;
57   if (didSetup) return;
58   didSetup = YES;
59   toks[0].value = [[NSNull null] retain];
60   toks[1].value = toks[0].value;
61   toks[2].value = [[NSNumber numberWithBool:YES] retain];
62   toks[3].value = [[NSNumber numberWithBool:NO]  retain];
63   toks[4].value = toks[2].value;
64   toks[5].value = toks[3].value;
65 }
66
67 /* cache */
68 static Class  StringClass = Nil;
69 static Class  NumberClass = Nil;
70 static EONull *null       = nil;
71
72 /* parsing functions */
73
74 static EOQualifier *_parseCompoundQualifier(id _ctx, const char *_buf,
75                                             unsigned _bufLen, unsigned *_qualLen);
76 static EOQualifier *_testOperator(id _ctx, const char *_buf,
77                                   unsigned _bufLen, unsigned *_opLen,
78                                   BOOL *_testAnd);
79 static EOQualifier *_parseQualifiers(id _ctx, const char *_buf,
80                                      unsigned _bufLen, unsigned *_qualLen);
81 static EOQualifier *_parseParenthesisQualifier(id _ctx,
82                                                const char *_buf, unsigned _bufLen,
83                                                unsigned *_qualLen);
84 static EOQualifier *_parseNotQualifier(id _ctx, const char *_buf,
85                                        unsigned _bufLen, unsigned *_qualLen);
86 static EOQualifier *_parseKeyCompQualifier(id _ctx, const char *_buf,
87                                            unsigned _bufLen, unsigned *_qualLen);
88 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
89                            unsigned *_keyLen);
90 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
91                       unsigned *_keyLen);
92 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen);
93 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
94                           unsigned *_opLen);
95
96 @interface EOQualifierParserContext : NSObject
97 {
98   NSMapTable *qualifierCache;
99 }
100 - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos;
101 - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct
102             atPos:(unsigned)_pos;
103 - (id)getObjectFromStackFor:(char)_c;
104
105 /* factory */
106
107 - (EOQualifier *)keyComparisonQualifierWithLeftKey:(NSString *)_leftKey
108   operatorSelector:(SEL)_sel
109   rightKey:(NSString *)_rightKey;
110 - (EOQualifier *)keyValueQualifierWithKey:(NSString *)_key
111   operatorSelector:(SEL)_sel
112   value:(id)_value;
113 - (EOQualifier *)andQualifierWithArray:(NSArray *)_qualifiers;
114 - (EOQualifier *)orQualifierWithArray:(NSArray *)_qualifiers;
115 - (EOQualifier *)notQualifierWithQualifier:(EOQualifier *)_qualifier;
116
117 @end
118
119 @interface EOQualifierVAParserContext : EOQualifierParserContext
120 {
121   va_list    *va;  
122 }
123 + (id)contextWithVaList:(va_list *)_va;
124 - (id)initWithVaList:(va_list *)_va;
125 @end
126
127 @interface EOQualifierEnumeratorParserContext : EOQualifierParserContext
128 {
129   NSEnumerator *enumerator;
130 }
131 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator;
132 - (id)initWithEnumerator:(NSEnumerator  *)_enumerator;
133 @end
134
135 @implementation EOQualifierVAParserContext
136
137 + (id)contextWithVaList:(va_list *)_va {
138   return [[[EOQualifierVAParserContext alloc] initWithVaList:_va] autorelease];
139 }
140
141 - (id)initWithVaList:(va_list *)_va {
142   if ((self = [super init])) {
143     self->va = _va;
144   }
145   return self;
146 }
147
148 - (id)getObjectFromStackFor:(char)_c {
149   id obj = nil;
150
151   if (StringClass == Nil) StringClass = [NSString class];
152   if (NumberClass == Nil) NumberClass = [NSNumber class];
153   if (null == nil)        null        = [EONull null];
154   
155   if (_c == 's') {
156     char *str = va_arg(*self->va, char*);
157     obj = [StringClass stringWithCString:str];
158   }
159   else if (_c == 'd') {
160     int i= va_arg(*self->va, int);
161     obj = [NumberClass numberWithInt:i];
162   }
163   else if (_c == 'f') {
164     double d = va_arg(*self->va, double);
165     obj = [NumberClass numberWithDouble:d];
166   }
167   else if (_c == '@') {
168     id o = va_arg(*self->va, id);
169 #if USE_DESCRIPTION_FOR_AT
170     obj = (o == nil) ? (id)null : (id)[o description];
171 #else
172     obj = (o == nil) ? (id)null : (id)o;
173 #endif
174   }
175   else {
176     [NSException raise:@"NSInvalidArgumentException"
177                  format:@"unknown conversation char %c", _c];
178   }
179   return obj;
180 }
181
182 @end /* EOQualifierVAParserContext */
183
184 @implementation EOQualifierEnumeratorParserContext
185
186 + (id)contextWithEnumerator:(NSEnumerator *)_enumerator {
187   return [[[EOQualifierEnumeratorParserContext alloc]
188                                       initWithEnumerator:_enumerator] autorelease];
189 }
190
191 - (id)initWithEnumerator:(NSEnumerator *)_enumerator {
192   if ((self = [super init])) {
193     ASSIGN(self->enumerator, _enumerator);
194   }
195   return self;
196 }
197
198 - (void)dealloc {
199   [self->enumerator release];
200   [super dealloc];;
201 }
202
203 - (id)getObjectFromStackFor:(char)_c {
204   static Class NumberClass = Nil;
205   id o;
206
207   if (NumberClass == Nil) NumberClass = [NSNumber class];
208
209   o = [self->enumerator nextObject];
210   switch (_c) {
211     case '@':
212 #if USE_DESCRIPTION_FOR_AT
213       return [o description];
214 #else
215       return o;
216 #endif
217     
218     case 'f':
219       return [NumberClass numberWithDouble:[o doubleValue]];
220       
221     case 'd':
222       return [NumberClass numberWithInt:[o intValue]];
223       
224     case 's':
225       // return [NSString stringWithCString:[o cString]];
226       return [[o copy] autorelease];
227       
228     default:
229       [NSException raise:@"NSInvalidArgumentException"
230                    format:@"unknown or not allowed conversation char %c", _c];
231   }
232   return nil;
233 }
234
235 @end /* EOQualifierEnumeratorParserContext */
236
237 @implementation EOQualifierParserContext
238
239 - (id)init {
240   if (StringClass == Nil) StringClass = [NSString class];
241   
242   if ((self = [super init])) {
243     self->qualifierCache = NSCreateMapTable(NSObjectMapKeyCallBacks,
244                                             NSObjectMapValueCallBacks,
245                                             200);
246   }
247   return self;
248 }
249
250 - (void)dealloc {
251   if (self->qualifierCache) NSFreeMapTable(self->qualifierCache);
252   [super dealloc];
253 }
254
255 - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos
256 {
257   return NSMapGet(self->qualifierCache,
258                   [StringClass stringWithFormat:@"%@_%d", _fct, _pos]);
259 }
260
261 - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct
262   atPos:(unsigned)_pos
263 {
264   NSMapInsert(self->qualifierCache,
265               [StringClass stringWithFormat:@"%@_%d", _fct, _pos],
266               _dict);
267 }
268
269 - (id)getObjectFromStackFor:(char)_c {
270   [self doesNotRecognizeSelector:_cmd];
271   return nil;
272 }
273
274 /* factory */
275
276 - (EOQualifier *)keyComparisonQualifierWithLeftKey:(NSString *)_leftKey
277   operatorSelector:(SEL)_sel
278   rightKey:(NSString *)_rightKey
279 {
280   static Class clazz = Nil;
281   if (clazz == Nil) clazz = [EOKeyComparisonQualifier class];
282   
283   return [[[clazz alloc]
284                   initWithLeftKey:_leftKey
285                   operatorSelector:_sel
286                   rightKey:_rightKey]
287                   autorelease];
288 }
289 - (EOQualifier *)keyValueQualifierWithKey:(NSString *)_key
290   operatorSelector:(SEL)_sel
291   value:(id)_value
292 {
293   static Class clazz = Nil;
294   if (clazz == Nil) clazz = [EOKeyValueQualifier class];
295   
296   return [[[clazz alloc]
297                   initWithKey:_key
298                   operatorSelector:_sel
299                   value:_value]
300                   autorelease];
301 }
302 - (EOQualifier *)andQualifierWithArray:(NSArray *)_qualifiers {
303   static Class clazz = Nil;
304   if (clazz == Nil) clazz = [EOAndQualifier class];
305   
306   return [[[clazz alloc] initWithQualifierArray:_qualifiers] autorelease];
307 }
308 - (EOQualifier *)orQualifierWithArray:(NSArray *)_qualifiers {
309   static Class clazz = Nil;
310   if (clazz == Nil) clazz = [EOOrQualifier class];
311   
312   return [[[clazz alloc] initWithQualifierArray:_qualifiers] autorelease];
313 }
314
315 - (EOQualifier *)notQualifierWithQualifier:(EOQualifier *)_qualifier {
316   static Class clazz = Nil;
317   if (clazz == Nil) clazz = [EONotQualifier class];
318   
319   return [[[clazz alloc] initWithQualifier:_qualifier] autorelease];
320 }
321
322 - (EOQualifierVariable *)variableWithKey:(NSString *)_key {
323   static Class clazz = Nil;
324   if (clazz == Nil) clazz = [EOQualifierVariable class];
325
326   return [clazz variableWithKey:_key];
327 }
328
329 @end /* EOQualifierParserContext */
330
331 @implementation EOQualifier(Parsing)
332
333 + (void)registerValueClass:(Class)_valueClass forTypeName:(NSString *)_type {
334   if (EOQualifierParserTypeMappings == nil)
335     EOQualifierParserTypeMappings = [[NSMutableDictionary alloc] init];
336   
337   if (_type == nil) {
338     NSLog(@"ERROR(%s): got passed no type name!", __PRETTY_FUNCTION__);
339     return;
340   }
341   if (_valueClass == nil) {
342     NSLog(@"ERROR(%s): got passed no value-class for type '%@'!",
343           __PRETTY_FUNCTION__, _type);
344     return;
345   }
346   
347   [EOQualifierParserTypeMappings setObject:_valueClass forKey:_type];
348 }
349
350 + (EOQualifier *)qualifierWithQualifierFormat:(NSString *)_qualifierFormat,... {
351   va_list     va;
352   EOQualifier *qualifier;
353   unsigned    length = 0;
354   const char  *buf;
355   unsigned    bufLen;
356   char        *cbuf;
357
358   _setupLiterals();
359   if (StringClass == Nil) StringClass = [NSString class];
360   
361   bufLen = [_qualifierFormat cStringLength];
362   cbuf   = malloc(bufLen + 1);
363   [_qualifierFormat getCString:cbuf]; cbuf[bufLen] = '\0';
364   buf = cbuf;
365   
366   va_start(va, _qualifierFormat);
367   qualifier =
368     _parseQualifiers([EOQualifierVAParserContext contextWithVaList:&va],
369                      buf, bufLen, &length);
370   va_end(va);
371   
372   if (qualifier != nil) { /* check whether the rest of the string is OK */
373     if (length < bufLen)
374       length += _countWhiteSpaces(buf + length, bufLen - length);
375     
376     if (length < bufLen) {
377       NSLog(@"WARNING(%s): unexpected chars at the end of the "
378             @"string(class=%@,len=%i) '%@'",
379             __PRETTY_FUNCTION__,
380             [_qualifierFormat class],
381             [_qualifierFormat length], _qualifierFormat);
382       NSLog(@"  buf-length: %i", bufLen);
383       NSLog(@"  length:     %i", length);
384       NSLog(@"  char[length]: '%c' (%i) '%s'", buf[length], buf[length],
385             (buf+length));
386       qualifier = nil;
387     }
388     else if (length > bufLen) {
389       NSLog(@"WARNING(%s): length should never be longer than bufLen ?, "
390             @"internal parsing error !",
391             __PRETTY_FUNCTION__);
392     }
393   }
394   free(cbuf);
395   return qualifier;
396 }
397
398 + (EOQualifier *)qualifierWithQualifierFormat:(NSString *)_qualifierFormat 
399   arguments:(NSArray *)_arguments
400 {
401   EOQualifier *qual  = nil;
402   unsigned    length = 0;
403   const char  *buf   = NULL;
404   unsigned    bufLen = 0;
405   EOQualifierEnumeratorParserContext *ctx;
406
407   _setupLiterals();
408   if (StringClass == Nil) StringClass = [NSString class];
409   
410   ctx = [EOQualifierEnumeratorParserContext contextWithEnumerator:
411                                               [_arguments objectEnumerator]];
412   
413   //NSLog(@"qclass: %@", [_qualifierFormat class]);
414   buf    = [_qualifierFormat cString];
415   bufLen = [_qualifierFormat cStringLength];
416   qual   = _parseQualifiers(ctx, buf, bufLen, &length);
417   
418   if (qual != nil) { /* check whether the rest of the string is OK */
419     if (length < bufLen) {
420       length += _countWhiteSpaces(buf + length, bufLen - length);
421     }
422     if (length != bufLen) {
423       NSLog(@"WARNING(%s): unexpected chars at the end of the string '%@'",
424             __PRETTY_FUNCTION__, _qualifierFormat);
425       qual = nil;
426     }
427   }
428   return qual;
429 }
430  
431 @end /* EOQualifier(Parsing) */
432
433 static EOQualifier *_parseSingleQualifier(id _ctx, const char *_buf,
434                                             unsigned _bufLen,
435                                             unsigned *_qualLen)
436 {
437   EOQualifier *res = nil;
438
439   if ((res = _parseParenthesisQualifier(_ctx, _buf, _bufLen, _qualLen))  != nil) {
440     if (qDebug)
441       NSLog(@"_parseSingleQualifier return <%@> for <%s> ", res, _buf);
442
443     return res;
444   }
445   if ((res = _parseNotQualifier(_ctx, _buf, _bufLen, _qualLen)) != nil) {
446     if (qDebug)
447       NSLog(@"_parseSingleQualifier return <%@> for <%s> ", res, _buf);
448
449     return res;
450   }
451   if ((res = _parseKeyCompQualifier(_ctx, _buf, _bufLen, _qualLen)) != nil) {
452     if (qDebug) {
453       NSLog(@"_parseSingleQualifier return <%@> for <%s> length %d", 
454             res, _buf, *_qualLen);
455     }
456     return res;
457   }
458   return nil;
459 }
460
461 static EOQualifier *_parseQualifiers(id _ctx, const char *_buf, unsigned _bufLen,
462                                      unsigned *_qualLen)
463 {
464   EOQualifier *res = nil;
465
466
467   if ((res = _parseCompoundQualifier(_ctx, _buf, _bufLen, _qualLen))) {
468     if (qDebug)
469       NSLog(@"_parseQualifiers return <%@> for <%s> ", res, _buf);
470     return res;
471   }
472
473   if ((res = _parseSingleQualifier(_ctx, _buf, _bufLen, _qualLen))) {
474     if (qDebug)
475       NSLog(@"_parseQualifiers return <%@> for <%s> ", res, _buf);
476     return res;
477   }
478   
479   if (qDebug)
480     NSLog(@"_parseQualifiers return nil for <%s> ", _buf);
481
482   return nil;
483 }
484
485 static EOQualifier *_parseParenthesisQualifier(id _ctx, const char *_buf,
486                                                unsigned _bufLen,
487                                                unsigned *_qualLen)
488 {
489   unsigned    pos     = 0;
490   unsigned    qualLen = 0;
491   EOQualifier *qual   = nil;
492
493   pos = _countWhiteSpaces(_buf, _bufLen);
494
495   if (_bufLen <= pos + 2) /* at least open and close parenthesis */ {
496     if (qDebug)
497       NSLog(@"1_parseParenthesisQualifier return nil for <%s> ", _buf);
498  
499     return nil;
500   }
501   if (_buf[pos] != '(') {
502     if (qDebug)
503       NSLog(@"2_parseParenthesisQualifier return nil for <%s> ", _buf);
504     
505     return nil;
506   }
507   pos++;
508   if (!(qual = _parseQualifiers(_ctx, _buf + pos, _bufLen - pos,
509                                 &qualLen))) {
510     if (qDebug)
511       NSLog(@"3_parseParenthesisQualifier return nil for <%s> ", _buf);
512     
513     return nil;
514   }
515   
516   pos += qualLen;
517   if (_bufLen <= pos) {
518     if (qDebug)
519       NSLog(@"4_parseParenthesisQualifier return nil for <%s> qual[%@] %@ bufLen %d "
520             @"pos %d", _buf, [qual class], qual, _bufLen, pos);
521
522     return nil;
523   }
524   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
525   if (_buf[pos] != ')') {
526     if (qDebug)
527       NSLog(@"5_parseParenthesisQualifier return nil for <%s> [%s] ", _buf, _buf+pos);
528
529     return nil;
530   }
531   if (qDebug)
532     NSLog(@"6_parseParenthesisQualifier return <%@> for <%s> ", qual, _buf);
533   
534   *_qualLen = pos + 1; /* one step after the parenthesis */
535   return qual;
536 }
537
538 static EOQualifier *_parseNotQualifier(id _ctx, const char *_buf,
539                                        unsigned _bufLen, unsigned *_qualLen)
540 {
541   unsigned    pos, len   = 0;
542   char        c0, c1, c2 = 0;
543   EOQualifier *qual      = nil;
544
545   pos = _countWhiteSpaces(_buf, _bufLen);
546
547   if (_bufLen - pos < 4) { /* at least 3 chars for 'NOT' */
548     if (qDebug)
549       NSLog(@"_parseNotQualifier return nil for <%s> ", _buf);
550     
551     return nil;
552   }
553   c0 = _buf[pos];
554   c1 = _buf[pos + 1];
555   c2 = _buf[pos + 2];
556   if (!(((c0 == 'n') || (c0 == 'N')) &&
557         ((c1 == 'o') || (c1 == 'O')) &&
558         ((c2 == 't') || (c2 == 'T')))) {
559     if (qDebug)
560       NSLog(@"_parseNotQualifier return nil for <%s> ", _buf);
561     return nil;
562   }
563   pos += 3;
564   qual = _parseSingleQualifier(_ctx, _buf + pos, _bufLen - pos, &len);
565   if (qual == nil) {
566     if (qDebug)
567       NSLog(@"_parseNotQualifier return nil for <%s> ", _buf);
568     
569     return nil;
570   }
571   *_qualLen = pos +len;
572   if (qDebug)
573     NSLog(@"_parseNotQualifier return %@ for <%s> ", qual, _buf);
574
575   return [_ctx notQualifierWithQualifier:qual];
576 }
577
578 static EOQualifier *_parseKeyCompQualifier(id _ctx, const char *_buf,
579                                            unsigned _bufLen, unsigned *_qualLen)
580 {
581   NSString     *key       = nil;
582   NSString     *op        = nil;
583   NSString     *value     = nil;
584   EOQualifier  *qual      = nil;
585   NSDictionary *dict      = nil;
586   SEL          sel        = NULL;
587   unsigned     length     = 0;
588   unsigned     pos        = 0;
589   BOOL         valueIsKey = NO;
590
591   dict = [_ctx resultForFunction:@"parseKeyCompQualifier" 
592                atPos:(unsigned long)_buf];
593   if (dict != nil) {
594     if (qDebug)
595       NSLog(@"_parseKeyCompQual return <%@> [cached] for <%s> ", dict, _buf);
596     
597     *_qualLen = [[dict objectForKey:@"length"] unsignedIntValue];
598     return [dict objectForKey:@"object"];
599   }
600   pos = _countWhiteSpaces(_buf, _bufLen);
601
602   if ((key = _parseKey(_ctx , _buf + pos, _bufLen - pos, &length)) == nil) {
603     if (qDebug)
604       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
605     
606     return nil;
607   }
608   pos += length;
609   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
610
611   if (!(op = _parseOp(_buf + pos, _bufLen - pos, &length))) {
612     if (qDebug)
613       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
614     return nil;
615   }
616   sel = [EOQualifier operatorSelectorForString:op];
617   if (sel == NULL) {
618     NSLog(@"WARNING(%s): possible unknown operator <%@>", __PRETTY_FUNCTION__,
619           op);
620     if (qDebug)
621       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
622     return nil;
623   }
624   pos       +=length;
625   pos       += _countWhiteSpaces(_buf + pos, _bufLen - pos);
626   valueIsKey = NO;  
627   
628   value = _parseValue(_ctx, _buf + pos, _bufLen - pos, &length);
629   if (value == nil) {
630     value = _parseKey(_ctx, _buf + pos, _bufLen - pos, &length);
631     if (value == nil) {
632       if (qDebug)
633         NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
634       return nil;
635     }
636     else
637       valueIsKey = YES;
638   }
639   pos      +=length;  
640   *_qualLen = pos;
641
642   qual = (valueIsKey)
643     ? [_ctx keyComparisonQualifierWithLeftKey:key
644             operatorSelector:sel
645             rightKey:value]
646     : [_ctx keyValueQualifierWithKey:key
647             operatorSelector:sel
648             value:value];
649   
650   if (qDebug)
651     NSLog(@"_parseKeyCompQualifier return <%@> for <%s> ", qual, _buf);
652
653   if (qual != nil) {
654     id keys[2], values[2];
655     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
656     keys[1] = @"object"; values[1] = qual;
657     [_ctx setResult:
658             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
659           forFunction:@"parseKeyCompQualifier"
660           atPos:(unsigned long)_buf];
661     *_qualLen = pos;
662   }
663   return qual;
664 }
665
666 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
667                           unsigned *_opLen)
668 {
669   unsigned pos = 0;
670   char     c0  = 0;
671   char     c1  = 0;  
672
673   if (_bufLen == 0) {
674     if (qDebug)
675       NSLog(@"_parseOp _bufLen == 0 --> return nil");
676     return nil;
677   }
678   pos = _countWhiteSpaces(_buf, _bufLen);
679   if (_bufLen - pos > 1) {/* at least an operation and a value */
680     c0 = _buf[pos];
681     c1 = _buf[pos+1];  
682
683     if (((c0 >= '<') && (c0 <= '>')) || (c0 == '!')) {
684       NSString *result;
685       
686       if ((c1 >= '<') && (c1 <= '>')) {
687         *_opLen = 2;
688         result = [StringClass stringWithCString:_buf + pos length:2];
689         if (qDebug)
690           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
691       }
692       else {
693         *_opLen = 1;
694         result = [StringClass stringWithCString:&c0 length:1];
695         if (qDebug)
696           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
697       }
698       return result;
699     }
700     else { /* string designator operator */
701       unsigned opStart = pos;
702       while (pos < _bufLen) {
703         if (_buf[pos] == ' ')
704           break;
705         pos++;
706       }
707       if (pos >= _bufLen) {
708         NSLog(@"WARNING(%s): found end of string during operator parsing",
709               __PRETTY_FUNCTION__);
710       }
711
712       if (qDebug) {
713         NSLog(@"%s: _parseOp return <%@> for <%s> ", __PRETTY_FUNCTION__,
714               [StringClass stringWithCString:_buf + opStart
715                            length:pos - opStart], _buf);
716       }
717       
718       *_opLen = pos;
719       return [StringClass stringWithCString:_buf + opStart length:pos - opStart];
720     }
721   }
722   if (qDebug)
723     NSLog(@"_parseOp return nil for <%s> ", _buf);
724   return nil;
725 }
726
727 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
728                            unsigned *_keyLen)
729
730   id           result   = nil;
731   NSDictionary *dict    = nil;
732   unsigned     pos      = 0;
733   unsigned     startKey = 0;
734   char         c        = 0;
735
736   if (_bufLen == 0) {
737     if (qDebug) NSLog(@"%s: _bufLen == 0 --> return nil", __PRETTY_FUNCTION__);
738     return nil;
739   }
740   dict = [_ctx resultForFunction:@"parseKey" atPos:(unsigned long)_buf];
741   if (dict != nil) {
742     if (qDebug) {
743       NSLog(@"%s: return <%@> [cached] for <%s> ", __PRETTY_FUNCTION__,
744             dict, _buf);
745     }
746     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
747     return [dict objectForKey:@"object"];
748   }
749   pos      = _countWhiteSpaces(_buf, _bufLen);
750   startKey = pos;
751   c        = _buf[pos];
752
753   if (c == '%') {
754     if (_bufLen - pos < 2) {
755       if (qDebug) {
756         NSLog(@"%s: [c==%%,bufLen-pos<2]: _parseValue return nil for <%s> ", 
757               __PRETTY_FUNCTION__, _buf);
758       }
759       return nil;
760     }
761     pos++;
762     result = [_ctx getObjectFromStackFor:_buf[pos]];
763     pos++;
764   }
765   else {
766     /* '{' for namspaces */
767     register BOOL isQuotedKey = NO;
768
769     if (c == '"')
770       isQuotedKey = YES;
771     else if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
772              c == '{')) {
773       if (qDebug) {
774         NSLog(@"%s: [c!=AZaz{]: _parseKey return nil for <%s> ", 
775               __PRETTY_FUNCTION__, _buf);
776       }
777       return nil;
778     }
779     
780     pos++;
781     while (pos < _bufLen) {
782       c = _buf[pos];
783       if (isQuotedKey && c == '"')
784         break;
785       else if
786         ((c == ' ') || (c == '<') || (c == '>') || (c == '=') || (c == '!') ||
787          c == ')' || c == '(')
788         break;
789       pos++;    
790     }
791     if (isQuotedKey) {
792       pos++; // skip quote
793       result = [StringClass stringWithCString:(_buf + startKey + 1) 
794                             length:(pos - startKey - 2)];
795     }
796     else {
797       result = [StringClass stringWithCString:(_buf + startKey) 
798                             length:(pos - startKey)];
799     }
800   }
801   *_keyLen = pos;  
802   if (qDebug)
803     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, result, _buf);
804   
805   if (result != nil) {
806     id keys[2], values[2];
807     
808     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
809     keys[1] = @"object"; values[1] = result;
810     
811     [_ctx setResult:
812             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
813           forFunction:@"parseKey"
814           atPos:(unsigned long)_buf];
815     *_keyLen = pos;
816   }
817   return result;
818 }
819
820 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
821                       unsigned *_keyLen)
822 {
823   NSString     *cast = nil;
824   NSDictionary *dict = nil;
825   id           obj   = nil;
826   unsigned     pos   = 0;
827   char         c     = 0;
828   
829   if (NumberClass == Nil) NumberClass = [NSNumber class];
830   if (null == nil) null = [[NSNull null] retain];
831   
832   if (_bufLen == 0) {
833     if (qDebug) NSLog(@"_parseValue _bufLen == 0 --> return nil");
834     return nil;
835   }
836   
837   dict = [_ctx resultForFunction:@"parseValue" atPos:(unsigned long)_buf];
838   if (dict != nil) {
839     if (qDebug) {
840       NSLog(@"_parseKeyCompQualifier return <%@> [cached] for <%s> ",
841             dict, _buf);
842     }
843     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
844     return [dict objectForKey:@"object"];
845   }
846   
847   pos = _countWhiteSpaces(_buf, _bufLen);
848   c   = _buf[pos];
849   
850   if (c == '$') { /* found EOQualifierVariable */
851     unsigned startVar = 0;
852     NSString *varKey;
853     
854     pos++;
855     startVar = pos;
856     while (pos < _bufLen) {
857       if ((_buf[pos] == ' ') || (_buf[pos] == ')'))
858         break;
859       pos++;
860     }
861
862     varKey = [StringClass stringWithCString:(_buf + startVar)
863                           length:pos - startVar];
864     obj = [_ctx variableWithKey:varKey];
865   }
866   else {
867     /* first, check for CAST */
868     BOOL parseComplexCast = NO;
869     
870     if (c == 'c' && _bufLen > 14) {
871       if (strstr(_buf, "cast") == _buf && (isspace(_buf[4]) || _buf[4]=='(')) {
872         /* for example: cast("1970-01-01T00:00:00Z" as 'dateTime') [min 15 #]*/
873         pos += 4; /* skip 'cast' */
874         while (isspace(_buf[pos])) /* skip spaces */
875           pos++;
876         if (_buf[pos] != '(') {
877           NSLog(@"WARNING(%s): got unexpected cast string: '%s'",
878                 __PRETTY_FUNCTION__, _buf);
879         }
880         else
881           pos++; /* skip opening bracket '(' */
882         
883         parseComplexCast = YES;
884         c = _buf[pos];
885       }
886     }
887     else if (c == '(') { /* starting with a cast */
888       /* for example: (NSCalendarDate)"1999-12-12" [min 5 chars] */
889       unsigned startCast = 0;
890       
891       pos++;
892       startCast = pos;
893       while (pos < _bufLen) {
894         if (_buf[pos] == ')')
895           break;
896         pos++;
897       }
898       pos++;
899       if (pos >= _bufLen) {
900         NSLog(@"WARNING(%s): found end of string while reading a cast",
901               __PRETTY_FUNCTION__);
902         return nil;
903       }
904       c    = _buf[pos];
905       cast = [StringClass stringWithCString:(_buf + startCast)
906                           length:(pos - 1 - startCast)];
907       if (qDebug)
908         NSLog(@"%s: got cast %@", __PRETTY_FUNCTION__, cast);
909     }
910     
911     /* next, check for FORMAT SPECIFIER */
912     if (c == '%') {
913       if (_bufLen - pos < 2) {
914         if (qDebug)
915           NSLog(@"_parseValue return nil for <%s> ", _buf);
916         
917         return nil;
918       }
919       pos++;
920       obj = [_ctx getObjectFromStackFor:_buf[pos]];
921       pos++;
922     }
923     
924     /* next, check for A NUMBER */
925     else if (((c >= '0') && (c <= '9')) || (c == '-')) { /* got a number */
926       unsigned startNumber;
927
928       startNumber = pos;
929       pos++;
930       while (pos < _bufLen) {
931         c = _buf[pos];
932         if (!((c >= '0') && (c <= '9')))
933           break;
934         pos++;
935       }
936       obj = [NumberClass numberWithInt:atoi(_buf + startNumber)];
937     }
938
939     /* check for some text literals */
940     if ((obj == nil) && ((_bufLen - pos) > 1)) {
941       unsigned char i;
942       
943       for (i = 0; i < 20 && (toks[i].token != NULL) && (obj == nil); i++) {
944         const unsigned char *tok;
945         unsigned char toklen;
946         int rc;
947         
948         tok = toks[i].token;
949         toklen = strlen((const char *)tok);
950         if ((_bufLen - pos) < toklen)
951           /* remaining string not long enough */
952           continue;
953         
954         rc = toks[i].scase 
955           ? strncmp(&(_buf[pos]),     (const char *)tok, toklen)
956           : strncasecmp(&(_buf[pos]), (const char *)tok, toklen);
957         if (rc != 0)
958           /* does not match */
959           continue;
960         
961         if (!(_buf[pos + toklen] == '\0' || isspace(_buf[pos + toklen]) ||
962               _buf[pos + toklen] == ')')) {
963           /*
964             Not at the string end or followed by a space or a right 
965             parenthesis. The latter is required to correctly parse this:
966               (not (attribute = nil) and
967                attribute.className = 'com.webobjects.foundation.NSTimestamp')
968           */
969           continue;
970         }
971
972         /* wow, found the token */
973         pos += toklen; /* skip it */
974         obj = toks[i].value;
975       }
976     }
977     
978     /* next, check for STRING */
979     if (obj == nil) {
980       if ((c == '\'') || (c == '"')) {
981         NSString *res                  = nil;
982         char     string[_bufLen - pos];
983         unsigned cnt                   = 0;
984       
985         pos++;
986         while (pos < _bufLen) {
987           char ch = _buf[pos];
988           if (ch == c)
989             break;
990           if ((ch == '\\') && (_bufLen > (pos + 1))) {
991             if (_buf[pos + 1] == c) {
992               pos += 1;
993               ch = c;
994             }
995           }
996           string[cnt++] = ch;
997           pos++;
998         }
999         if (pos >= _bufLen) {
1000           NSLog(@"WARNING(%s): found end of string before end of quoted text",
1001                 __PRETTY_FUNCTION__);
1002           return nil;
1003         }
1004         res = [StringClass stringWithCString:string length:cnt];
1005         pos++; /* don`t forget quotations */
1006         if (qDebug) NSLog(@"_parseValue return <%@> for <%s> ", res, _buf);
1007         obj = res;
1008       }
1009     }
1010     
1011     /* complete parsing of cast */
1012     if (parseComplexCast && (pos + 6) < _bufLen) {
1013       /* now we need " as 'dateTime'" [min 7 #] */
1014       
1015       /* skip spaces */
1016       while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1017       
1018       //printf("POS: '%s'\n", &(_buf[pos]));
1019       /* parse 'as' */
1020       if (_buf[pos] != 'a' && _buf[pos] != 'A')
1021         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1022       else if (_buf[pos + 1] != 's' && _buf[pos + 1] != 'S')
1023         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1024       else {
1025         /* skip AS */
1026         pos += 2;
1027         
1028         /* skip spaces */
1029         while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1030         
1031         /* read cast type */
1032         if (_buf[pos] != '\'') {
1033           NSLog(@"%s: expected type of complex cast ...", __PRETTY_FUNCTION__);
1034         }
1035         else {
1036           const unsigned char *cs, *ce;
1037           
1038           //printf("POS: '%s'\n", &(_buf[pos]));
1039           pos++;
1040           cs = (const unsigned char *)&(_buf[pos]);
1041           ce = (const unsigned char *)index((const char *)cs, '\'');
1042           cast = [NSString stringWithCString:(const char*)cs length:(ce - cs)];
1043           if (qDebug) {
1044             NSLog(@"%s: parsed complex cast: '%@' to '%@'", 
1045                   __PRETTY_FUNCTION__, obj, cast);
1046           }
1047           pos += (ce - cs);
1048           pos++; // skip '
1049           pos++; // skip )
1050           //printf("POS: '%s'\n", &(_buf[pos]));
1051         }
1052       }
1053     }
1054   }
1055   
1056   if (cast != nil && obj != nil) {
1057     Class class = Nil;
1058     id orig = obj;
1059     
1060     if ((class = [EOQualifierParserTypeMappings objectForKey:cast]) == nil) {
1061       /* no value explicitly mapped to class, try to construct class name... */
1062       NSString *className;
1063
1064       className = cast;
1065       if ((class = NSClassFromString(className)) == Nil) {
1066         /* check some default cast types ... */
1067         className = [cast lowercaseString];
1068         
1069         if ([className isEqualToString:@"datetime"])
1070           class = [NSCalendarDate class];
1071         else if ([className isEqualToString:@"datetime.tz"])
1072           class = [NSCalendarDate class];
1073       }
1074     }
1075     if (class) {
1076       obj = [[[class alloc] initWithString:[orig description]] autorelease];
1077       
1078       if (obj == nil) {
1079         NSLog(@"%s: could not init object '%@' of cast class %@(%@) !",
1080               __PRETTY_FUNCTION__, orig, class, cast);
1081         obj = null;
1082       }
1083     }
1084     else {
1085       NSLog(@"WARNING(%s): could not map cast '%@' to a class "
1086             @"(returning null) !", 
1087             __PRETTY_FUNCTION__, cast);
1088       obj = null;
1089     }
1090   }
1091   
1092   if (qDebug) {
1093     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, 
1094           obj != nil ? obj : (id)@"<nil>", _buf);
1095   }
1096   
1097   if (obj != nil) {
1098     NSDictionary *d;
1099     id keys[2], values[2];
1100     
1101     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
1102     keys[1] = @"object"; values[1] = obj;
1103     
1104     d = [[NSDictionary alloc] initWithObjects:values forKeys:keys count:2];
1105     [_ctx setResult:d forFunction:@"parseValue" atPos:(unsigned long)_buf];
1106     [d release];
1107     *_keyLen = pos;
1108   }
1109   return obj;
1110 }
1111
1112 static EOQualifier *_testOperator(id _ctx, const char *_buf,
1113                                   unsigned _bufLen, unsigned *_opLen,
1114                                   BOOL *isAnd)
1115 {
1116   EOQualifier *qual       = nil;
1117   char        c0, c1, c2  = 0;
1118   unsigned    pos, len    = 0;
1119
1120   pos = _countWhiteSpaces(_buf, _bufLen);  
1121   
1122   if (_bufLen < 4) {/* at least OR or AND and somethink more */   
1123     if (qDebug)
1124       NSLog(@"_testOperator return nil for <%s> ", _buf);
1125     return nil;
1126   }
1127   c0 = _buf[pos + 0];
1128   c1 = _buf[pos + 1];
1129   c2 = _buf[pos + 2];
1130   
1131   if (((c0 == 'a') || (c0  == 'A')) &&
1132         ((c1 == 'n') || (c1  == 'N')) &&
1133         ((c2 == 'd') || (c2  == 'D'))) {
1134       pos    += 3;
1135       *isAnd  = YES;
1136   }
1137   else if (((c0 == 'o') || (c0  == 'O')) && ((c1 == 'r') || (c1  == 'R'))) {
1138       pos    += 2;
1139       *isAnd  = NO;
1140   }
1141   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
1142   qual = _parseSingleQualifier(_ctx, _buf + pos, _bufLen - pos, &len);
1143   *_opLen = pos + len;
1144   if (qDebug)
1145     NSLog(@"_testOperator return %@ for <%s> ", qual, _buf);
1146   
1147   return qual;
1148 }
1149
1150 static EOQualifier *_parseCompoundQualifier(id _ctx, const char *_buf,
1151                                             unsigned _bufLen, unsigned *_qualLen)
1152 {
1153   EOQualifier    *q0, *q1 = nil;
1154   NSMutableArray *array   = nil;
1155   unsigned       pos, len = 0;
1156   EOQualifier    *result;
1157   BOOL           isAnd;
1158
1159   isAnd = YES;
1160
1161   if ((q0 = _parseSingleQualifier(_ctx, _buf, _bufLen, &len)) == nil) {
1162     if (qDebug)
1163       NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf);
1164     
1165     return nil;
1166   }
1167   pos = len;
1168
1169   if (!(q1 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &isAnd))) {
1170     if (qDebug)
1171       NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf);
1172     return nil;
1173   }
1174   pos  += len;
1175   array = [NSMutableArray arrayWithObjects:q0, q1, nil];
1176   
1177   while (YES) {
1178     BOOL newIsAnd;
1179
1180     newIsAnd = YES;
1181     q0       = _testOperator(_ctx,  _buf + pos, _bufLen - pos, &len, &newIsAnd);
1182
1183     if (!q0)
1184       break;
1185     
1186     if (newIsAnd != isAnd) {
1187       NSArray *a;
1188
1189       a = [[array copy] autorelease];
1190       
1191       q1 = (isAnd)
1192         ? [_ctx andQualifierWithArray:a]
1193         : [_ctx orQualifierWithArray:a];
1194
1195       [array removeAllObjects];
1196       [array addObject:q1];
1197       isAnd = newIsAnd;
1198     }
1199     [array addObject:q0];
1200
1201     pos += len;
1202   }
1203
1204   *_qualLen = pos;
1205   result = (isAnd)
1206     ? [_ctx andQualifierWithArray:array]
1207     : [_ctx orQualifierWithArray:array];
1208   
1209   if (qDebug)
1210     NSLog(@"_parseAndOrQualifier return <%@> for <%s> ", result, _buf);
1211
1212   return result;
1213 }
1214
1215 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen) {
1216   unsigned cnt = 0;
1217   
1218   if (_bufLen == 0) {
1219     if (qDebug)
1220       NSLog(@"_parseString _bufLen == 0 --> return nil");
1221     return 0;
1222   }
1223   
1224   while (_buf[cnt] == ' ' || _buf[cnt] == '\t' || 
1225          _buf[cnt] == '\n' || _buf[cnt] == '\r') {
1226     cnt++;
1227     if (cnt == _bufLen)
1228       break;
1229   }
1230   return cnt;
1231 }