/* Copyright (C) 2000-2005 SKYRIX Software AG This file is part of SOPE. SOPE is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. SOPE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "EOQualifier.h" #include "EONull.h" #include "common.h" //#define USE_DESCRIPTION_FOR_AT 1 static int qDebug = 0; static NSMutableDictionary *EOQualifierParserTypeMappings = nil; /* The literals understood by the value parser. NOTE: Any literal used here can never be used as a key ! So add as little as possible. */ typedef struct { const unsigned char *token; id value; int scase; } EOQPTokEntry; static EOQPTokEntry toks[] = { { (const unsigned char *)"NULL", nil, 0 }, { (const unsigned char *)"nil", nil, 1 }, { (const unsigned char *)"YES", nil, 0 }, { (const unsigned char *)"NO", nil, 0 }, { (const unsigned char *)"TRUE", nil, 0 }, { (const unsigned char *)"FALSE", nil, 0 }, { (const unsigned char *)NULL, nil, 0 } }; static inline void _setupLiterals(void) { static BOOL didSetup = NO; if (didSetup) return; didSetup = YES; toks[0].value = [[NSNull null] retain]; toks[1].value = toks[0].value; toks[2].value = [[NSNumber numberWithBool:YES] retain]; toks[3].value = [[NSNumber numberWithBool:NO] retain]; toks[4].value = toks[2].value; toks[5].value = toks[3].value; } /* cache */ static Class StringClass = Nil; static Class NumberClass = Nil; static EONull *null = nil; /* parsing functions */ static EOQualifier *_parseCompoundQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen); static EOQualifier *_testOperator(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_opLen, BOOL *_testAnd); static EOQualifier *_parseQualifiers(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen); static EOQualifier *_parseParenthesisQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen); static EOQualifier *_parseNotQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen); static EOQualifier *_parseKeyCompQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen); static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_keyLen); static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_keyLen); static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen); static NSString *_parseOp(const char *_buf, unsigned _bufLen, unsigned *_opLen); @interface EOQualifierParserContext : NSObject { NSMapTable *qualifierCache; } - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos; - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct atPos:(unsigned)_pos; - (id)getObjectFromStackFor:(char)_c; /* factory */ - (EOQualifier *)keyComparisonQualifierWithLeftKey:(NSString *)_leftKey operatorSelector:(SEL)_sel rightKey:(NSString *)_rightKey; - (EOQualifier *)keyValueQualifierWithKey:(NSString *)_key operatorSelector:(SEL)_sel value:(id)_value; - (EOQualifier *)andQualifierWithArray:(NSArray *)_qualifiers; - (EOQualifier *)orQualifierWithArray:(NSArray *)_qualifiers; - (EOQualifier *)notQualifierWithQualifier:(EOQualifier *)_qualifier; @end @interface EOQualifierVAParserContext : EOQualifierParserContext { va_list *va; } + (id)contextWithVaList:(va_list *)_va; - (id)initWithVaList:(va_list *)_va; @end @interface EOQualifierEnumeratorParserContext : EOQualifierParserContext { NSEnumerator *enumerator; } + (id)contextWithEnumerator:(NSEnumerator *)_enumerator; - (id)initWithEnumerator:(NSEnumerator *)_enumerator; @end @implementation EOQualifierVAParserContext + (id)contextWithVaList:(va_list *)_va { return [[[EOQualifierVAParserContext alloc] initWithVaList:_va] autorelease]; } - (id)initWithVaList:(va_list *)_va { if ((self = [super init])) { self->va = _va; } return self; } - (id)getObjectFromStackFor:(char)_c { id obj = nil; if (StringClass == Nil) StringClass = [NSString class]; if (NumberClass == Nil) NumberClass = [NSNumber class]; if (null == nil) null = [EONull null]; if (_c == 's') { char *str = va_arg(*self->va, char*); obj = [StringClass stringWithCString:str]; } else if (_c == 'd') { int i= va_arg(*self->va, int); obj = [NumberClass numberWithInt:i]; } else if (_c == 'f') { double d = va_arg(*self->va, double); obj = [NumberClass numberWithDouble:d]; } else if (_c == '@') { id o = va_arg(*self->va, id); #if USE_DESCRIPTION_FOR_AT obj = (o == nil) ? (id)null : (id)[o description]; #else obj = (o == nil) ? (id)null : (id)o; #endif } else { [NSException raise:@"NSInvalidArgumentException" format:@"unknown conversation char %c", _c]; } return obj; } @end /* EOQualifierVAParserContext */ @implementation EOQualifierEnumeratorParserContext + (id)contextWithEnumerator:(NSEnumerator *)_enumerator { return [[[EOQualifierEnumeratorParserContext alloc] initWithEnumerator:_enumerator] autorelease]; } - (id)initWithEnumerator:(NSEnumerator *)_enumerator { if ((self = [super init])) { ASSIGN(self->enumerator, _enumerator); } return self; } - (void)dealloc { [self->enumerator release]; [super dealloc];; } - (id)getObjectFromStackFor:(char)_c { static Class NumberClass = Nil; id o; if (NumberClass == Nil) NumberClass = [NSNumber class]; o = [self->enumerator nextObject]; switch (_c) { case '@': #if USE_DESCRIPTION_FOR_AT return [o description]; #else return o; #endif case 'f': return [NumberClass numberWithDouble:[o doubleValue]]; case 'd': return [NumberClass numberWithInt:[o intValue]]; case 's': // return [NSString stringWithCString:[o cString]]; return [[o copy] autorelease]; default: [NSException raise:@"NSInvalidArgumentException" format:@"unknown or not allowed conversation char %c", _c]; } return nil; } @end /* EOQualifierEnumeratorParserContext */ @implementation EOQualifierParserContext - (id)init { if (StringClass == Nil) StringClass = [NSString class]; if ((self = [super init])) { self->qualifierCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 200); } return self; } - (void)dealloc { if (self->qualifierCache) NSFreeMapTable(self->qualifierCache); [super dealloc]; } - (NSDictionary *)resultForFunction:(NSString *)_fct atPos:(unsigned)_pos { return NSMapGet(self->qualifierCache, [StringClass stringWithFormat:@"%@_%d", _fct, _pos]); } - (void)setResult:(NSDictionary *)_dict forFunction:(NSString *)_fct atPos:(unsigned)_pos { NSMapInsert(self->qualifierCache, [StringClass stringWithFormat:@"%@_%d", _fct, _pos], _dict); } - (id)getObjectFromStackFor:(char)_c { [self doesNotRecognizeSelector:_cmd]; return nil; } /* factory */ - (EOQualifier *)keyComparisonQualifierWithLeftKey:(NSString *)_leftKey operatorSelector:(SEL)_sel rightKey:(NSString *)_rightKey { static Class clazz = Nil; if (clazz == Nil) clazz = [EOKeyComparisonQualifier class]; return [[[clazz alloc] initWithLeftKey:_leftKey operatorSelector:_sel rightKey:_rightKey] autorelease]; } - (EOQualifier *)keyValueQualifierWithKey:(NSString *)_key operatorSelector:(SEL)_sel value:(id)_value { static Class clazz = Nil; if (clazz == Nil) clazz = [EOKeyValueQualifier class]; return [[[clazz alloc] initWithKey:_key operatorSelector:_sel value:_value] autorelease]; } - (EOQualifier *)andQualifierWithArray:(NSArray *)_qualifiers { static Class clazz = Nil; if (clazz == Nil) clazz = [EOAndQualifier class]; return [[[clazz alloc] initWithQualifierArray:_qualifiers] autorelease]; } - (EOQualifier *)orQualifierWithArray:(NSArray *)_qualifiers { static Class clazz = Nil; if (clazz == Nil) clazz = [EOOrQualifier class]; return [[[clazz alloc] initWithQualifierArray:_qualifiers] autorelease]; } - (EOQualifier *)notQualifierWithQualifier:(EOQualifier *)_qualifier { static Class clazz = Nil; if (clazz == Nil) clazz = [EONotQualifier class]; return [[[clazz alloc] initWithQualifier:_qualifier] autorelease]; } - (EOQualifierVariable *)variableWithKey:(NSString *)_key { static Class clazz = Nil; if (clazz == Nil) clazz = [EOQualifierVariable class]; return [clazz variableWithKey:_key]; } @end /* EOQualifierParserContext */ @implementation EOQualifier(Parsing) + (void)registerValueClass:(Class)_valueClass forTypeName:(NSString *)_type { if (EOQualifierParserTypeMappings == nil) EOQualifierParserTypeMappings = [[NSMutableDictionary alloc] init]; if (_type == nil) { NSLog(@"ERROR(%s): got passed no type name!", __PRETTY_FUNCTION__); return; } if (_valueClass == nil) { NSLog(@"ERROR(%s): got passed no value-class for type '%@'!", __PRETTY_FUNCTION__, _type); return; } [EOQualifierParserTypeMappings setObject:_valueClass forKey:_type]; } + (EOQualifier *)qualifierWithQualifierFormat:(NSString *)_qualifierFormat,... { va_list va; EOQualifier *qualifier; unsigned length = 0; const char *buf; unsigned bufLen; char *cbuf; _setupLiterals(); if (StringClass == Nil) StringClass = [NSString class]; bufLen = [_qualifierFormat cStringLength]; cbuf = malloc(bufLen + 1); [_qualifierFormat getCString:cbuf]; cbuf[bufLen] = '\0'; buf = cbuf; va_start(va, _qualifierFormat); qualifier = _parseQualifiers([EOQualifierVAParserContext contextWithVaList:&va], buf, bufLen, &length); va_end(va); if (qualifier != nil) { /* check whether the rest of the string is OK */ if (length < bufLen) length += _countWhiteSpaces(buf + length, bufLen - length); if (length < bufLen) { NSLog(@"WARNING(%s): unexpected chars at the end of the " @"string(class=%@,len=%i) '%@'", __PRETTY_FUNCTION__, [_qualifierFormat class], [_qualifierFormat length], _qualifierFormat); NSLog(@" buf-length: %i", bufLen); NSLog(@" length: %i", length); NSLog(@" char[length]: '%c' (%i) '%s'", buf[length], buf[length], (buf+length)); qualifier = nil; } else if (length > bufLen) { NSLog(@"WARNING(%s): length should never be longer than bufLen ?, " @"internal parsing error !", __PRETTY_FUNCTION__); } } free(cbuf); return qualifier; } + (EOQualifier *)qualifierWithQualifierFormat:(NSString *)_qualifierFormat arguments:(NSArray *)_arguments { EOQualifier *qual = nil; unsigned length = 0; const char *buf = NULL; unsigned bufLen = 0; EOQualifierEnumeratorParserContext *ctx; _setupLiterals(); if (StringClass == Nil) StringClass = [NSString class]; ctx = [EOQualifierEnumeratorParserContext contextWithEnumerator: [_arguments objectEnumerator]]; //NSLog(@"qclass: %@", [_qualifierFormat class]); buf = [_qualifierFormat cString]; bufLen = [_qualifierFormat cStringLength]; qual = _parseQualifiers(ctx, buf, bufLen, &length); if (qual != nil) { /* check whether the rest of the string is OK */ if (length < bufLen) { length += _countWhiteSpaces(buf + length, bufLen - length); } if (length != bufLen) { NSLog(@"WARNING(%s): unexpected chars at the end of the string '%@'", __PRETTY_FUNCTION__, _qualifierFormat); qual = nil; } } return qual; } @end /* EOQualifier(Parsing) */ static EOQualifier *_parseSingleQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { EOQualifier *res = nil; if ((res = _parseParenthesisQualifier(_ctx, _buf, _bufLen, _qualLen)) != nil) { if (qDebug) NSLog(@"_parseSingleQualifier return <%@> for <%s> ", res, _buf); return res; } if ((res = _parseNotQualifier(_ctx, _buf, _bufLen, _qualLen)) != nil) { if (qDebug) NSLog(@"_parseSingleQualifier return <%@> for <%s> ", res, _buf); return res; } if ((res = _parseKeyCompQualifier(_ctx, _buf, _bufLen, _qualLen)) != nil) { if (qDebug) { NSLog(@"_parseSingleQualifier return <%@> for <%s> length %d", res, _buf, *_qualLen); } return res; } return nil; } static EOQualifier *_parseQualifiers(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { EOQualifier *res = nil; if ((res = _parseCompoundQualifier(_ctx, _buf, _bufLen, _qualLen))) { if (qDebug) NSLog(@"_parseQualifiers return <%@> for <%s> ", res, _buf); return res; } if ((res = _parseSingleQualifier(_ctx, _buf, _bufLen, _qualLen))) { if (qDebug) NSLog(@"_parseQualifiers return <%@> for <%s> ", res, _buf); return res; } if (qDebug) NSLog(@"_parseQualifiers return nil for <%s> ", _buf); return nil; } static EOQualifier *_parseParenthesisQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { unsigned pos = 0; unsigned qualLen = 0; EOQualifier *qual = nil; pos = _countWhiteSpaces(_buf, _bufLen); if (_bufLen <= pos + 2) /* at least open and close parenthesis */ { if (qDebug) NSLog(@"1_parseParenthesisQualifier return nil for <%s> ", _buf); return nil; } if (_buf[pos] != '(') { if (qDebug) NSLog(@"2_parseParenthesisQualifier return nil for <%s> ", _buf); return nil; } pos++; if (!(qual = _parseQualifiers(_ctx, _buf + pos, _bufLen - pos, &qualLen))) { if (qDebug) NSLog(@"3_parseParenthesisQualifier return nil for <%s> ", _buf); return nil; } pos += qualLen; if (_bufLen <= pos) { if (qDebug) NSLog(@"4_parseParenthesisQualifier return nil for <%s> qual[%@] %@ bufLen %d " @"pos %d", _buf, [qual class], qual, _bufLen, pos); return nil; } pos += _countWhiteSpaces(_buf + pos, _bufLen - pos); if (_buf[pos] != ')') { if (qDebug) NSLog(@"5_parseParenthesisQualifier return nil for <%s> [%s] ", _buf, _buf+pos); return nil; } if (qDebug) NSLog(@"6_parseParenthesisQualifier return <%@> for <%s> ", qual, _buf); *_qualLen = pos + 1; /* one step after the parenthesis */ return qual; } static EOQualifier *_parseNotQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { unsigned pos, len = 0; char c0, c1, c2 = 0; EOQualifier *qual = nil; pos = _countWhiteSpaces(_buf, _bufLen); if (_bufLen - pos < 4) { /* at least 3 chars for 'NOT' */ if (qDebug) NSLog(@"_parseNotQualifier return nil for <%s> ", _buf); return nil; } c0 = _buf[pos]; c1 = _buf[pos + 1]; c2 = _buf[pos + 2]; if (!(((c0 == 'n') || (c0 == 'N')) && ((c1 == 'o') || (c1 == 'O')) && ((c2 == 't') || (c2 == 'T')))) { if (qDebug) NSLog(@"_parseNotQualifier return nil for <%s> ", _buf); return nil; } pos += 3; qual = _parseSingleQualifier(_ctx, _buf + pos, _bufLen - pos, &len); if (qual == nil) { if (qDebug) NSLog(@"_parseNotQualifier return nil for <%s> ", _buf); return nil; } *_qualLen = pos +len; if (qDebug) NSLog(@"_parseNotQualifier return %@ for <%s> ", qual, _buf); return [_ctx notQualifierWithQualifier:qual]; } static EOQualifier *_parseKeyCompQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { NSString *key = nil; NSString *op = nil; NSString *value = nil; EOQualifier *qual = nil; NSDictionary *dict = nil; SEL sel = NULL; unsigned length = 0; unsigned pos = 0; BOOL valueIsKey = NO; dict = [_ctx resultForFunction:@"parseKeyCompQualifier" atPos:(unsigned)_buf]; if (dict != nil) { if (qDebug) NSLog(@"_parseKeyCompQual return <%@> [cached] for <%s> ", dict, _buf); *_qualLen = [[dict objectForKey:@"length"] unsignedIntValue]; return [dict objectForKey:@"object"]; } pos = _countWhiteSpaces(_buf, _bufLen); if ((key = _parseKey(_ctx , _buf + pos, _bufLen - pos, &length)) == nil) { if (qDebug) NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf); return nil; } pos += length; pos += _countWhiteSpaces(_buf + pos, _bufLen - pos); if (!(op = _parseOp(_buf + pos, _bufLen - pos, &length))) { if (qDebug) NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf); return nil; } sel = [EOQualifier operatorSelectorForString:op]; if (sel == NULL) { NSLog(@"WARNING(%s): possible unknown operator <%@>", __PRETTY_FUNCTION__, op); if (qDebug) NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf); return nil; } pos +=length; pos += _countWhiteSpaces(_buf + pos, _bufLen - pos); valueIsKey = NO; value = _parseValue(_ctx, _buf + pos, _bufLen - pos, &length); if (value == nil) { value = _parseKey(_ctx, _buf + pos, _bufLen - pos, &length); if (value == nil) { if (qDebug) NSLog(@"_parseKeyCompQualifier return nil for <%s> ", _buf); return nil; } else valueIsKey = YES; } pos +=length; *_qualLen = pos; qual = (valueIsKey) ? [_ctx keyComparisonQualifierWithLeftKey:key operatorSelector:sel rightKey:value] : [_ctx keyValueQualifierWithKey:key operatorSelector:sel value:value]; if (qDebug) NSLog(@"_parseKeyCompQualifier return <%@> for <%s> ", qual, _buf); if (qual != nil) { id keys[2], values[2]; keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos]; keys[1] = @"object"; values[1] = qual; [_ctx setResult: [NSDictionary dictionaryWithObjects:values forKeys:keys count:2] forFunction:@"parseKeyCompQualifier" atPos:(unsigned)_buf]; *_qualLen = pos; } return qual; } static NSString *_parseOp(const char *_buf, unsigned _bufLen, unsigned *_opLen) { unsigned pos = 0; char c0 = 0; char c1 = 0; if (_bufLen == 0) { if (qDebug) NSLog(@"_parseOp _bufLen == 0 --> return nil"); return nil; } pos = _countWhiteSpaces(_buf, _bufLen); if (_bufLen - pos > 1) {/* at least an operation and a value */ c0 = _buf[pos]; c1 = _buf[pos+1]; if (((c0 >= '<') && (c0 <= '>')) || (c0 == '!')) { NSString *result; if ((c1 >= '<') && (c1 <= '>')) { *_opLen = 2; result = [StringClass stringWithCString:_buf + pos length:2]; if (qDebug) NSLog(@"_parseOp return <%@> for <%s> ", result, _buf); } else { *_opLen = 1; result = [StringClass stringWithCString:&c0 length:1]; if (qDebug) NSLog(@"_parseOp return <%@> for <%s> ", result, _buf); } return result; } else { /* string designator operator */ unsigned opStart = pos; while (pos < _bufLen) { if (_buf[pos] == ' ') break; pos++; } if (pos >= _bufLen) { NSLog(@"WARNING(%s): found end of string during operator parsing", __PRETTY_FUNCTION__); } if (qDebug) { NSLog(@"%s: _parseOp return <%@> for <%s> ", __PRETTY_FUNCTION__, [StringClass stringWithCString:_buf + opStart length:pos - opStart], _buf); } *_opLen = pos; return [StringClass stringWithCString:_buf + opStart length:pos - opStart]; } } if (qDebug) NSLog(@"_parseOp return nil for <%s> ", _buf); return nil; } static NSString *_parseKey(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_keyLen) { id result = nil; NSDictionary *dict = nil; unsigned pos = 0; unsigned startKey = 0; char c = 0; if (_bufLen == 0) { if (qDebug) NSLog(@"%s: _bufLen == 0 --> return nil", __PRETTY_FUNCTION__); return nil; } dict = [_ctx resultForFunction:@"parseKey" atPos:(unsigned)_buf]; if (dict != nil) { if (qDebug) { NSLog(@"%s: return <%@> [cached] for <%s> ", __PRETTY_FUNCTION__, dict, _buf); } *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue]; return [dict objectForKey:@"object"]; } pos = _countWhiteSpaces(_buf, _bufLen); startKey = pos; c = _buf[pos]; if (c == '%') { if (_bufLen - pos < 2) { if (qDebug) { NSLog(@"%s: [c==%%,bufLen-pos<2]: _parseValue return nil for <%s> ", __PRETTY_FUNCTION__, _buf); } return nil; } pos++; result = [_ctx getObjectFromStackFor:_buf[pos]]; pos++; } else { /* '{' for namspaces */ register BOOL isQuotedKey = NO; if (c == '"') isQuotedKey = YES; else if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || c == '{')) { if (qDebug) { NSLog(@"%s: [c!=AZaz{]: _parseKey return nil for <%s> ", __PRETTY_FUNCTION__, _buf); } return nil; } pos++; while (pos < _bufLen) { c = _buf[pos]; if (isQuotedKey && c == '"') break; else if ((c == ' ') || (c == '<') || (c == '>') || (c == '=') || (c == '!') || c == ')' || c == '(') break; pos++; } if (isQuotedKey) { pos++; // skip quote result = [StringClass stringWithCString:(_buf + startKey + 1) length:(pos - startKey - 2)]; } else { result = [StringClass stringWithCString:(_buf + startKey) length:(pos - startKey)]; } } *_keyLen = pos; if (qDebug) NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, result, _buf); if (result != nil) { id keys[2], values[2]; keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos]; keys[1] = @"object"; values[1] = result; [_ctx setResult: [NSDictionary dictionaryWithObjects:values forKeys:keys count:2] forFunction:@"parseKey" atPos:(unsigned)_buf]; *_keyLen = pos; } return result; } static id _parseValue(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_keyLen) { NSString *cast = nil; NSDictionary *dict = nil; id obj = nil; unsigned pos = 0; char c = 0; if (NumberClass == Nil) NumberClass = [NSNumber class]; if (null == nil) null = [[NSNull null] retain]; if (_bufLen == 0) { if (qDebug) NSLog(@"_parseValue _bufLen == 0 --> return nil"); return nil; } dict = [_ctx resultForFunction:@"parseValue" atPos:(unsigned)_buf]; if (dict != nil) { if (qDebug) { NSLog(@"_parseKeyCompQualifier return <%@> [cached] for <%s> ", dict, _buf); } *_keyLen = [[dict objectForKey:@"length"] unsignedIntValue]; return [dict objectForKey:@"object"]; } pos = _countWhiteSpaces(_buf, _bufLen); c = _buf[pos]; if (c == '$') { /* found EOQualifierVariable */ unsigned startVar = 0; NSString *varKey; pos++; startVar = pos; while (pos < _bufLen) { if ((_buf[pos] == ' ') || (_buf[pos] == ')')) break; pos++; } varKey = [StringClass stringWithCString:(_buf + startVar) length:pos - startVar]; obj = [_ctx variableWithKey:varKey]; } else { /* first, check for CAST */ BOOL parseComplexCast = NO; if (c == 'c' && _bufLen > 14) { if (strstr(_buf, "cast") == _buf && (isspace(_buf[4]) || _buf[4]=='(')) { /* for example: cast("1970-01-01T00:00:00Z" as 'dateTime') [min 15 #]*/ pos += 4; /* skip 'cast' */ while (isspace(_buf[pos])) /* skip spaces */ pos++; if (_buf[pos] != '(') { NSLog(@"WARNING(%s): got unexpected cast string: '%s'", __PRETTY_FUNCTION__, _buf); } else pos++; /* skip opening bracket '(' */ parseComplexCast = YES; c = _buf[pos]; } } else if (c == '(') { /* starting with a cast */ /* for example: (NSCalendarDate)"1999-12-12" [min 5 chars] */ unsigned startCast = 0; pos++; startCast = pos; while (pos < _bufLen) { if (_buf[pos] == ')') break; pos++; } pos++; if (pos >= _bufLen) { NSLog(@"WARNING(%s): found end of string while reading a cast", __PRETTY_FUNCTION__); return nil; } c = _buf[pos]; cast = [StringClass stringWithCString:(_buf + startCast) length:(pos - 1 - startCast)]; if (qDebug) NSLog(@"%s: got cast %@", __PRETTY_FUNCTION__, cast); } /* next, check for FORMAT SPECIFIER */ if (c == '%') { if (_bufLen - pos < 2) { if (qDebug) NSLog(@"_parseValue return nil for <%s> ", _buf); return nil; } pos++; obj = [_ctx getObjectFromStackFor:_buf[pos]]; pos++; } /* next, check for A NUMBER */ else if (((c >= '0') && (c <= '9')) || (c == '-')) { /* got a number */ unsigned startNumber; startNumber = pos; pos++; while (pos < _bufLen) { c = _buf[pos]; if (!((c >= '0') && (c <= '9'))) break; pos++; } obj = [NumberClass numberWithInt:atoi(_buf + startNumber)]; } /* check for some text literals */ if ((obj == nil) && ((_bufLen - pos) > 1)) { unsigned char i; for (i = 0; i < 20 && (toks[i].token != NULL) && (obj == nil); i++) { const unsigned char *tok; unsigned char toklen; int rc; tok = toks[i].token; toklen = strlen((const char *)tok); if ((_bufLen - pos) < toklen) /* remaining string not long enough */ continue; rc = toks[i].scase ? strncmp(&(_buf[pos]), (const char *)tok, toklen) : strncasecmp(&(_buf[pos]), (const char *)tok, toklen); if (rc != 0) /* does not match */ continue; if (!(_buf[pos + toklen] == '\0' || isspace(_buf[pos + toklen]))) /* not at the string end or folloed by a space */ continue; /* wow, found the token */ pos += toklen; /* skip it */ obj = toks[i].value; } } /* next, check for STRING */ if (obj == nil) { if ((c == '\'') || (c == '"')) { NSString *res = nil; char string[_bufLen - pos]; unsigned cnt = 0; pos++; while (pos < _bufLen) { char ch = _buf[pos]; if (ch == c) break; if ((ch == '\\') && (_bufLen > (pos + 1))) { if (_buf[pos + 1] == c) { pos += 1; ch = c; } } string[cnt++] = ch; pos++; } if (pos >= _bufLen) { NSLog(@"WARNING(%s): found end of string before end of quoted text", __PRETTY_FUNCTION__); return nil; } res = [StringClass stringWithCString:string length:cnt]; pos++; /* don`t forget quotations */ if (qDebug) NSLog(@"_parseValue return <%@> for <%s> ", res, _buf); obj = res; } } /* complete parsing of cast */ if (parseComplexCast && (pos + 6) < _bufLen) { /* now we need " as 'dateTime'" [min 7 #] */ /* skip spaces */ while (isspace(_buf[pos]) && pos < _bufLen) pos++; //printf("POS: '%s'\n", &(_buf[pos])); /* parse 'as' */ if (_buf[pos] != 'a' && _buf[pos] != 'A') NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__); else if (_buf[pos + 1] != 's' && _buf[pos + 1] != 'S') NSLog(@"%s: expecting 'AS' of complex cast ...", __PRETTY_FUNCTION__); else { /* skip AS */ pos += 2; /* skip spaces */ while (isspace(_buf[pos]) && pos < _bufLen) pos++; /* read cast type */ if (_buf[pos] != '\'') { NSLog(@"%s: expected type of complex cast ...", __PRETTY_FUNCTION__); } else { const unsigned char *cs, *ce; //printf("POS: '%s'\n", &(_buf[pos])); pos++; cs = (const unsigned char *)&(_buf[pos]); ce = (const unsigned char *)index((const char *)cs, '\''); cast = [NSString stringWithCString:(const char*)cs length:(ce - cs)]; if (qDebug) { NSLog(@"%s: parsed complex cast: '%@' to '%@'", __PRETTY_FUNCTION__, obj, cast); } pos += (ce - cs); pos++; // skip ' pos++; // skip ) //printf("POS: '%s'\n", &(_buf[pos])); } } } } if (cast != nil && obj != nil) { Class class = Nil; id orig = obj; if ((class = [EOQualifierParserTypeMappings objectForKey:cast]) == nil) { /* no value explicitly mapped to class, try to construct class name... */ NSString *className; className = cast; if ((class = NSClassFromString(className)) == Nil) { /* check some default cast types ... */ className = [cast lowercaseString]; if ([className isEqualToString:@"datetime"]) class = [NSCalendarDate class]; else if ([className isEqualToString:@"datetime.tz"]) class = [NSCalendarDate class]; } } if (class) { obj = [[[class alloc] initWithString:[orig description]] autorelease]; if (obj == nil) { NSLog(@"%s: could not init object '%@' of cast class %@(%@) !", __PRETTY_FUNCTION__, orig, class, cast); obj = null; } } else { NSLog(@"WARNING(%s): could not map cast '%@' to a class " @"(returning null) !", __PRETTY_FUNCTION__, cast); obj = null; } } if (qDebug) { NSLog(@"%s: return <%@> for <%s> ", __PRETTY_FUNCTION__, obj?obj:@"", _buf); } if (obj != nil) { id keys[2], values[2]; keys[0] = @"length"; values[0] = [NSNumber numberWithUnsignedInt:pos]; keys[1] = @"object"; values[1] = obj; [_ctx setResult: [NSDictionary dictionaryWithObjects:values forKeys:keys count:2] forFunction:@"parseValue" atPos:(unsigned)_buf]; *_keyLen = pos; } return obj; } static EOQualifier *_testOperator(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_opLen, BOOL *isAnd) { EOQualifier *qual = nil; char c0, c1, c2 = 0; unsigned pos, len = 0; pos = _countWhiteSpaces(_buf, _bufLen); if (_bufLen < 4) {/* at least OR or AND and somethink more */ if (qDebug) NSLog(@"_testOperator return nil for <%s> ", _buf); return nil; } c0 = _buf[pos + 0]; c1 = _buf[pos + 1]; c2 = _buf[pos + 2]; if (((c0 == 'a') || (c0 == 'A')) && ((c1 == 'n') || (c1 == 'N')) && ((c2 == 'd') || (c2 == 'D'))) { pos += 3; *isAnd = YES; } else if (((c0 == 'o') || (c0 == 'O')) && ((c1 == 'r') || (c1 == 'R'))) { pos += 2; *isAnd = NO; } pos += _countWhiteSpaces(_buf + pos, _bufLen - pos); qual = _parseSingleQualifier(_ctx, _buf + pos, _bufLen - pos, &len); *_opLen = pos + len; if (qDebug) NSLog(@"_testOperator return %@ for <%s> ", qual, _buf); return qual; } static EOQualifier *_parseCompoundQualifier(id _ctx, const char *_buf, unsigned _bufLen, unsigned *_qualLen) { EOQualifier *q0, *q1 = nil; NSMutableArray *array = nil; unsigned pos, len = 0; EOQualifier *result; BOOL isAnd; isAnd = YES; if ((q0 = _parseSingleQualifier(_ctx, _buf, _bufLen, &len)) == nil) { if (qDebug) NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf); return nil; } pos = len; if (!(q1 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &isAnd))) { if (qDebug) NSLog(@"_parseAndOrQualifier return nil for <%s> ", _buf); return nil; } pos += len; array = [NSMutableArray arrayWithObjects:q0, q1, nil]; while (YES) { BOOL newIsAnd; newIsAnd = YES; q0 = _testOperator(_ctx, _buf + pos, _bufLen - pos, &len, &newIsAnd); if (!q0) break; if (newIsAnd != isAnd) { NSArray *a; a = [[array copy] autorelease]; q1 = (isAnd) ? [_ctx andQualifierWithArray:a] : [_ctx orQualifierWithArray:a]; [array removeAllObjects]; [array addObject:q1]; isAnd = newIsAnd; } [array addObject:q0]; pos += len; } *_qualLen = pos; result = (isAnd) ? [_ctx andQualifierWithArray:array] : [_ctx orQualifierWithArray:array]; if (qDebug) NSLog(@"_parseAndOrQualifier return <%@> for <%s> ", result, _buf); return result; } static inline unsigned _countWhiteSpaces(const char *_buf, unsigned _bufLen) { unsigned cnt = 0; if (_bufLen == 0) { if (qDebug) NSLog(@"_parseString _bufLen == 0 --> return nil"); return 0; } while (_buf[cnt] == ' ' || _buf[cnt] == '\t' || _buf[cnt] == '\n' || _buf[cnt] == '\r') { cnt++; if (cnt == _bufLen) break; } return cnt; }