2 Copyright (C) 2003-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #include "NGRuleParser.h"
24 #include "NGRuleModel.h"
25 #include "NGRuleAssignment.h"
26 #include "NSObject+Logs.h"
27 #include "NSString+misc.h"
28 #include "NSString+Ext.h"
29 #include "EOTrueQualifier.h"
30 #include <EOControl/EOQualifier.h>
33 // TODO: proper reports errors in last-exception !
34 // TODO: improve performance
35 // TODO: parse assignment class? (eg "a = b; (BoolAssignment)")
37 #define RULE_PRIORITY_NORMAL 100
39 @implementation NGRuleParser
41 static BOOL parseDebugOn = YES;
44 parseDebugOn = [[NSUserDefaults standardUserDefaults]
45 boolForKey:@"NGRuleParserDebugEnabled"];
48 + (id)sharedRuleParser {
49 static NGRuleParser *parser = nil; // THREAD
51 parser = [[NGRuleParser alloc] init];
56 if ((self = [super init])) {
57 self->ruleQuotes = @"'\"";
58 self->ruleEscape = '\\';
59 self->priorityMapping =
60 [[NSDictionary alloc] initWithObjectsAndKeys:
61 [NSNumber numberWithInt:1000], @"important",
62 [NSNumber numberWithInt:200], @"very high",
63 [NSNumber numberWithInt:150], @"high",
64 [NSNumber numberWithInt:RULE_PRIORITY_NORMAL],
66 [NSNumber numberWithInt:RULE_PRIORITY_NORMAL],
68 [NSNumber numberWithInt:50], @"low",
69 [NSNumber numberWithInt:5], @"very low",
70 [NSNumber numberWithInt:0], @"fallback",
73 [[NSDictionary alloc] initWithObjectsAndKeys:
74 [NSNumber numberWithBool:YES], @"yes",
75 [NSNumber numberWithBool:NO], @"no",
76 [NSNumber numberWithBool:YES], @"true",
77 [NSNumber numberWithBool:NO], @"false",
84 [self->priorityMapping release];
85 [self->boolMapping release];
86 [self->ruleQuotes release];
87 [self->lastException release];
93 - (NSException *)lastException {
94 return self->lastException;
99 - (NGRule *)parseRuleFromPropertyList:(id)_plist {
103 if ([_plist isKindOfClass:[NSString class]])
104 return [self parseRuleFromString:_plist];
106 [self debugWithFormat:
107 @"cannot deal with plist rule of class '%@': %@",
108 NSStringFromClass([_plist class]), _plist];
112 - (NGRule *)parseRuleFromArray:(NSArray *)_a {
113 /* eg: ( "a>2", "a='3'", 5 ) */
115 id qpart, apart, ppart;
122 if ((count = [_a count]) < 2) {
123 [self debugWithFormat:@"invalid rule array: %@", _a];
129 qpart = [_a objectAtIndex:0];
130 apart = [_a objectAtIndex:1];
131 ppart = count > 2 ? [_a objectAtIndex:2] : nil;
133 /* parse separate strings */
135 // TODO: handle plists !
136 q = [self parseQualifierFromString:[qpart stringValue]];
137 action = [self parseActionFromString:[apart stringValue]];
138 priority = [self parsePriorityFromString:[ppart stringValue]];
141 return [NGRule ruleWithQualifier:q action:action priority:priority];
144 - (NGRule *)parseRuleFromString:(NSString *)_s {
145 /* "qualifier => assignment [; prio]" */
146 NSString *qs, *as, *ps;
155 [self debugWithFormat:@"should parse rule: '%@'", _s];
159 ok = [self splitString:_s
160 intoQualifierString:&qs
162 andPriorityString:&ps];
165 [self debugWithFormat:@" splitted: q='%@', as='%@', pri=%@", qs, as, ps];
167 /* parse separate strings */
169 q = [self parseQualifierFromString:qs];
170 action = [self parseActionFromString:as];
171 priority = [self parsePriorityFromString:ps];
174 return [NGRule ruleWithQualifier:q action:action priority:priority];
177 - (NGRuleModel *)parseRuleModelFromPropertyList:(id)_plist {
181 if ([_plist isKindOfClass:[NSString class]]) {
184 if ((rule = [self parseRuleFromString:_plist]) == nil)
187 return [[[NGRuleModel alloc]
188 initWithRules:[NSArray arrayWithObject:rule]]
191 else if ([_plist isKindOfClass:[NSArray class]]) {
192 NSMutableArray *rules;
195 if ((count = [(NSArray *)_plist count]) == 0)
196 return [[[NGRuleModel alloc] init] autorelease];
198 rules = [NSMutableArray arrayWithCapacity:(count + 1)];
199 for (i = 0; i < count; i++) {
202 rule = [self parseRuleFromPropertyList:[_plist objectAtIndex:i]];
204 [self debugWithFormat:@"could not parse rule %i in model !", (i + 1)];
207 [rules addObject:rule];
210 return [[[NGRuleModel alloc] initWithRules:rules] autorelease];
213 [self debugWithFormat:
214 @"cannot deal with plist rule-model of class '%@': %@",
215 NSStringFromClass([_plist class]), _plist];
220 /* parsing of parts */
222 - (BOOL)splitString:(NSString *)_s
223 intoQualifierString:(NSString **)_qs
224 actionString:(NSString **)_as
225 andPriorityString:(NSString **)_ps
229 NSString *qs, *as, *ps;
235 if ((len = [_s length]) == 0)
238 /* split into qualifier and assignment/prio */
240 r = [_s rangeOfString:@"=>"
241 skipQuotes:self->ruleQuotes
242 escapedByChar:self->ruleEscape];
244 [self debugWithFormat:@"ERROR: missing => in rule '%@'", _s];
248 qs = [[_s substringToIndex:r.location] stringByTrimmingSpaces];
249 as = [_s substringFromIndex:(r.location + r.length)];
251 /* split assignment and prio */
253 r = [as rangeOfString:@";"
254 skipQuotes:self->ruleQuotes
255 escapedByChar:self->ruleEscape];
259 as = [as stringByTrimmingSpaces];
262 ps = [[as substringFromIndex:(r.location + r.length)]
263 stringByTrimmingSpaces];
264 as = [[as substringToIndex:r.location] stringByTrimmingSpaces];
274 - (EOQualifier *)parseQualifierFromString:(NSString *)_s {
275 if ([_s length] == 0)
278 _s = [_s stringByTrimmingSpaces];
280 if ([_s isEqualToString:@"*true*"]) {
281 static EOTrueQualifier *tq = nil;
282 if (tq == nil) tq = [[EOTrueQualifier alloc] init];
286 return [EOQualifier qualifierWithQualifierFormat:_s arguments:nil];
289 - (id)parseActionFromString:(NSString *)_s {
293 Class AssignmentClass;
296 _s = [_s stringByTrimmingSpaces];
298 /* split assignment */
300 r = [_s rangeOfString:@"="
301 skipQuotes:self->ruleQuotes
302 escapedByChar:self->ruleEscape];
304 [self debugWithFormat:@"cannot parse rule action: '%@'", _s];
308 key = [[_s substringToIndex:r.location] stringByTrimmingSpaces];
309 valstr = [[_s substringFromIndex:(r.location + r.length)]
310 stringByTrimmingSpaces];
314 AssignmentClass = [NGRuleKeyAssignment class];
319 if ([valstr length] > 0) {
320 unichar c1 = [valstr characterAtIndex:0];
323 if (c1 == '"' || c1 == '\'') {
324 /* a quoted, constant string */
328 AssignmentClass = [NGRuleAssignment class];
330 qs = [NSString stringWithCharacters:&c1 length:1];
331 s = [valstr substringFromIndex:1]; // TODO: perf
332 r = [s rangeOfString:qs];
334 [self debugWithFormat:
335 @"quoting of assignment string-value is not closed !"];
339 value = [s substringToIndex:r.location];
341 else if (isdigit(c1) || c1 == '-') {
342 AssignmentClass = [NGRuleAssignment class];
343 value = [NSNumber numberWithInt:[valstr intValue]];
345 else if ((tmp=[self->boolMapping objectForKey:[valstr lowercaseString]])) {
346 AssignmentClass = [NGRuleAssignment class];
349 else if ([valstr isEqualToString:@"nil"] ||
350 [valstr isEqualToString:@"null"]) {
351 AssignmentClass = [NGRuleAssignment class];
352 value = [NSNull null];
356 return [AssignmentClass assignmentWithKeyPath:key value:value];
359 - (int)parsePriorityFromString:(NSString *)_s {
363 _s = [_s stringByTrimmingSpaces];
364 // [self debugWithFormat:@"parse priority: '%@'", _s];
366 if ([_s length] == 0)
367 return RULE_PRIORITY_NORMAL;
368 c1 = [_s characterAtIndex:0];
370 if (isdigit(c1) || c1 == '-')
371 return [_s intValue];
373 if ((num = [self->priorityMapping objectForKey:_s]))
374 return [num intValue];
376 [self debugWithFormat:@"cannot parse rule priority: '%@'", _s];
377 return RULE_PRIORITY_NORMAL;
382 - (BOOL)isDebuggingEnabled {
386 @end /* NGRuleParser */