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