diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index 2461fcef0..8c7afb572 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -79,6 +79,52 @@ @implementation NSComparisonPredicate (MGLExpressionAdditions) - (id)mgl_jsonExpressionObject { + switch (self.comparisonPredicateModifier) { + case NSDirectPredicateModifier: + break; + + case NSAllPredicateModifier: + // “ALL x != y” is logically equivalent to “NOT y IN x”. + if (self.predicateOperatorType == NSNotEqualToPredicateOperatorType) { + // https://github.com/mapbox/mapbox-gl-js/issues/9339 + if (self.options) { + [NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ALL … !=” comparisons."]; + } + + NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression + rightExpression:self.leftExpression + modifier:NSDirectPredicateModifier + type:NSInPredicateOperatorType + options:self.options]; + NSPredicate *invertedPredicate = [NSCompoundPredicate notPredicateWithSubpredicate:directPredicate]; + return invertedPredicate.mgl_jsonExpressionObject; + } else { + [NSException raise:NSInvalidArgumentException format:@"“ALL” is only supported for the “!=” operator."]; + } + + case NSAnyPredicateModifier: + // “ANY x = y” is logically equivalent to “y IN x”. + if (self.predicateOperatorType == NSEqualToPredicateOperatorType) { + // https://github.com/mapbox/mapbox-gl-js/issues/9339 + if (self.options) { + [NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ANY … =” comparisons."]; + } + + NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression + rightExpression:self.leftExpression + modifier:NSDirectPredicateModifier + type:NSInPredicateOperatorType + options:self.options]; + return directPredicate.mgl_jsonExpressionObject; + } else { + [NSException raise:NSInvalidArgumentException format:@"“ANY” or “SOME” is only supported for the “=” operator."]; + } + + default: + [NSException raise:NSInvalidArgumentException + format:@"NSComparisonPredicateModifier:%lu is not supported.", (unsigned long)self.comparisonPredicateModifier]; + } + NSString *op; switch (self.predicateOperatorType) { case NSLessThanPredicateOperatorType: diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index b39ec2c57..181899ab0 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -315,6 +315,53 @@ XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter); XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter], [NSPredicate predicateWithValue:NO]); } + { + NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} = $featureIdentifier"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"]; + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] + mustRoundTrip:YES]; + } + { + NSArray *expected = @[@"!", @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} = $featureIdentifier"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"]; + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] + mustRoundTrip:YES]; + } + { + NSArray *expected = @[@"!", @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} != $featureIdentifier"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT $featureIdentifier IN {6, 5, 4, 3}"]; + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] + mustRoundTrip:YES]; + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} != $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} > $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} != $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} = $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} < $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } } - (void)testComparisonPredicatesWithOptions { @@ -388,6 +435,20 @@ @"locale": @"tlh"}]]; XCTAssertEqualObjects([predicate.mgl_jsonExpressionObject lastObject], jsonExpression); } + + // https://github.com/mapbox/mapbox-gl-js/issues/9339 + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY {6, 5, 4, 3} =[c] $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE {6, 5, 4, 3} =[d] $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL {6, 5, 4, 3} !=[cd] $featureIdentifier"]; + XCTAssertThrowsSpecificNamed(predicate.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } } - (void)testCompoundPredicates {