]> err.no Git - sope/blob - sope-core/EOControl/EOQualifierParser.m
renamed packages as discussed in the developer list
[sope] / sope-core / EOControl / EOQualifierParser.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo 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   OGo 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 OGo; 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 // $Id$
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   { "NULL",  nil, 0 },
47   { "nil",   nil, 1 },
48   { "YES",   nil, 0 },
49   { "NO",    nil, 0 },
50   { "TRUE",  nil, 0 },
51   { "FALSE", nil, 0 },
52   { 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" atPos:(unsigned)_buf];
592   if (dict != nil) {
593     if (qDebug)
594       NSLog(@"_parseKeyCompQual return <%@> [cached] for <%s> ", dict, _buf);
595     
596     *_qualLen = [[dict objectForKey:@"length"] unsignedIntValue];
597     return [dict objectForKey:@"object"];
598   }
599   pos = _countWhiteSpaces(_buf, _bufLen);
600
601   if ((key = _parseKey(_ctx , _buf + pos, _bufLen - pos, &length)) == nil) {
602     if (qDebug)
603       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
604     
605     return nil;
606   }
607   pos += length;
608   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
609
610   if (!(op = _parseOp(_buf + pos, _bufLen - pos, &length))) {
611     if (qDebug)
612       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
613     return nil;
614   }
615   sel = [EOQualifier operatorSelectorForString:op];
616   if (sel == NULL) {
617     NSLog(@"WARNING(%s): possible unknown operator <%@>", __PRETTY_FUNCTION__,
618           op);
619     if (qDebug)
620       NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
621     return nil;
622   }
623   pos       +=length;
624   pos       += _countWhiteSpaces(_buf + pos, _bufLen - pos);
625   valueIsKey = NO;  
626   
627   value = _parseValue(_ctx, _buf + pos, _bufLen - pos, &length);
628   if (value == nil) {
629     value = _parseKey(_ctx, _buf + pos, _bufLen - pos, &length);
630     if (value == nil) {
631       if (qDebug)
632         NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf);
633       return nil;
634     }
635     else
636       valueIsKey = YES;
637   }
638   pos      +=length;  
639   *_qualLen = pos;
640
641   qual = (valueIsKey)
642     ? [_ctx keyComparisonQualifierWithLeftKey:key
643             operatorSelector:sel
644             rightKey:value]
645     : [_ctx keyValueQualifierWithKey:key
646             operatorSelector:sel
647             value:value];
648   
649   if (qDebug)
650     NSLog(@"_parseKeyCompQualifier return <%@> for <%s> ", qual, _buf);
651
652   if (qual != nil) {
653     id keys[2], values[2];
654     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
655     keys[1] = @"object"; values[1] = qual;
656     [_ctx setResult:
657             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
658           forFunction:@"parseKeyCompQualifier"
659           atPos:(unsigned)_buf];
660     *_qualLen = pos;
661   }
662   return qual;
663 }
664
665 static NSString *_parseOp(const char *_buf, unsigned _bufLen,
666                           unsigned *_opLen)
667 {
668   unsigned pos = 0;
669   char     c0  = 0;
670   char     c1  = 0;  
671
672   if (_bufLen == 0) {
673     if (qDebug)
674       NSLog(@"_parseOp _bufLen == 0 --> return nil");
675     return nil;
676   }
677   pos = _countWhiteSpaces(_buf, _bufLen);
678   if (_bufLen - pos > 1) {/* at least an operation and a value */
679     c0 = _buf[pos];
680     c1 = _buf[pos+1];  
681
682     if (((c0 >= '<') && (c0 <= '>')) || (c0 == '!')) {
683       NSString *result;
684       
685       if ((c1 >= '<') && (c1 <= '>')) {
686         *_opLen = 2;
687         result = [StringClass stringWithCString:_buf + pos length:2];
688         if (qDebug)
689           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
690       }
691       else {
692         *_opLen = 1;
693         result = [StringClass stringWithCString:&c0 length:1];
694         if (qDebug)
695           NSLog(@"_parseOp return <%@> for <%s> ", result, _buf);
696       }
697       return result;
698     }
699     else { /* string designator operator */
700       unsigned opStart = pos;
701       while (pos < _bufLen) {
702         if (_buf[pos] == ' ')
703           break;
704         pos++;
705       }
706       if (pos >= _bufLen) {
707         NSLog(@"WARNING(%s): found end of string during operator parsing",
708               __PRETTY_FUNCTION__);
709       }
710
711       if (qDebug) {
712         NSLog(@"%s: _parseOp return <%@> for <%s> ", __PRETTY_FUNCTION__,
713               [StringClass stringWithCString:_buf + opStart
714                            length:pos - opStart], _buf);
715       }
716       
717       *_opLen = pos;
718       return [StringClass stringWithCString:_buf + opStart length:pos - opStart];
719     }
720   }
721   if (qDebug)
722     NSLog(@"_parseOp return nil for <%s> ", _buf);
723   return nil;
724 }
725
726 static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen,
727                            unsigned *_keyLen)
728
729   id           result   = nil;
730   NSDictionary *dict    = nil;
731   unsigned     pos      = 0;
732   unsigned     startKey = 0;
733   char         c        = 0;
734
735   if (_bufLen == 0) {
736     if (qDebug) NSLog(@"%s: _bufLen == 0 --> return nil", __PRETTY_FUNCTION__);
737     return nil;
738   }
739   dict = [_ctx resultForFunction:@"parseKey" atPos:(unsigned)_buf];
740   if (dict != nil) {
741     if (qDebug) {
742       NSLog(@"%s: return <%@> [cached] for <%s> ", __PRETTY_FUNCTION__,
743             dict, _buf);
744     }
745     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
746     return [dict objectForKey:@"object"];
747   }
748   pos      = _countWhiteSpaces(_buf, _bufLen);
749   startKey = pos;
750   c        = _buf[pos];
751
752   if (c == '%') {
753     if (_bufLen - pos < 2) {
754       if (qDebug) {
755         NSLog(@"%s: [c==%%,bufLen-pos<2]: _parseValue return nil for <%s> ", 
756               __PRETTY_FUNCTION__, _buf);
757       }
758       return nil;
759     }
760     pos++;
761     result = [_ctx getObjectFromStackFor:_buf[pos]];
762     pos++;
763   }
764   else {
765     /* '{' for namspaces */
766     register BOOL isQuotedKey = NO;
767
768     if (c == '"')
769       isQuotedKey = YES;
770     else if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
771              c == '{')) {
772       if (qDebug) {
773         NSLog(@"%s: [c!=AZaz{]: _parseKey return nil for <%s> ", 
774               __PRETTY_FUNCTION__, _buf);
775       }
776       return nil;
777     }
778     
779     pos++;
780     while (pos < _bufLen) {
781       c = _buf[pos];
782       if (isQuotedKey && c == '"')
783         break;
784       else if
785         ((c == ' ') || (c == '<') || (c == '>') || (c == '=') || (c == '!') ||
786          c == ')' || c == '(')
787         break;
788       pos++;    
789     }
790     if (isQuotedKey) {
791       pos++; // skip quote
792       result = [StringClass stringWithCString:(_buf + startKey + 1) 
793                             length:(pos - startKey - 2)];
794     }
795     else {
796       result = [StringClass stringWithCString:(_buf + startKey) 
797                             length:(pos - startKey)];
798     }
799   }
800   *_keyLen = pos;  
801   if (qDebug)
802     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, result, _buf);
803   
804   if (result != nil) {
805     id keys[2], values[2];
806     
807     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
808     keys[1] = @"object"; values[1] = result;
809     
810     [_ctx setResult:
811             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
812           forFunction:@"parseKey"
813           atPos:(unsigned)_buf];
814     *_keyLen = pos;
815   }
816   return result;
817 }
818
819 static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen,
820                       unsigned *_keyLen)
821 {
822   NSString     *cast = nil;
823   NSDictionary *dict = nil;
824   id           obj   = nil;
825   unsigned     pos   = 0;
826   char         c     = 0;
827   
828   if (NumberClass == Nil) NumberClass = [NSNumber class];
829   if (null == nil) null = [[NSNull null] retain];
830   
831   if (_bufLen == 0) {
832     if (qDebug) NSLog(@"_parseValue _bufLen == 0 --> return nil");
833     return nil;
834   }
835   
836   dict = [_ctx resultForFunction:@"parseValue" atPos:(unsigned)_buf];
837   if (dict != nil) {
838     if (qDebug) {
839       NSLog(@"_parseKeyCompQualifier return <%@> [cached] for <%s> ",
840             dict, _buf);
841     }
842     *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue];
843     return [dict objectForKey:@"object"];
844   }
845   
846   pos = _countWhiteSpaces(_buf, _bufLen);
847   c   = _buf[pos];
848   
849   if (c == '$') { /* found EOQualifierVariable */
850     unsigned startVar = 0;
851     NSString *varKey;
852     
853     pos++;
854     startVar = pos;
855     while (pos < _bufLen) {
856       if ((_buf[pos] == ' ') || (_buf[pos] == ')'))
857         break;
858       pos++;
859     }
860
861     varKey = [StringClass stringWithCString:(_buf + startVar)
862                           length:pos - startVar];
863     obj = [_ctx variableWithKey:varKey];
864   }
865   else {
866     /* first, check for CAST */
867     BOOL parseComplexCast = NO;
868     
869     if (c == 'c' && _bufLen > 14) {
870       if (strstr(_buf, "cast") == _buf && (isspace(_buf[4]) || _buf[4]=='(')) {
871         /* for example: cast("1970-01-01T00:00:00Z" as 'dateTime') [min 15 #]*/
872         pos += 4; /* skip 'cast' */
873         while (isspace(_buf[pos])) /* skip spaces */
874           pos++;
875         if (_buf[pos] != '(') {
876           NSLog(@"WARNING(%s): got unexpected cast string: '%s'",
877                 __PRETTY_FUNCTION__, _buf);
878         }
879         else
880           pos++; /* skip opening bracket '(' */
881         
882         parseComplexCast = YES;
883         c = _buf[pos];
884       }
885     }
886     else if (c == '(') { /* starting with a cast */
887       /* for example: (NSCalendarDate)"1999-12-12" [min 5 chars] */
888       unsigned startCast = 0;
889       
890       pos++;
891       startCast = pos;
892       while (pos < _bufLen) {
893         if (_buf[pos] == ')')
894           break;
895         pos++;
896       }
897       pos++;
898       if (pos >= _bufLen) {
899         NSLog(@"WARNING(%s): found end of string while reading a cast",
900               __PRETTY_FUNCTION__);
901         return nil;
902       }
903       c    = _buf[pos];
904       cast = [StringClass stringWithCString:(_buf + startCast)
905                           length:(pos - 1 - startCast)];
906       if (qDebug)
907         NSLog(@"%s: got cast %@", __PRETTY_FUNCTION__, cast);
908     }
909     
910     /* next, check for FORMAT SPECIFIER */
911     if (c == '%') {
912       if (_bufLen - pos < 2) {
913         if (qDebug)
914           NSLog(@"_parseValue return nil for <%s> ", _buf);
915         
916         return nil;
917       }
918       pos++;
919       obj = [_ctx getObjectFromStackFor:_buf[pos]];
920       pos++;
921     }
922     
923     /* next, check for A NUMBER */
924     else if (((c >= '0') && (c <= '9')) || (c == '-')) { /* got a number */
925       unsigned startNumber;
926
927       startNumber = pos;
928       pos++;
929       while (pos < _bufLen) {
930         c = _buf[pos];
931         if (!((c >= '0') && (c <= '9')))
932           break;
933         pos++;
934       }
935       obj = [NumberClass numberWithInt:atoi(_buf + startNumber)];
936     }
937
938     /* check for some text literals */
939     if ((obj == nil) && ((_bufLen - pos) > 1)) {
940       unsigned char i;
941       
942       for (i = 0; i < 20 && (toks[i].token != NULL) && (obj == nil); i++) {
943         const unsigned char *tok;
944         unsigned char toklen;
945         int rc;
946         
947         tok = toks[i].token;
948         toklen = strlen(tok);
949         if ((_bufLen - pos) < toklen)
950           /* remaining string not long enough */
951           continue;
952         
953         rc = toks[i].scase 
954           ? strncmp(&(_buf[pos]),     tok, toklen)
955           : strncasecmp(&(_buf[pos]), tok, toklen);
956         if (rc != 0)
957           /* does not match */
958           continue;
959         if (!(_buf[pos + toklen] == '\0' || isspace(_buf[pos + toklen])))
960           /* not at the string end or folloed by a space */
961           continue;
962
963         /* wow, found the token */
964         pos += toklen; /* skip it */
965         obj = toks[i].value;
966       }
967     }
968     
969     /* next, check for STRING */
970     if (obj == nil) {
971       if ((c == '\'') || (c == '"')) {
972         NSString *res                  = nil;
973         char     string[_bufLen - pos];
974         unsigned cnt                   = 0;
975       
976         pos++;
977         while (pos < _bufLen) {
978           char ch = _buf[pos];
979           if (ch == c)
980             break;
981           if ((ch == '\\') && (_bufLen > (pos + 1))) {
982             if (_buf[pos + 1] == c) {
983               pos += 1;
984               ch = c;
985             }
986           }
987           string[cnt++] = ch;
988           pos++;
989         }
990         if (pos >= _bufLen) {
991           NSLog(@"WARNING(%s): found end of string before end of quoted text",
992                 __PRETTY_FUNCTION__);
993           return nil;
994         }
995         res = [StringClass stringWithCString:string length:cnt];
996         pos++; /* don`t forget quotations */
997         if (qDebug) NSLog(@"_parseValue return <%@> for <%s> ", res, _buf);
998         obj = res;
999       }
1000     }
1001     
1002     /* complete parsing of cast */
1003     if (parseComplexCast && (pos + 6) < _bufLen) {
1004       /* now we need " as 'dateTime'" [min 7 #] */
1005       
1006       /* skip spaces */
1007       while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1008       
1009       //printf("POS: '%s'\n", &(_buf[pos]));
1010       /* parse 'as' */
1011       if (_buf[pos] != 'a' && _buf[pos] != 'A')
1012         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1013       else if (_buf[pos + 1] != 's' && _buf[pos + 1] != 'S')
1014         NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__);
1015       else {
1016         /* skip AS */
1017         pos += 2;
1018         
1019         /* skip spaces */
1020         while (isspace(_buf[pos]) && pos < _bufLen) pos++;
1021         
1022         /* read cast type */
1023         if (_buf[pos] != '\'') {
1024           NSLog(@"%s: expected type of complex cast ...", __PRETTY_FUNCTION__);
1025         }
1026         else {
1027           const unsigned char *cs, *ce;
1028           
1029           //printf("POS: '%s'\n", &(_buf[pos]));
1030           pos++;
1031           cs = &(_buf[pos]);
1032           ce = index(cs, '\'');
1033           cast = [NSString stringWithCString:cs length:(ce - cs)];
1034           if (qDebug) {
1035             NSLog(@"%s: parsed complex cast: '%@' to '%@'", __PRETTY_FUNCTION__,
1036                   obj, cast);
1037           }
1038           pos += (ce - cs);
1039           pos++; // skip '
1040           pos++; // skip )
1041           //printf("POS: '%s'\n", &(_buf[pos]));
1042         }
1043       }
1044     }
1045   }
1046   
1047   if (cast != nil && obj != nil) {
1048     Class class = Nil;
1049     id orig = obj;
1050     
1051     if ((class = [EOQualifierParserTypeMappings objectForKey:cast]) == nil) {
1052       /* no value explicitly mapped to class, try to construct class name... */
1053       NSString *className;
1054
1055       className = cast;
1056       if ((class = NSClassFromString(className)) == Nil) {
1057         /* check some default cast types ... */
1058         className = [cast lowercaseString];
1059         
1060         if ([className isEqualToString:@"datetime"])
1061           class = [NSCalendarDate class];
1062         else if ([className isEqualToString:@"datetime.tz"])
1063           class = [NSCalendarDate class];
1064       }
1065     }
1066     if (class) {
1067       obj = [[[class alloc] initWithString:[orig description]] autorelease];
1068       
1069       if (obj == nil) {
1070         NSLog(@"%s: could not init object '%@' of cast class %@(%@) !",
1071               __PRETTY_FUNCTION__, orig, class, cast);
1072         obj = null;
1073       }
1074     }
1075     else {
1076       NSLog(@"WARNING(%s): could not map cast '%@' to a class "
1077             @"(returning null) !", 
1078             __PRETTY_FUNCTION__, cast);
1079       obj = null;
1080     }
1081   }
1082   
1083   if (qDebug) {
1084     NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, 
1085           obj?obj:@"<nil>", _buf);
1086   }
1087   
1088   if (obj != nil) {
1089     id keys[2], values[2];
1090     
1091     keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos];
1092     keys[1] = @"object"; values[1] = obj;
1093     
1094     [_ctx setResult:
1095             [NSDictionary dictionaryWithObjects:values forKeys:keys count:2]
1096           forFunction:@"parseValue" atPos:(unsigned)_buf];
1097     *_keyLen = pos;
1098   }
1099   return obj;
1100 }
1101
1102 static EOQualifier *_testOperator(id _ctx, const char *_buf,
1103                                   unsigned _bufLen, unsigned *_opLen,
1104                                   BOOL *isAnd)
1105 {
1106   EOQualifier *qual       = nil;
1107   char        c0, c1, c2  = 0;
1108   unsigned    pos, len    = 0;
1109
1110   pos = _countWhiteSpaces(_buf, _bufLen);  
1111   
1112   if (_bufLen < 4) {/* at least OR or AND and somethink more */   
1113     if (qDebug)
1114       NSLog(@"_testOperator return nil for <%s> ", _buf);
1115     return nil;
1116   }
1117   c0 = _buf[pos + 0];
1118   c1 = _buf[pos + 1];
1119   c2 = _buf[pos + 2];
1120   
1121   if (((c0 == 'a') || (c0  == 'A')) &&
1122         ((c1 == 'n') || (c1  == 'N')) &&
1123         ((c2 == 'd') || (c2  == 'D'))) {
1124       pos    += 3;
1125       *isAnd  = YES;
1126   }
1127   else if (((c0 == 'o') || (c0  == 'O')) && ((c1 == 'r') || (c1  == 'R'))) {
1128       pos    += 2;
1129       *isAnd  = NO;
1130   }
1131   pos += _countWhiteSpaces(_buf + pos, _bufLen - pos);
1132   qual = _parseSingleQualifier(_ctx, _buf + pos, _bufLen - pos, &len);
1133   *_opLen = pos + len;
1134   if (qDebug)
1135     NSLog(@"_testOperator return %@ for <%s> ", qual, _buf);
1136   
1137   return qual;
1138 }
1139
1140 static EOQualifier *_parseCompoundQualifier(id _ctx, const char *_buf,
1141                                             unsigned _bufLen, unsigned *_qualLen)
1142 {
1143   EOQualifier    *q0, *q1 = nil;
1144   NSMutableArray *array   = nil;
1145   unsigned       pos, len = 0;
1146   EOQualifier    *result;
1147   BOOL           isAnd;
1148
1149   isAnd = YES;
1150
1151   if ((q0 = _parseSingleQualifier(_ctx, _buf, _bufLen, &len)) == nil) {
1152     if (qDebug)
1153       NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf);
1154     
1155     return nil;
1156   }
1157   pos = len;
1158
1159   if (!(q1 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &isAnd))) {
1160     if (qDebug)
1161       NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf);
1162     return nil;
1163   }
1164   pos  += len;
1165   array = [NSMutableArray arrayWithObjects:q0, q1, nil];
1166   
1167   while (YES) {
1168     BOOL newIsAnd;
1169
1170     newIsAnd = YES;
1171     q0       = _testOperator(_ctx,  _buf + pos, _bufLen - pos, &len, &newIsAnd);
1172
1173     if (!q0)
1174       break;
1175     
1176     if (newIsAnd != isAnd) {
1177       NSArray *a;
1178
1179       a = [[array copy] autorelease];
1180       
1181       q1 = (isAnd)
1182         ? [_ctx andQualifierWithArray:a]
1183         : [_ctx orQualifierWithArray:a];
1184
1185       [array removeAllObjects];
1186       [array addObject:q1];
1187       isAnd = newIsAnd;
1188     }
1189     [array addObject:q0];
1190
1191     pos += len;
1192   }
1193
1194   *_qualLen = pos;
1195   result = (isAnd)
1196     ? [_ctx andQualifierWithArray:array]
1197     : [_ctx orQualifierWithArray:array];
1198   
1199   if (qDebug)
1200     NSLog(@"_parseAndOrQualifier return <%@> for <%s> ", result, _buf);
1201
1202   return result;
1203 }
1204
1205 static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen) {
1206   unsigned cnt = 0;
1207   
1208   if (_bufLen == 0) {
1209     if (qDebug)
1210       NSLog(@"_parseString _bufLen == 0 --> return nil");
1211     return 0;
1212   }
1213   
1214   while (_buf[cnt] == ' ' || _buf[cnt] == '\t' || 
1215          _buf[cnt] == '\n' || _buf[cnt] == '\r') {
1216     cnt++;
1217     if (cnt == _bufLen)
1218       break;
1219   }
1220   return cnt;
1221 }