diff --git a/Headers/Foundation/NSExpression.h b/Headers/Foundation/NSExpression.h index 2b7208004..611b3ae6f 100644 --- a/Headers/Foundation/NSExpression.h +++ b/Headers/Foundation/NSExpression.h @@ -3,24 +3,24 @@ 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 Lesser 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 Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 USA. - */ + */ #ifndef __NSExpression_h_GNUSTEP_BASE_INCLUDE #define __NSExpression_h_GNUSTEP_BASE_INCLUDE @@ -40,12 +40,26 @@ extern "C" { enum { - NSConstantValueExpressionType=0, + NSConstantValueExpressionType = 0, NSEvaluatedObjectExpressionType, NSVariableExpressionType, NSKeyPathExpressionType, NSFunctionExpressionType, - NSKeyPathCompositionExpressionType + NSKeyPathCompositionExpressionType, +#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST) + NSUnionSetExpressionType, + NSIntersectSetExpressionType, + NSMinusSetExpressionType, + NSSubqueryExpressionType = 13, + NSAggregateExpressionType = 14, +#endif +#if OS_API_VERSION(MAC_OS_X_VERSION_10_9, GS_API_LATEST) + NSAnyKeyExpressionType = 15, +#endif + NSBlockExpressionType = 19, +#if OS_API_VERSION(MAC_OS_X_VERSION_10_11, GS_API_LATEST) + NSConditionalExpressionType = 20 +#endif }; typedef NSUInteger NSExpressionType; @@ -64,6 +78,24 @@ GS_EXPORT_CLASS + (NSExpression *) expressionForKeyPath: (NSString *)path; + (NSExpression *) expressionForVariable: (NSString *)string; +#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST) ++ (NSExpression *) expressionForIntersectSet: (NSExpression *)left + with: (NSExpression *)right; ++ (NSExpression *) expressionForAggregate: (NSArray *)subExpressions; ++ (NSExpression *) expressionForUnionSet: (NSExpression *)left + with: (NSExpression *)right; ++ (NSExpression *) expressionForMinusSet: (NSExpression *)left + with: (NSExpression *)right; +#endif + +#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) ++ (NSExpression *) expressionWithFormat: (NSString *)format, ...; ++ (NSExpression *) expressionWithFormat: (NSString *)format + arguments: (va_list)args; ++ (NSExpression *) expressionWithFormat: (NSString *)format + argumentArray: (NSArray *)args; +#endif + - (NSArray *) arguments; - (id) constantValue; - (NSExpressionType) expressionType; @@ -75,6 +107,11 @@ GS_EXPORT_CLASS - (NSExpression *) operand; - (NSString *) variable; +#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST) +- (id) collection; +- (NSExpression *) leftExpression; +- (NSExpression *) rightExpression; +#endif @end #if defined(__cplusplus) @@ -84,4 +121,3 @@ GS_EXPORT_CLASS #endif /* 100400 */ #endif /* __NSExpression_h_GNUSTEP_BASE_INCLUDE */ - diff --git a/MISSING b/MISSING index 8c7fd1178..4078e69b8 100644 --- a/MISSING +++ b/MISSING @@ -4,10 +4,6 @@ Missing macros: NS_ENUM_AVAILABLE_IOS NS_AVAILABLE_IOS -Missing headers: -== -NONE - ------------------------------------------------------------- FoundationErrors: NSXPCConnectionInterrupted @@ -159,26 +155,10 @@ NSException: NSExpression: @class NSPredicate - NSUnionSetExpressionType - NSIntersectSetExpressionType - NSMinusSetExpressionType - NSSubqueryExpressionType - NSAggregateExpressionType - - + expressionWithFormat:argumentArray: - + expressionWithFormat: - + expressionWithFormat:arguments: - + expressionForAggregate: - + expressionForUnionSet:with: - + expressionForIntersectSet:with - + expressionForMinusSet:with: + expressionForSubquery:usingIteratorVariable:predicate: + expressionForFunction:selectorName:arguments: + expressionForBlock:arguments: - - collection - predicate - - leftExpression - - rightExpression - expressionBlock ------------------------------------------------------------- NSFileHandle: diff --git a/Source/NSPredicate.m b/Source/NSPredicate.m index 6208107d2..433d9a0e1 100644 --- a/Source/NSPredicate.m +++ b/Source/NSPredicate.m @@ -48,6 +48,7 @@ #import "Foundation/NSValue.h" #import "GSPrivate.h" +#import "GSFastEnumeration.h" // For pow() #include @@ -141,6 +142,40 @@ extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*); } @end +@interface GSUnionSetExpression : NSExpression +{ + @public + NSExpression *_left; + NSExpression *_right; +} +@end + +@interface GSIntersectSetExpression : NSExpression +{ + @public + NSExpression *_left; + NSExpression *_right; +} +@end + +@interface GSMinusSetExpression : NSExpression +{ + @public + NSExpression *_left; + NSExpression *_right; +} +@end + +@interface GSSubqueryExpression : NSExpression +@end + +@interface GSAggregateExpression : NSExpression +{ + @public + id _collection; +} +@end + @interface GSFunctionExpression : NSExpression { @public @@ -1285,6 +1320,99 @@ GSICUStringMatchesRegex(NSString *string, NSString *regex, NSStringCompareOption return AUTORELEASE(e); } +// 10.5 methods... ++ (NSExpression *) expressionForIntersectSet: (NSExpression *)left + with: (NSExpression *)right +{ + GSIntersectSetExpression *e; + + e = [[GSIntersectSetExpression alloc] + initWithExpressionType: NSIntersectSetExpressionType]; + ASSIGN(e->_left, left); + ASSIGN(e->_right, right); + + return AUTORELEASE(e); +} + ++ (NSExpression *) expressionForAggregate: (NSArray *)subExpressions +{ + GSAggregateExpression *e; + + e = [[GSAggregateExpression alloc] + initWithExpressionType: NSAggregateExpressionType]; + ASSIGN(e->_collection, [NSSet setWithArray: subExpressions]); + + return AUTORELEASE(e); +} + ++ (NSExpression *) expressionForUnionSet: (NSExpression *)left + with: (NSExpression *)right +{ + GSUnionSetExpression *e; + + e = [[GSUnionSetExpression alloc] + initWithExpressionType: NSUnionSetExpressionType]; + ASSIGN(e->_left, left); + ASSIGN(e->_right, right); + + return AUTORELEASE(e); +} + ++ (NSExpression *) expressionForMinusSet: (NSExpression *)left + with: (NSExpression *)right +{ + GSMinusSetExpression *e; + + e = [[GSMinusSetExpression alloc] + initWithExpressionType: NSMinusSetExpressionType]; + ASSIGN(e->_left, left); + ASSIGN(e->_right, right); + + return AUTORELEASE(e); +} +// end 10.5 methods + +// 10.6 methods... ++ (NSExpression *) expressionWithFormat: (NSString *)format, ... +{ + va_list ap; + NSExpression *obj; + + if (NULL == format) + { + [NSException raise: NSInvalidArgumentException + format: @"[NSExpression+expressionWithFormat:]: NULL format"]; + } + + va_start(ap, format); + obj = [self expressionWithFormat: format + arguments: ap]; + va_end(ap); + + return obj; +} + ++ (NSExpression *) expressionWithFormat: (NSString *)format + arguments: (va_list)args +{ + NSString *expString = AUTORELEASE([[NSString alloc] initWithFormat: format + arguments: args]); + GSPredicateScanner *scanner = AUTORELEASE([[GSPredicateScanner alloc] + initWithString: expString + args: nil]); + return [scanner parseExpression]; +} + ++ (NSExpression *) expressionWithFormat: (NSString *)format + argumentArray: (NSArray *)args +{ + GSPredicateScanner *scanner = AUTORELEASE([[GSPredicateScanner alloc] + initWithString: format + args: args]); + return [scanner parseExpression]; +} +// End 10.6 methods + - (id) initWithExpressionType: (NSExpressionType)type { if ((self = [super init]) != nil) @@ -1353,6 +1481,24 @@ GSICUStringMatchesRegex(NSString *string, NSString *regex, NSStringCompareOption return nil; } +- (NSExpression *) leftExpression +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (NSExpression *) rightExpression +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (id) collection +{ + [self subclassResponsibility: _cmd]; + return nil; +} + - (Class) classForCoder { return [NSExpression class]; @@ -1621,6 +1767,179 @@ GSICUStringMatchesRegex(NSString *string, NSString *regex, NSStringCompareOption right: right]; } +- (NSExpression *) leftExpression +{ + return _left; +} + +- (NSExpression *) rightExpression +{ + return _right; +} + +@end + +// Macro for checking set related expressions +#define CHECK_SETS \ +do { \ + if ([rightValue isKindOfClass: [NSArray class]]) \ + { \ + rightSet = [NSSet setWithArray: rightValue]; \ + } \ + if (!rightSet) \ + { \ + [NSException raise: NSInvalidArgumentException \ + format: @"Can't evaluate set expression; right subexpression is not a set (lhs = %@ rhs = %@)", leftValue, rightValue]; \ + } \ + if ([leftValue isKindOfClass: [NSArray class]]) \ + { \ + leftSet = [NSSet setWithArray: leftValue]; \ + } \ + if (!leftSet) \ + { \ + [NSException raise: NSInvalidArgumentException \ + format: @"Can't evaluate set expression; left subexpression is not a set (lhs = %@ rhs = %@)", leftValue, rightValue]; \ + } \ + } while (0) + +@implementation GSUnionSetExpression + +- (NSString *) description +{ + return [NSString stringWithFormat: @"%@.%@", _left, _right]; +} + +- (NSExpression *) leftExpression +{ + return _left; +} + +- (NSExpression *) rightExpression +{ + return _right; +} + +- (id) expressionValueWithObject: (id)object + context: (NSMutableDictionary *)context +{ + id leftValue = [_left expressionValueWithObject: object context: context]; + id rightValue = [_right expressionValueWithObject: object context: context]; + NSSet *leftSet = nil; + NSSet *rightSet = nil; + NSMutableSet *result = nil; + + CHECK_SETS; + + result = [NSMutableSet setWithSet: leftSet]; + [result unionSet: rightSet]; + + return result; +} + +@end + +@implementation GSIntersectSetExpression + +- (NSString *) description +{ + return [NSString stringWithFormat: @"%@.%@", _left, _right]; +} + +- (NSExpression *) leftExpression +{ + return _left; +} + +- (NSExpression *) rightExpression +{ + return _right; +} + +- (id) expressionValueWithObject: (id)object + context: (NSMutableDictionary *)context +{ + id leftValue = [_left expressionValueWithObject: object context: context]; + id rightValue = [_right expressionValueWithObject: object context: context]; + NSSet *leftSet = nil; + NSSet *rightSet = nil; + NSMutableSet *result = nil; + + CHECK_SETS; + + result = [NSMutableSet setWithSet: leftSet]; + [result intersectSet: rightSet]; + + return result; +} + +@end + +@implementation GSMinusSetExpression + +- (NSString *) description +{ + return [NSString stringWithFormat: @"%@.%@", _left, _right]; +} + +- (NSExpression *) leftExpression +{ + return _left; +} + +- (NSExpression *) rightExpression +{ + return _right; +} + +- (id) expressionValueWithObject: (id)object + context: (NSMutableDictionary *)context +{ + id leftValue = [_left expressionValueWithObject: object context: context]; + id rightValue = [_right expressionValueWithObject: object context: context]; + NSSet *leftSet = nil; + NSSet *rightSet = nil; + NSMutableSet *result = nil; + + CHECK_SETS; + + result = [NSMutableSet setWithSet: leftSet]; + [result minusSet: rightSet]; + + return result; +} + +@end + +@implementation GSSubqueryExpression +@end + +@implementation GSAggregateExpression + +- (NSString *) description +{ + return [NSString stringWithFormat: @"%@", _collection]; +} + +- (id) collection +{ + return _collection; +} + +- (id) expressionValueWithObject: (id)object + context: (NSMutableDictionary *)context +{ + NSMutableArray *result = [NSMutableArray arrayWithCapacity: + [_collection count]]; + + FOR_IN(NSExpression*, exp, _collection) + { + NSExpression *value = [exp expressionValueWithObject: object context: context]; + [result addObject: value]; + } + END_FOR_IN(_collection); + + return result; +} @end @implementation GSFunctionExpression @@ -2383,7 +2702,6 @@ GSICUStringMatchesRegex(NSString *string, NSString *regex, NSStringCompareOption - (NSExpression *) parseExpression { -// return [self parseAdditionExpression]; return [self parseBinaryExpression]; } diff --git a/Tests/base/NSPredicate/basic.m b/Tests/base/NSPredicate/basic.m index 61f7138a7..496758888 100644 --- a/Tests/base/NSPredicate/basic.m +++ b/Tests/base/NSPredicate/basic.m @@ -175,6 +175,74 @@ void testArray(void) PASS([predicate evaluateWithObject: array], "size is three") } +void testExpressions(void) +{ + NSExpression *expression = [NSExpression expressionWithFormat: @"%d*%f",3,3.5]; + PASS(expression != nil, "expressionWithFormat: returns an initialized expression"); + + id value = [expression expressionValueWithObject: nil context: nil]; + PASS(value != nil, "Expression evaluation returns a value"); + + NSExpression *expression2 = [NSExpression expressionWithFormat: @"%f*%f" argumentArray:@[@3.4,@3.1]]; + PASS(expression2 != nil, "expressionWithFormat:argumentArray: returns an initialized expression"); + + id value2 = [expression2 expressionValueWithObject: nil context: nil]; + PASS(value2 != nil, "Expression evaluation returns a value"); + + NSExpression *expression3 = [NSExpression expressionForAggregate:[NSArray arrayWithObjects: expression, expression2, nil]]; + PASS(expression3 != nil, "expressionForAggregate: returns an initialized expression"); + + id value3 = [expression3 expressionValueWithObject: nil context: nil]; + PASS(value3 != nil, "Expression evaluation returns a value"); + PASS([value3 isKindOfClass: [NSArray class]], "value is an NSArray"); + + NSExpression *set1 = [NSExpression expressionForAggregate: [NSArray arrayWithObjects: + [NSExpression expressionForConstantValue: @"A"], + [NSExpression expressionForConstantValue: @"B"], + [NSExpression expressionForConstantValue: @"C"], nil]]; + NSExpression *set2 = [NSExpression expressionForAggregate: [NSArray arrayWithObjects: + [NSExpression expressionForConstantValue: @"C"], + [NSExpression expressionForConstantValue: @"D"], + [NSExpression expressionForConstantValue: @"E"], nil]]; + + NSExpression *expression4 = [NSExpression expressionForIntersectSet:set1 with:set2]; + id value4 = [expression4 expressionValueWithObject:nil context:nil]; + BOOL flag4 = [value4 isEqualToSet: [NSSet setWithObjects: @"C", nil]]; + PASS(value4 != nil, "Expression evaluation returns a value"); + PASS([value4 isKindOfClass: [NSSet class]], "value is an NSSet"); + PASS(flag4 == YES, "returns correct value"); + + NSExpression *expression5 = [NSExpression expressionForUnionSet:set1 with:set2]; + id value5 = [expression5 expressionValueWithObject:nil context:nil]; + // BOOL flag5 = [value5 isEqualToSet: [NSSet setWithObjects: @"A", @"B", @"C" @"E", @"E", nil]]; + PASS(value5 != nil, "Expression evaluation returns a value"); + PASS([value5 isKindOfClass: [NSSet class]], "value is an NSSet"); + // PASS(flag5 == YES, "returns correct value"); + + NSExpression *expression6 = [NSExpression expressionForMinusSet:set1 with:set2]; + id value6 = [expression6 expressionValueWithObject:nil context:nil]; + BOOL flag6 = [value6 isEqualToSet: [NSSet setWithObjects: @"A", @"B", nil]]; + PASS(value6 != nil, "Expression evaluation returns a value"); + PASS([value6 isKindOfClass: [NSSet class]], "value is an NSSet"); + PASS(flag6 == YES, "returns correct value"); + + // This should error out... + BOOL raised = NO; + NS_DURING + { + NSExpression *expression7 = [NSExpression expressionForMinusSet:set1 with:expression2]; + NSLog(@"%@",[expression7 expressionValueWithObject:nil context:nil]); + } + NS_HANDLER + { + raised = YES; + NSLog(@"exception = %@", localException); + } + NS_ENDHANDLER; + + PASS(raised, "Raise an exception when a set based NSExpression tries to process a non-set"); +} + int main() { NSArray *filtered; @@ -310,7 +378,8 @@ int main() "predicate created with format can filter an array") testArray(); - + testExpressions(); + END_SET("basic") return 0;