libs-base/Source/NSPredicate.m
Gregory John Casamento b464e5a838 Merged from baseline.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/branches/NibCompatibility@22884 72102866-910b-0410-8b05-ffd578937521
2006-05-10 03:18:08 +00:00

1867 lines
40 KiB
Objective-C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Interface for NSPredicate for GNUStep
Copyright (C) 2005 Free Software Foundation, Inc.
Written by: Dr. H. Nikolaus Schaller
Created: 2005
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#include <Foundation/NSComparisonPredicate.h>
#include <Foundation/NSCompoundPredicate.h>
#include <Foundation/NSExpression.h>
#include <Foundation/NSPredicate.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSEnumerator.h>
#include <Foundation/NSException.h>
#include <Foundation/NSKeyValueCoding.h>
#include <Foundation/NSNull.h>
#include <Foundation/NSScanner.h>
#include <Foundation/NSString.h>
#include <Foundation/NSValue.h>
#include <stdarg.h>
#define NIMP [NSException raise: NSGenericException \
format: @"%s(%s) has not implemented %s",\
GSClassNameFromObject(self), GSObjCIsInstance(self) ? "instance" : "class",\
GSNameFromSelector(_cmd)]
static BOOL scanPredicateKeyword(NSScanner *sc, NSString *key)
{
unsigned loc = [sc scanLocation]; // save to back up
unichar c;
[sc setCaseSensitive: NO];
if ([sc scanString: key intoString: NULL] == NO)
{
return NO; // no match
}
c = [[sc string] characterAtIndex: [sc scanLocation]];
if (![[NSCharacterSet alphanumericCharacterSet] characterIsMember: c])
{
return YES; // ok
}
[sc setScanLocation: loc]; // back up
return NO; // no match
}
@interface GSTruePredicate : NSPredicate
@end
@interface GSFalsePredicate : NSPredicate
@end
@interface GSAndCompoundPredicate : NSCompoundPredicate
{
@public
NSArray *_subs;
}
- (id) _initWithSubpredicates: (NSArray *)list;
@end
@interface GSOrCompoundPredicate : NSCompoundPredicate
{
@public
NSArray *_subs;
}
- (id) _initWithSubpredicates: (NSArray *)list;
@end
@interface GSNotCompoundPredicate : NSCompoundPredicate
{
@public
NSPredicate *_sub;
}
- (id) _initWithSubpredicate: (id)predicateOrList;
@end
@interface GSConstantValueExpression : NSExpression
{
@public
id _obj;
}
@end
@interface GSEvaluatedObjectExpression : NSExpression
@end
@interface GSVariableExpression : NSExpression
{
@public
NSString *_variable;
}
@end
@interface GSKeyPathExpression : NSExpression
{
@public
NSString *_keyPath;
}
@end
@interface GSFunctionExpression : NSExpression
{
@public
NSString *_function;
NSArray *_args;
NSMutableArray *_eargs; // temporary space
unsigned int _argc;
SEL _selector;
}
@end
@interface NSExpression (Private)
+ (id) _parseBinaryExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v;
+ (id) _parseExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v;
@end
@implementation NSPredicate
+ (id) _parseWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSPredicate *r;
r = [GSAndCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
if (![sc isAtEnd])
{
[NSException raise: NSInvalidArgumentException
format: @"Format string contains extra characters: \"%@\"",
[sc string]];
}
return r;
}
+ (NSPredicate *) predicateWithFormat: (NSString *) format, ...
{
NSPredicate *p;
va_list va;
va_start (va, format);
p = [self predicateWithFormat: format arguments: va];
va_end (va);
return p;
}
+ (NSPredicate *) predicateWithFormat: (NSString *)format
argumentArray: (NSArray *)args
{
NSScanner *s;
s = [NSScanner scannerWithString: format];
return [self _parseWithScanner: s args: [args objectEnumerator] vargs: NULL];
}
+ (NSPredicate *) predicateWithFormat: (NSString *)format
arguments: (va_list)args
{
NSScanner *s;
s = [NSScanner scannerWithString: format];
return [self _parseWithScanner: s args: nil vargs: args];
}
+ (NSPredicate *) predicateWithValue: (BOOL)value
{
if (value)
{
return (NSPredicate *)[GSTruePredicate new];
}
return (NSPredicate *)[GSFalsePredicate new];
}
// we don't ever instantiate NSPredicate
- (id) copyWithZone: (NSZone *)z
{
[self subclassResponsibility: _cmd];
return RETAIN(self);
}
- (BOOL) evaluateWithObject: (id)object
{
[self subclassResponsibility: _cmd];
return NO;
}
- (NSString *) description
{
return [self predicateFormat];
}
- (NSString *) predicateFormat
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
return [[self copy] autorelease];
}
- (void) encodeWithCoder: (NSCoder *) coder;
{
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *) coder;
{
[self subclassResponsibility: _cmd];
return self;
}
@end
@implementation GSTruePredicate
- (id) copyWithZone: (NSZone *) z
{
return [self retain];
}
- (BOOL) evaluateWithObject: (id)object
{
return YES;
}
- (NSString *) predicateFormat
{
return @"TRUEPREDICATE";
}
@end
@implementation GSFalsePredicate
- (id) copyWithZone: (NSZone *)z
{
return [self retain];
}
- (BOOL) evaluateWithObject: (id)object
{
return NO;
}
- (NSString *) predicateFormat
{
return @"FALSEPREDICATE";
}
@end
@implementation NSCompoundPredicate
+ (NSPredicate *) andPredicateWithSubpredicates: (NSArray *)list
{
return [[[GSAndCompoundPredicate alloc] _initWithSubpredicates: list]
autorelease];
}
+ (NSPredicate *) notPredicateWithSubpredicate: (NSPredicate *)predicate
{
return [[[GSNotCompoundPredicate alloc] _initWithSubpredicate: predicate]
autorelease];
}
+ (NSPredicate *) orPredicateWithSubpredicates: (NSArray *)list
{
return [[[GSOrCompoundPredicate alloc] _initWithSubpredicates: list]
autorelease];
}
- (NSCompoundPredicateType) compoundPredicateType
{
[self subclassResponsibility: _cmd];
return 0;
}
- (id) initWithType: (NSCompoundPredicateType)type
subpredicates: (NSArray *)list
{
[self release];
switch (type)
{
case NSAndPredicateType:
return [[GSAndCompoundPredicate alloc] _initWithSubpredicates: list];
case NSOrPredicateType:
return [[GSOrCompoundPredicate alloc] _initWithSubpredicates: list];
case NSNotPredicateType:
return [[GSNotCompoundPredicate alloc] _initWithSubpredicate: list];
default:
return nil;
}
}
- (id) copyWithZone: (NSZone *)z
{
[self subclassResponsibility: _cmd];
return [self retain];
}
- (NSArray *) subpredicates
{
[self subclassResponsibility: _cmd];
return nil;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *)coder
{
return self;
}
@end
@implementation GSAndCompoundPredicate
+ (id) _parseWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSPredicate *l;
l = [GSOrCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
while (scanPredicateKeyword(sc, @"AND"))
{
NSPredicate *r;
r = [GSOrCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
if ([r isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)r compoundPredicateType]
== NSAndPredicateType)
{ // merge
if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSAndPredicateType)
{
[(NSMutableArray *)[(NSCompoundPredicate *)l subpredicates]
addObjectsFromArray: [(NSCompoundPredicate *)r subpredicates]];
}
else
{
[(NSMutableArray *)[(NSCompoundPredicate *)r subpredicates]
insertObject: l atIndex: 0];
l = r;
}
}
else if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSAndPredicateType)
{
[(NSMutableArray *)[(NSCompoundPredicate *)l subpredicates]
addObject: r]; // add to l
}
else
{
l = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects: l, r, nil]];
}
}
return l;
}
- (id) _initWithSubpredicates: (NSArray *)list
{
NSAssert ([list count] > 1, NSInvalidArgumentException);
if ((self = [super init]) != nil)
{
_subs = [list retain];
}
return self;
}
- (void) dealloc
{
[_subs release];
[super dealloc];
}
- (NSCompoundPredicateType) compoundPredicateType
{
return NSAndPredicateType;
}
- (BOOL) evaluateWithObject: (id) object
{
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *p;
while ((p = [e nextObject]) != nil)
{
if ([p evaluateWithObject: object] == NO)
{
return NO; // any NO returns NO
}
}
return YES; // all are true
}
- (NSString *) predicateFormat
{
NSString *fmt = @"";
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *sub;
unsigned cnt = 0;
while ((sub = [e nextObject]) != nil)
{
// when to add ()? -> if sub is compound and of type "or"
if (cnt == 0)
{
fmt = [sub predicateFormat]; // first
}
else
{
if (cnt == 1
&& [[_subs objectAtIndex: 0]
isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)[_subs objectAtIndex: 0]
compoundPredicateType] == NSOrPredicateType)
{
// we need () around first OR on left side
fmt = [NSString stringWithFormat: @"(%@)", fmt];
}
if ([sub isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *) sub compoundPredicateType]
== NSOrPredicateType)
{
// we need () around right OR
fmt = [NSString stringWithFormat: @"%@ AND (%@)",
fmt, [sub predicateFormat]];
}
else
{
fmt = [NSString stringWithFormat: @"%@ AND %@",
fmt, [sub predicateFormat]];
}
}
cnt++;
}
return fmt;
}
- (NSArray *) subpredicates
{
return _subs;
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
GSAndCompoundPredicate *copy = [self copy];
unsigned int count = [copy->_subs count];
unsigned int i;
for (i = 0; i < count; i++)
{
NSPredicate *rep;
rep = [_subs objectAtIndex: i];
rep = [rep predicateWithSubstitutionVariables: variables];
[(NSMutableArray *)(copy->_subs) replaceObjectAtIndex: i
withObject: rep];
}
return [copy autorelease];
}
@end
@implementation GSOrCompoundPredicate
+ (id) _parseWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSPredicate *l;
l = [GSNotCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
while (scanPredicateKeyword(sc, @"OR"))
{
NSPredicate *r;
r = [GSNotCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
if ([r isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)r compoundPredicateType]
== NSOrPredicateType)
{ // merge
if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)l compoundPredicateType]
== NSOrPredicateType)
{
[(NSMutableArray *)[(NSCompoundPredicate *)l subpredicates]
addObjectsFromArray: [(NSCompoundPredicate *)r subpredicates]];
}
else
{
[(NSMutableArray *)[(NSCompoundPredicate *)r subpredicates]
insertObject: l atIndex: 0];
l = r;
}
}
else if ([l isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *) l compoundPredicateType]
== NSOrPredicateType)
{
[(NSMutableArray *)[(NSCompoundPredicate *)l subpredicates]
addObject: r]; // add to l
}
else
{
l = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects: l, r, nil]];
}
}
return l;
}
- (id) _initWithSubpredicates: (NSArray *)list
{
NSAssert ([list count] > 1, NSInvalidArgumentException);
if ((self = [super init]) != nil)
{
_subs = [list retain];
}
return self;
}
- (void) dealloc
{
[_subs release];
[super dealloc];
}
- (NSCompoundPredicateType) compoundPredicateType
{
return NSOrPredicateType;
}
- (BOOL) evaluateWithObject: (id)object
{
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *p;
while ((p = [e nextObject]) != nil)
{
if ([p evaluateWithObject: object] == YES)
{
return YES; // any YES returns YES
}
}
return NO; // none is true
}
- (NSString *) predicateFormat
{
NSString *fmt = @"";
NSEnumerator *e = [_subs objectEnumerator];
NSPredicate *sub;
while ((sub = [e nextObject]) != nil)
{
if ([fmt length] > 0)
{
fmt = [NSString stringWithFormat: @"%@ OR %@",
fmt, [sub predicateFormat]];
}
else
{
fmt = [sub predicateFormat]; // first
}
}
return fmt;
}
- (NSArray *) subpredicates
{
return _subs;
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
GSOrCompoundPredicate *copy = [self copy];
unsigned int count = [copy->_subs count];
unsigned int i;
for (i = 0; i < count; i++)
{
NSPredicate *rep;
rep = [_subs objectAtIndex: i];
rep = [rep predicateWithSubstitutionVariables: variables];
[(NSMutableArray *)(copy->_subs) replaceObjectAtIndex: i withObject: rep];
}
return [copy autorelease];
}
@end
@implementation GSNotCompoundPredicate
+ (id) _parseWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
if ([sc scanString: @"(" intoString: NULL])
{
NSPredicate *r;
r = [NSPredicate _parseWithScanner: sc args: e vargs: v];
if (![sc scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in compound predicate"];
}
return r;
}
if (scanPredicateKeyword(sc, @"NOT"))
{
NSPredicate *r;
r = [GSNotCompoundPredicate _parseWithScanner: sc
args: e
vargs: v];
return [self notPredicateWithSubpredicate: r]; // -> NOT NOT x or NOT (y)
}
if (scanPredicateKeyword(sc, @"TRUEPREDICATE"))
{
return [NSPredicate predicateWithValue: YES];
}
if (scanPredicateKeyword(sc, @"FALSEPREDICATE"))
{
return [NSPredicate predicateWithValue: NO];
}
return [NSComparisonPredicate _parseWithScanner: sc
args: e
vargs: v];
}
- (id) _initWithSubpredicate: (id)listOrPredicate
{
if ((self = [super init]) != nil)
{
if ([listOrPredicate isKindOfClass: [NSArray class]])
{
_sub = [[listOrPredicate objectAtIndex: 0] retain];
}
else
{
_sub = [listOrPredicate retain];
}
}
return self;
}
- (void) dealloc
{
[_sub release];
[super dealloc];
}
- (NSCompoundPredicateType) compoundPredicateType
{
return NSNotPredicateType;
}
- (BOOL) evaluateWithObject: (id)object
{
return ![_sub evaluateWithObject: object];
}
- (NSString *) predicateFormat
{
if ([_sub isKindOfClass: [NSCompoundPredicate class]]
&& [(NSCompoundPredicate *)_sub compoundPredicateType]
!= NSNotPredicateType)
{
return [NSString stringWithFormat: @"NOT(%@)", [_sub predicateFormat]];
}
return [NSString stringWithFormat: @"NOT %@", [_sub predicateFormat]];
}
- (NSArray *) subpredicates
{
return [NSArray arrayWithObject: _sub];
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
GSNotCompoundPredicate *copy = [self copy];
copy->_sub = [_sub predicateWithSubstitutionVariables: variables];
return [copy autorelease];
}
@end
@implementation NSComparisonPredicate
+ (id) _parseWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
NSPredicateOperatorType type = 0;
unsigned opts = 0;
NSExpression *left;
NSPredicate *p;
BOOL negate = NO;
if (scanPredicateKeyword(sc, @"ANY"))
{
modifier = NSAnyPredicateModifier;
}
else if (scanPredicateKeyword(sc, @"ALL"))
{
modifier = NSAllPredicateModifier;
}
else if (scanPredicateKeyword(sc, @"NONE"))
{
modifier = NSAnyPredicateModifier, negate = YES;
}
else if (scanPredicateKeyword(sc, @"SOME"))
{
modifier = NSAllPredicateModifier, negate = YES;
}
left = [NSExpression _parseBinaryExpressionWithScanner: sc
args: e vargs: v];
if ([sc scanString: @"<" intoString: NULL])
{
type = NSLessThanPredicateOperatorType;
}
else if ([sc scanString: @"<=" intoString: NULL])
{
type = NSLessThanOrEqualToPredicateOperatorType;
}
else if ([sc scanString: @">" intoString: NULL])
{
type = NSGreaterThanPredicateOperatorType;
}
else if ([sc scanString: @">=" intoString: NULL])
{
type = NSGreaterThanOrEqualToPredicateOperatorType;
}
else if ([sc scanString: @"=" intoString: NULL])
{
type = NSEqualToPredicateOperatorType;
}
else if ([sc scanString: @"!=" intoString: NULL])
{
type = NSNotEqualToPredicateOperatorType;
}
else if (scanPredicateKeyword(sc, @"MATCHES"))
{
type = NSMatchesPredicateOperatorType;
}
else if (scanPredicateKeyword(sc, @"LIKE"))
{
type = NSLikePredicateOperatorType;
}
else if (scanPredicateKeyword(sc, @"BEGINSWITH"))
{
type = NSBeginsWithPredicateOperatorType;
}
else if (scanPredicateKeyword(sc, @"ENDSWITH"))
{
type = NSEndsWithPredicateOperatorType;
}
else if (scanPredicateKeyword(sc, @"IN"))
{
type = NSInPredicateOperatorType;
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid comparison predicate: %@",
[[sc string] substringFromIndex: [sc scanLocation]]];
}
if ([sc scanString: @"[cd]" intoString: NULL])
{
opts = NSCaseInsensitivePredicateOption
| NSDiacriticInsensitivePredicateOption;
}
else if ([sc scanString: @"[c]" intoString: NULL])
{
opts = NSCaseInsensitivePredicateOption;
}
else if ([sc scanString: @"[d]" intoString: NULL])
{
opts = NSDiacriticInsensitivePredicateOption;
}
p = [self predicateWithLeftExpression: left
rightExpression: [NSExpression _parseBinaryExpressionWithScanner: sc args: e vargs: v]
modifier: modifier type: type options: opts];
return negate ? [NSCompoundPredicate notPredicateWithSubpredicate: p] : p;
}
+ (NSPredicate *) predicateWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
customSelector: (SEL) sel
{
return [[[self alloc] initWithLeftExpression: left
rightExpression: right customSelector: sel] autorelease];
}
+ (NSPredicate *) predicateWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
modifier: (NSComparisonPredicateModifier)modifier
type: (NSPredicateOperatorType)type
options: (unsigned)opts
{
return [[[self alloc] initWithLeftExpression: left rightExpression: right
modifier: modifier type: type options: opts] autorelease];
}
- (NSComparisonPredicateModifier) comparisonPredicateModifier
{
return _modifier;
}
- (SEL) customSelector
{
return _selector;
}
- (NSPredicate *) initWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
customSelector: (SEL)sel
{
if ((self = [super init]) != nil)
{
_left = [left retain];
_right = [right retain];
_selector = sel;
_type = NSCustomSelectorPredicateOperatorType;
}
return self;
}
- (id) initWithLeftExpression: (NSExpression *)left
rightExpression: (NSExpression *)right
modifier: (NSComparisonPredicateModifier)modifier
type: (NSPredicateOperatorType)type
options: (unsigned)opts
{
if ((self = [super init]) != nil)
{
_left = [left retain];
_right = [right retain];
_modifier = modifier;
_type = type;
_options = opts;
}
return self;
}
- (void) dealloc;
{
[_left release];
[_right release];
[super dealloc];
}
- (NSExpression *) leftExpression
{
return _left;
}
- (unsigned) options
{
return _options;
}
- (NSPredicateOperatorType) predicateOperatorType
{
return _type;
}
- (NSExpression *) rightExpression
{
return _right;
}
- (NSString *) predicateFormat
{
NSString *modi = @"";
NSString *comp = @"?comparison?";
NSString *opt = @"";
switch (_modifier)
{
case NSDirectPredicateModifier:
break;
case NSAnyPredicateModifier:
modi = @"ANY "; break;
case NSAllPredicateModifier:
modi = @"ALL"; break;
default:
modi = @"?modifier?"; break;
}
switch (_type)
{
case NSLessThanPredicateOperatorType:
comp = @"<"; break;
case NSLessThanOrEqualToPredicateOperatorType:
comp = @"<="; break;
case NSGreaterThanPredicateOperatorType:
comp = @">="; break;
case NSGreaterThanOrEqualToPredicateOperatorType:
comp = @">"; break;
case NSEqualToPredicateOperatorType:
comp = @"="; break;
case NSNotEqualToPredicateOperatorType:
comp = @"!="; break;
case NSMatchesPredicateOperatorType:
comp = @"MATCHES"; break;
case NSLikePredicateOperatorType:
comp = @"LIKE"; break;
case NSBeginsWithPredicateOperatorType:
comp = @"BEGINSWITH"; break;
case NSEndsWithPredicateOperatorType:
comp = @"ENDSWITH"; break;
case NSInPredicateOperatorType:
comp = @"IN"; break;
case NSCustomSelectorPredicateOperatorType:
{
comp = NSStringFromSelector (_selector);
}
}
switch (_options)
{
case NSCaseInsensitivePredicateOption:
opt = @"[c]"; break;
case NSDiacriticInsensitivePredicateOption:
opt = @"[d]"; break;
case NSCaseInsensitivePredicateOption
| NSDiacriticInsensitivePredicateOption:
opt = @"[cd]"; break;
default:
opt = @"[?options?]"; break;
}
return [NSString stringWithFormat: @"%@%@ %@%@ %@",
modi, _left, comp, opt, _right];
}
- (NSPredicate *) predicateWithSubstitutionVariables: (NSDictionary *)variables
{
NSComparisonPredicate *copy = [self copy];
// FIXME ... perform substitution in the left and right expressions
return [copy autorelease];
}
@end
@implementation NSExpression
+ (id) _parseExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
static NSCharacterSet *_identifier;
NSString *ident;
double dbl;
if ([sc scanDouble: &dbl])
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithDouble: dbl]];
}
// FIXME: handle integer, hex constants, 0x 0o 0b
if ([sc scanString: @"-" intoString: NULL])
{
return [NSExpression expressionForFunction: @"_chs" arguments:
[NSArray arrayWithObject: [self _parseExpressionWithScanner: sc
args: e vargs: v]]];
}
if ([sc scanString: @"(" intoString: NULL])
{
NSExpression *arg;
arg = [self _parseExpressionWithScanner: sc args: e vargs: v];
if (![sc scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in expression"];
}
return arg;
}
if ([sc scanString: @"{" intoString: NULL])
{
NSMutableArray *a = [NSMutableArray arrayWithCapacity: 10];
if ([sc scanString: @"}" intoString: NULL])
{
return a; // empty
}
[a addObject:
[self _parseExpressionWithScanner: sc args: e vargs: v]];
while ([sc scanString: @"," intoString: NULL])
{
[a addObject:
[self _parseExpressionWithScanner: sc args: e vargs: v]];
}
if (![sc scanString: @"}" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing } in aggregate"];
}
return a;
}
if (scanPredicateKeyword(sc, @"NULL"))
{
return [NSExpression expressionForConstantValue: [NSNull null]];
}
if (scanPredicateKeyword(sc, @"TRUE"))
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithBool: YES]];
}
if (scanPredicateKeyword(sc, @"FALSE"))
{
return [NSExpression expressionForConstantValue:
[NSNumber numberWithBool: NO]];
}
if (scanPredicateKeyword(sc, @"SELF"))
{
return [NSExpression expressionForEvaluatedObject];
}
if ([sc scanString: @"$" intoString: NULL])
{ // variable
NSExpression *var;
var = [self _parseExpressionWithScanner: sc args: e vargs: v];
if (![var keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid variable identifier: %@", var];
}
return [NSExpression expressionForVariable: [var keyPath]];
}
if (scanPredicateKeyword(sc, @"%K"))
{
if (e)
{
return [NSExpression expressionForKeyPath: [e nextObject]];
// should we even allow to pass in/pass through an NSExpression to %K and convert only NSString objects to keyPaths?
}
return [NSExpression expressionForKeyPath: va_arg (v, id)];
}
if (scanPredicateKeyword(sc, @"%@"))
{
if (e)
{
return [NSExpression expressionForConstantValue: [e nextObject]];
}
return [NSExpression expressionForConstantValue: va_arg (v, id)];
}
// FIXME: other formats
if ([sc scanString: @"\"" intoString: NULL])
{
NSString *str = @"string constant";
return [NSExpression expressionForConstantValue: str];
}
if ([sc scanString: @"'" intoString: NULL])
{
NSString *str = @"string constant";
return [NSExpression expressionForConstantValue: str];
}
if ([sc scanString: @"@" intoString: NULL])
{
NSExpression *tmp;
tmp = [self _parseExpressionWithScanner: sc args: e vargs: v];
if (![tmp keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid keypath identifier: %@", tmp];
}
return [NSExpression expressionForKeyPath:
[NSString stringWithFormat: @"@%@", [tmp keyPath]]];
}
[sc scanString: @"#" intoString: NULL]; // skip # as prefix (reserved words)
if (!_identifier)
{
_identifier = [[NSCharacterSet characterSetWithCharactersInString: @"_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"] retain];
}
if (![sc scanCharactersFromSet: _identifier intoString: &ident])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing identifier: %@",
[[sc string] substringFromIndex: [sc scanLocation]]];
}
return [NSExpression expressionForKeyPath: ident];
}
+ (id) _parseFunctionalExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSExpression *left;
left = [self _parseExpressionWithScanner: sc
args: e
vargs: v];
while (YES)
{
if ([sc scanString: @"(" intoString: NULL])
{ // function - this parser allows for (max)(a, b, c) to be properly recognized and even (%K)(a, b, c) if %K evaluates to "max"
NSMutableArray *args = [NSMutableArray arrayWithCapacity: 5];
if (![left keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid function identifier: %@", left];
}
if (![sc scanString: @")" intoString: NULL])
{
[args addObject:
[self _parseExpressionWithScanner: sc args: e vargs: v]];
while ([sc scanString: @"," intoString: NULL])
{
[args addObject: [self _parseExpressionWithScanner:
sc args: e vargs: v]];
}
if (![sc scanString: @")" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ) in function arguments"];
}
}
left = [NSExpression expressionForFunction: [left keyPath]
arguments: args];
}
else if ([sc scanString: @"[" intoString: NULL])
{ // index expression
NSArray *a;
a = [NSArray arrayWithObject:
[self _parseExpressionWithScanner: sc args: e vargs: v]];
if (scanPredicateKeyword(sc, @"FIRST"))
{
left = [NSExpression expressionForFunction: @"_first"
arguments: a];
}
else if (scanPredicateKeyword(sc, @"LAST"))
{
left = [NSExpression expressionForFunction: @"_last"
arguments: a];
}
else if (scanPredicateKeyword(sc, @"SIZE"))
{
left = [NSExpression expressionForFunction: @"count"
arguments: a];
}
else
{
left = [NSExpression expressionForFunction: @"_index"
arguments: a];
}
if (![sc scanString: @"]" intoString: NULL])
{
[NSException raise: NSInvalidArgumentException
format: @"Missing ] in index argument"];
}
}
else if ([sc scanString: @"." intoString: NULL])
{ // keypath - this parser allows for (a).(b.c) to be properly recognized and even %K.((%K)) if the first %K evaluates to "a" and the second %K to "b.c"
NSExpression *right;
if (![left keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid left keypath: %@", left];
}
right
= [self _parseExpressionWithScanner: sc args: e vargs: v];
if (![right keyPath])
{
[NSException raise: NSInvalidArgumentException
format: @"Invalid right keypath: %@", left];
}
left = [NSExpression expressionForKeyPath:
[NSString stringWithFormat: @"%@.%@",
[left keyPath], [right keyPath]]]; // concatenate
}
else
{
return left; // done with suffixes
}
}
}
+ (id) _parsePowerExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSExpression *left;
left = [self _parseFunctionalExpressionWithScanner: sc
args: e
vargs: v];
while (YES)
{
NSExpression *right;
if ([sc scanString: @"**" intoString: NULL])
{
right = [self _parseFunctionalExpressionWithScanner: sc
args: e
vargs: v];
}
else
{
return left;
}
}
}
+ (id) _parseMultiplicationExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSExpression *left;
left = [self _parsePowerExpressionWithScanner: sc args: e vargs: v];
while (YES)
{
NSExpression *right;
if ([sc scanString: @"*" intoString: NULL])
{
right = [self _parsePowerExpressionWithScanner: sc args: e vargs: v];
}
else if ([sc scanString: @"/" intoString: NULL])
{
right = [self _parsePowerExpressionWithScanner: sc args: e vargs: v];
}
else
{
return left;
}
}
}
+ (id) _parseAdditionExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSExpression *left;
left = [self _parseMultiplicationExpressionWithScanner: sc
args: e
vargs: v];
while (YES)
{
NSExpression *right;
if ([sc scanString: @"+" intoString: NULL])
{
right = [self _parseMultiplicationExpressionWithScanner: sc
args: e
vargs: v];
}
else if ([sc scanString: @"-" intoString: NULL])
{
right = [self _parseMultiplicationExpressionWithScanner: sc
args: e
vargs: v];
}
else
{
return left;
}
}
}
+ (id) _parseBinaryExpressionWithScanner: (NSScanner *)sc
args: (NSEnumerator*)e
vargs: (va_list)v
{
NSExpression *left;
left = [self _parseAdditionExpressionWithScanner: sc
args: e
vargs: v];
while (YES)
{
NSExpression *right;
if ([sc scanString: @": =" intoString: NULL]) // assignment
{
// check left to be a variable?
right = [self _parseAdditionExpressionWithScanner: sc
args: e
vargs: v];
}
else
{
return left;
}
}
}
+ (NSExpression *) expressionForConstantValue: (id)obj
{
GSConstantValueExpression *e;
e = [[[GSConstantValueExpression alloc] init] autorelease];
e->_obj = [obj retain];
return e;
}
+ (NSExpression *) expressionForEvaluatedObject
{
return [[[GSEvaluatedObjectExpression alloc] init] autorelease];
}
+ (NSExpression *) expressionForFunction: (NSString *)name
arguments: (NSArray *)args
{
GSFunctionExpression *e;
NSString *s;
e = [[[GSFunctionExpression alloc] init] autorelease];
s = [NSString stringWithFormat: @"_eval_%@: context: ", name];
e->_selector = NSSelectorFromString(s);
if (![e respondsToSelector: e->_selector])
{
[NSException raise: NSInvalidArgumentException
format: @"Unknown function implementation: %@", name];
}
e->_function = [name retain];
e->_argc = [args count];
e->_args = [args retain];
e->_eargs = [args copy]; // space for evaluated arguments
return e;
}
+ (NSExpression *) expressionForKeyPath: (NSString *)path
{
GSKeyPathExpression *e;
if (![path isKindOfClass: [NSString class]])
{
[NSException raise: NSInvalidArgumentException
format: @"Keypath is not NSString: %@", path];
}
e = [[[GSKeyPathExpression alloc] init] autorelease];
e->_keyPath = [path retain];
return e;
}
+ (NSExpression *) expressionForVariable: (NSString *)string
{
GSVariableExpression *e;
e = [[[GSVariableExpression alloc] init] autorelease];
e->_variable = [string retain];
return e;
}
- (NSArray *) arguments
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) constantValue
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) description
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpressionType) expressionType
{
[self subclassResponsibility: _cmd];
return 0;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) function
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) keyPath
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSExpression *) operand
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) variable
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) initWithExpressionType: (NSExpressionType)type
{
[self release];
switch (type)
{
case NSConstantValueExpressionType:
return [[GSConstantValueExpression alloc] init];
case NSEvaluatedObjectExpressionType:
return [[GSEvaluatedObjectExpression alloc] init];
case NSVariableExpressionType:
return [[GSVariableExpression alloc] init];
case NSKeyPathExpressionType:
return [[GSKeyPathExpression alloc] init];
case NSFunctionExpressionType:
return [[GSFunctionExpression alloc] init];
default:
return nil;
}
}
- (id) copyWithZone: (NSZone *)z
{
[self subclassResponsibility: _cmd];
return nil;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[self subclassResponsibility: _cmd];
}
- (id) initWithCoder: (NSCoder *)coder
{
[self subclassResponsibility: _cmd];
return nil;
}
@end
@implementation GSConstantValueExpression
- (NSArray *) arguments
{
return nil;
}
- (id) constantValue
{
return _obj;
}
- (NSString *) description
{
return _obj;
}
- (NSExpressionType) expressionType
{
return NSConstantValueExpressionType;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return _obj;
}
- (NSString *) function
{
return nil;
}
- (NSString *) keyPath
{
return nil;
}
- (NSExpression *) operand
{
return nil;
}
- (NSString *) variable
{
return nil;
}
- (void) dealloc
{
[_obj release];
[super dealloc];
}
@end
@implementation GSEvaluatedObjectExpression
- (NSArray *) arguments
{
return nil;
}
- (id) constantValue
{
return nil;
}
- (NSString *) description
{
return @"SELF";
}
- (NSExpressionType) expressionType
{
return NSEvaluatedObjectExpressionType;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return self;
}
- (NSString *) function
{
return nil;
}
- (NSString *) keyPath
{
return nil;
}
- (NSExpression *) operand
{
return nil;
}
- (NSString *) variable
{
return nil;
}
@end
@implementation GSVariableExpression
- (NSArray *) arguments
{
return nil;
}
- (id) constantValue
{
return nil;
}
- (NSString *) description
{
return [NSString stringWithFormat: @"$%@", _variable];
}
- (NSExpressionType) expressionType
{
return NSVariableExpressionType;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return [context objectForKey: _variable];
}
- (NSString *) function
{
return nil;
}
- (NSString *) keyPath
{
return nil;
}
- (NSExpression *) operand
{
return nil;
}
- (NSString *) variable
{
return _variable;
}
- (void) dealloc;
{
[_variable release];
[super dealloc];
}
@end
@implementation GSKeyPathExpression
- (NSArray *) arguments
{
return nil;
}
- (id) constantValue
{
return nil;
}
- (NSString *) description
{
return _keyPath;
}
- (NSExpressionType) expressionType
{
return NSKeyPathExpressionType;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
return [object valueForKeyPath: _keyPath];
}
- (NSString *) function
{
return nil;
}
- (NSString *) keyPath
{
return _keyPath;
}
- (NSExpression *) operand
{
return nil;
}
- (NSString *) variable
{
return nil;
}
- (void) dealloc;
{
[_keyPath release];
[super dealloc];
}
@end
@implementation GSFunctionExpression
- (NSArray *) arguments
{
return _args;
}
- (id) constantValue
{
return nil;
}
- (NSString *) description
{
// here we should recognize binary and unary operators
// and convert back to standard format
// and add parentheses if required
return [NSString stringWithFormat: @"%@(%@)",
[NSStringFromSelector (_selector) substringFromIndex: 6], _args];
}
- (NSExpressionType) expressionType
{
return NSFunctionExpressionType;
}
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{ // apply method selector
unsigned int i;
for (i = 0; i < _argc; i++)
{
id o;
o = [_args objectAtIndex: i];
o = [o expressionValueWithObject: object context: context];
[_eargs replaceObjectAtIndex: i withObject: o];
}
return [self performSelector: _selector
withObject: object
withObject: context];
}
- (id) _eval__chs: (id)object context: (NSMutableDictionary *)context
{
return [NSNumber numberWithInt: -[[_eargs objectAtIndex: 0] intValue]];
}
- (id) _eval__first: (id)object context: (NSMutableDictionary *)context
{
return [[_eargs objectAtIndex: 0] objectAtIndex: 0];
}
- (id) _eval__last: (id)object context: (NSMutableDictionary *)context
{
return [[_eargs objectAtIndex: 0] lastObject];
}
- (id) _eval__index: (id)object context: (NSMutableDictionary *)context
{
if ([[_eargs objectAtIndex: 0] isKindOfClass: [NSDictionary class]])
return [[_eargs objectAtIndex: 0] objectForKey: [_eargs objectAtIndex: 1]];
return [[_eargs objectAtIndex: 0] objectAtIndex: [[_eargs objectAtIndex: 1] unsignedIntValue]]; // raises exception if invalid
}
- (id) _eval_count: (id)object context: (NSMutableDictionary *)context
{
if (_argc != 1)
; // error
return [NSNumber numberWithUnsignedInt: [[_eargs objectAtIndex: 0] count]];
}
- (id) _eval_avg: (NSArray *)expressions
context: (NSMutableDictionary *)context
{
NIMP;
return [NSNumber numberWithDouble: 0.0];
}
- (id) _eval_sum: (NSArray *)expressions
context: (NSMutableDictionary *)context
{
NIMP;
return [NSNumber numberWithDouble: 0.0];
}
- (id) _eval_min: (NSArray *)expressions
context: (NSMutableDictionary *)context
{
NIMP;
return [NSNumber numberWithDouble: 0.0];
}
- (id) _eval_max: (NSArray *)expressions
context: (NSMutableDictionary *)context
{
NIMP;
return [NSNumber numberWithDouble: 0.0];
}
// add arithmetic functions: average, median, mode, stddev, sqrt, log, ln, exp, floor, ceiling, abs, trunc, random, randomn, now
- (NSString *) function
{
return _function;
}
- (NSString *) keyPath
{
return nil;
}
- (NSExpression *) operand
{
return nil;
}
- (NSString *) variable
{
return nil;
}
- (void) dealloc;
{
[_args release];
[_eargs release];
[_function release];
[super dealloc];
}
@end
@implementation NSArray (NSPredicate)
- (NSArray *) filteredArrayUsingPredicate: (NSPredicate *)predicate
{
NSMutableArray *result;
NSEnumerator *e = [self objectEnumerator];
id object;
result = [NSMutableArray arrayWithCapacity: [self count]];
while ((object = [e nextObject]) != nil)
{
if ([predicate evaluateWithObject: object] == YES)
{
[result addObject: object]; // passes filter
}
}
return result; // we could/should convert to a non-mutable copy
}
@end