]> err.no Git - sope/blob - sope-core/NGExtensions/NGRuleEngine.subproj/NGRuleParser.m
fixed logging in WOComponent
[sope] / sope-core / NGExtensions / NGRuleEngine.subproj / NGRuleParser.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
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 "NGRuleParser.h"
24 #include "NGRule.h"
25 #include "NGRuleModel.h"
26 #include "NGRuleAssignment.h"
27 #include "NSObject+Logs.h"
28 #include "NSString+misc.h"
29 #include "NSString+Ext.h"
30 #include "EOTrueQualifier.h"
31 #include <EOControl/EOQualifier.h>
32 #include "common.h"
33
34 // TODO: proper reports errors in last-exception !
35 // TODO: improve performance
36
37 #define RULE_PRIORITY_NORMAL 100
38
39 @implementation NGRuleParser
40
41 static BOOL parseDebugOn = YES;
42
43 + (void)initialize {
44   parseDebugOn = [[NSUserDefaults standardUserDefaults] 
45                                   boolForKey:@"NGRuleParserDebugEnabled"];
46 }
47
48 + (id)sharedRuleParser {
49   static NGRuleParser *parser = nil; // THREAD
50   if (parser == nil)
51     parser = [[NGRuleParser alloc] init];
52   return parser;
53 }
54
55 - (id)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], 
65                               @"normal",
66                               [NSNumber numberWithInt:RULE_PRIORITY_NORMAL],
67                               @"default",
68                               [NSNumber numberWithInt:50],   @"low",
69                               [NSNumber numberWithInt:5],    @"very low",
70                               [NSNumber numberWithInt:0],    @"fallback",
71                             nil];
72     self->boolMapping =
73       [[NSDictionary alloc] initWithObjectsAndKeys:
74                               [NSNumber numberWithBool:YES], @"yes",
75                               [NSNumber numberWithBool:NO],  @"no",
76                               [NSNumber numberWithBool:YES], @"true",
77                               [NSNumber numberWithBool:NO],  @"false",
78                             nil];
79   }
80   return self;
81 }
82
83 - (void)dealloc {
84   [self->priorityMapping release];
85   [self->boolMapping   release];
86   [self->ruleQuotes    release];
87   [self->lastException release];
88   [super dealloc];
89 }
90
91 /* accessors */
92
93 - (NSException *)lastException {
94   return self->lastException;
95 }
96
97 /* parsing */
98
99 - (NGRule *)parseRuleFromPropertyList:(id)_plist {
100   if (_plist == nil)
101     return nil;
102   
103   if ([_plist isKindOfClass:[NSString class]])
104     return [self parseRuleFromString:_plist];
105   
106   [self debugWithFormat:
107           @"cannot deal with plist rule of class '%@': %@",
108           NSStringFromClass([_plist class]), _plist];
109   return nil;
110 }
111
112 - (NGRule *)parseRuleFromArray:(NSArray *)_a {
113   /* eg: ( "a>2", "a='3'", 5 ) */
114   unsigned count;
115   id qpart, apart, ppart;
116   EOQualifier *q;
117   id   action;
118   int  priority;
119   
120   if (_a == nil) 
121     return nil;
122   if ((count = [_a count]) < 2) {
123     [self debugWithFormat:@"invalid rule array: %@", _a];
124     return nil;
125   }
126
127   /* extract parts */
128   
129   qpart = [_a objectAtIndex:0];
130   apart = [_a objectAtIndex:1];
131   ppart = count > 2 ? [_a objectAtIndex:2] : nil;
132   
133   /* parse separate strings */
134   
135   // TODO: handle plists !
136   q        = [self parseQualifierFromString:[qpart stringValue]];
137   action   = [self parseActionFromString:[apart stringValue]];
138   priority = [self parsePriorityFromString:[ppart stringValue]];
139   
140   /* create rule */
141   return [NGRule ruleWithQualifier:q action:action priority:priority];
142 }
143
144 - (NGRule *)parseRuleFromString:(NSString *)_s {
145   /* "qualifier => assignment [; prio]" */
146   NSString    *qs, *as, *ps;
147   EOQualifier *q;
148   id   action;
149   int  priority;
150   BOOL ok;
151
152   if (_s == nil)
153     return nil;
154   
155   [self debugWithFormat:@"should parse rule: '%@'", _s];
156
157   /* split string */
158   
159   ok = [self splitString:_s 
160              intoQualifierString:&qs
161              actionString:&as
162              andPriorityString:&ps];
163   if (!ok) return nil;
164   
165   [self debugWithFormat:@"  splitted: q='%@', as='%@', pri=%@", qs, as, ps];
166   
167   /* parse separate strings */
168   
169   q        = [self parseQualifierFromString:qs];
170   action   = [self parseActionFromString:as];
171   priority = [self parsePriorityFromString:ps];
172   
173   /* create rule */
174   return [NGRule ruleWithQualifier:q action:action priority:priority];
175 }
176
177 - (NGRuleModel *)parseRuleModelFromPropertyList:(id)_plist {
178   if (_plist == nil)
179     return nil;
180   
181   if ([_plist isKindOfClass:[NSString class]]) {
182     NGRule *rule;
183     
184     if ((rule = [self parseRuleFromString:_plist]) == nil)
185       return nil;
186     
187     return [[[NGRuleModel alloc]
188                           initWithRules:[NSArray arrayWithObject:rule]]
189                           autorelease];
190   }
191   else if ([_plist isKindOfClass:[NSArray class]]) {
192     NSMutableArray *rules;
193     unsigned i, count;
194     
195     if ((count = [(NSArray *)_plist count]) == 0)
196       return [[[NGRuleModel alloc] init] autorelease];
197     
198     rules = [NSMutableArray arrayWithCapacity:(count + 1)];
199     for (i = 0; i < count; i++) {
200       NGRule *rule;
201       
202       rule = [self parseRuleFromPropertyList:[_plist objectAtIndex:i]];
203       if (rule == nil) {
204         [self debugWithFormat:@"could not parse rule %i in model !", (i + 1)];
205         return nil;
206       }
207       [rules addObject:rule];
208     }
209     
210     return [[[NGRuleModel alloc] initWithRules:rules] autorelease];
211   }
212   else {
213     [self debugWithFormat:
214             @"cannot deal with plist rule-model of class '%@': %@",
215             NSStringFromClass([_plist class]), _plist];
216     return nil;
217   }
218 }
219
220 /* parsing of parts */
221
222 - (BOOL)splitString:(NSString *)_s 
223   intoQualifierString:(NSString **)_qs
224   actionString:(NSString **)_as
225   andPriorityString:(NSString **)_ps
226 {
227   unsigned len;
228   NSRange  r;
229   NSString *qs, *as, *ps;
230
231   if (_qs) *_qs = nil;
232   if (_as) *_as = nil;
233   if (_ps) *_ps = nil;
234   
235   if ((len = [_s length]) == 0)
236     return NO;
237   
238   /* split into qualifier and assignment/prio */
239   
240   r = [_s rangeOfString:@"=>" 
241           skipQuotes:self->ruleQuotes 
242           escapedByChar:self->ruleEscape];
243   if (r.length == 0) {
244     [self debugWithFormat:@"ERROR: missing => in rule '%@'", _s];
245     return NO;
246   }
247   
248   qs = [[_s substringToIndex:r.location] stringByTrimmingSpaces];
249   as = [_s substringFromIndex:(r.location + r.length)];
250   
251   /* split assignment and prio */
252   
253   r = [as rangeOfString:@";" 
254           skipQuotes:self->ruleQuotes 
255           escapedByChar:self->ruleEscape];
256   if (r.length == 0) {
257     /* no priority */
258     ps = nil;
259     as = [as stringByTrimmingSpaces];
260   }
261   else {
262     ps = [[as substringFromIndex:(r.location + r.length)] 
263               stringByTrimmingSpaces];
264     as = [[as substringToIndex:r.location] stringByTrimmingSpaces];
265   }
266   
267   /* return results */
268   *_qs = qs;
269   *_as = as;
270   *_ps = ps;
271   return YES;
272 }
273
274 - (EOQualifier *)parseQualifierFromString:(NSString *)_s {
275   if ([_s length] == 0)
276     return nil;
277   
278   _s = [_s stringByTrimmingSpaces];
279   
280   if ([_s isEqualToString:@"*true*"]) {
281     static EOTrueQualifier *tq = nil;
282     if (tq == nil) tq = [[EOTrueQualifier alloc] init];
283     return tq;
284   }
285   
286   return [EOQualifier qualifierWithQualifierFormat:_s arguments:nil];
287 }
288
289 - (id)parseActionFromString:(NSString *)_s {
290   NSRange  r;
291   NSString *key;
292   NSString *valstr;
293   Class    AssignmentClass;
294   id       value;
295   
296   _s = [_s stringByTrimmingSpaces];
297   
298   /* split assignment */
299   
300   r = [_s rangeOfString:@"="
301           skipQuotes:self->ruleQuotes 
302           escapedByChar:self->ruleEscape];
303   if (r.length == 0) {
304     [self debugWithFormat:@"cannot parse rule action: '%@'", _s];
305     return nil;
306   }
307   
308   key    = [[_s substringToIndex:r.location] stringByTrimmingSpaces];
309   valstr = [[_s substringFromIndex:(r.location + r.length)] 
310                 stringByTrimmingSpaces];
311
312   /* setup defaults */
313   
314   AssignmentClass = [NGRuleKeyAssignment class];
315   value = valstr;
316   
317   /* parse value */
318   
319   if ([valstr length] > 0) {
320     unichar c1 = [valstr characterAtIndex:0];
321     id tmp;
322     
323     if (c1 == '"' || c1 == '\'') {
324       /* a quoted, constant string */
325       NSString *s, *qs;
326       NSRange  r;
327       
328       AssignmentClass = [NGRuleAssignment class];
329       
330       qs = [NSString stringWithCharacters:&c1 length:1];
331       s  = [valstr substringFromIndex:1]; // TODO: perf
332       r  = [s rangeOfString:qs];
333       if (r.length == 0) {
334         [self debugWithFormat:
335                 @"quoting of assignment string-value is not closed !"];
336         value = valstr;
337       }
338       else
339         value = [s substringToIndex:r.location];
340     }
341     else if (isdigit(c1) || c1 == '-') {
342       AssignmentClass = [NGRuleAssignment class];
343       value = [NSNumber numberWithInt:[valstr intValue]];
344     }
345     else if ((tmp=[self->boolMapping objectForKey:[valstr lowercaseString]])) {
346       AssignmentClass = [NGRuleAssignment class];
347       value = tmp;
348     }
349     else if ([valstr isEqualToString:@"nil"] || 
350              [valstr isEqualToString:@"null"]) {
351       AssignmentClass = [NGRuleAssignment class];
352       value = [NSNull null];
353     }
354   }
355   
356   return [AssignmentClass assignmentWithKeyPath:key value:value];
357 }
358
359 - (int)parsePriorityFromString:(NSString *)_s {
360   unichar c1;
361   id num;
362   
363   _s = [_s stringByTrimmingSpaces];
364   // [self debugWithFormat:@"parse priority: '%@'", _s];
365   
366   if ([_s length] == 0)
367     return RULE_PRIORITY_NORMAL;
368   c1 = [_s characterAtIndex:0];
369   
370   if (isdigit(c1) || c1 == '-')
371     return [_s intValue];
372   
373   if ((num = [self->priorityMapping objectForKey:_s]))
374     return [num intValue];
375   
376   [self debugWithFormat:@"cannot parse rule priority: '%@'", _s];
377   return RULE_PRIORITY_NORMAL;
378 }
379
380 /* debugging */
381
382 - (BOOL)isDebuggingEnabled {
383   return parseDebugOn;
384 }
385
386 @end /* NGRuleParser */