From 1aa5c67837a19d5f8ba8f7336f183da83e68441c Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Thu, 17 May 2018 14:47:01 -0400 Subject: [ios, macos] Support array values in match expressions. (#11866) * [ios, macos] Support array values in match expressions. * [ios, macos] Update mgl_match documentation. * [ios, macos] Update changelogs. * [ios, macos] Clarify match expressions documentation. --- .../docs/guides/Predicates and Expressions.md | 4 +++ .../src/NSComparisonPredicate+MGLAdditions.mm | 17 ++++++------ platform/darwin/src/NSExpression+MGLAdditions.mm | 25 +++++++++++++++--- platform/darwin/test/MGLExpressionTests.mm | 6 +++++ platform/darwin/test/MGLPredicateTests.mm | 30 +++++++++++----------- platform/ios/CHANGELOG.md | 4 +++ platform/macos/CHANGELOG.md | 4 +++ 7 files changed, 62 insertions(+), 28 deletions(-) diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index 18eccda569..e0d4755d4a 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -536,6 +536,10 @@ expression that contains references to those variables. An input expression, then any number of argument pairs, followed by a default expression. Each argument pair consists of a constant value followed by an expression to produce as a result of matching that constant value. + If the input value is an aggregate expression, then any of the constant values within + that aggregate expression result in the following argument. This is shorthand for + specifying an argument pair for each of the constant values within that aggregate + expression. It is not possible to match the aggregate expression itself. diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index 380215ff32..15aa71419d 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -115,15 +115,14 @@ return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject]; } case NSInPredicateOperatorType: { - NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil]; - NSArray *optionsExpressions = self.rightExpression.constantValue; - for (id object in optionsExpressions) { - id option = ((NSExpression *)object).mgl_jsonExpressionObject; - [elements addObject:option]; - [elements addObject:@YES]; - } - [elements addObject:@NO]; - return elements; + + NSExpression *matchExpression = [NSExpression expressionForFunction:@"MGL_MATCH" + arguments:@[self.leftExpression, + self.rightExpression, + [NSExpression expressionForConstantValue:@YES], + [NSExpression expressionForConstantValue:@NO]]]; + + return matchExpression.mgl_jsonExpressionObject; } case NSContainsPredicateOperatorType: { NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index be93b13f3c..af4a197662 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -763,6 +763,11 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *array = (NSArray *)object; NSString *op = array.firstObject; + if (![op isKindOfClass:[NSString class]]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array); + return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions]; + } + NSArray *argumentObjects = [array subarrayWithRange:NSMakeRange(1, array.count - 1)]; NSString *functionName = MGLFunctionNamesByExpressionOperator[op]; @@ -940,9 +945,13 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments]; } else if ([op isEqualToString:@"match"]) { NSMutableArray *optionsArray = [NSMutableArray array]; - NSEnumerator *optionsEnumerator = argumentObjects.objectEnumerator; - while (id object = optionsEnumerator.nextObject) { - NSExpression *option = [NSExpression expressionWithMGLJSONObject:object]; + + for (NSUInteger index = 0; index < argumentObjects.count; index++) { + NSExpression *option = [NSExpression expressionWithMGLJSONObject:argumentObjects[index]]; + // match operators with arrays as matching values should not parse arrays as generic functions. + if (index > 0 && index < argumentObjects.count - 1 && !(index % 2 == 0) && [argumentObjects[index] isKindOfClass:[NSArray class]]) { + option = [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects[index])]; + } [optionsArray addObject:option]; } @@ -1346,7 +1355,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue; for (NSUInteger index = minimumIndex; index < arguments.count; index++) { - [expressionObject addObject:arguments[index].mgl_jsonExpressionObject]; + NSArray *argumentObject = arguments[index].mgl_jsonExpressionObject; + // match operators with arrays as matching values should not parse arrays using the literal operator. + if (index > 0 && index < arguments.count - 1 && !(index % 2 == 0) + && (arguments[index].expressionType == NSAggregateExpressionType || + (arguments[index].expressionType == NSConstantValueExpressionType && [arguments[index].constantValue isKindOfClass:[NSArray class]]))) { + + argumentObject = argumentObject.count == 2 ? argumentObject[1] : argumentObject; + } + [expressionObject addObject:argumentObject]; } return expressionObject; diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index e514ec22a7..dbdad2ed13 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -777,6 +777,12 @@ using namespace std::string_literals; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(x, {'a', 'A'}, 'Apple', {'b', 'B'}, 'Banana', 'Kumquat')"]; + NSArray *jsonExpression = @[@"match", @[@"get", @"x"], @[@"a", @"A"], @"Apple", @[@"b", @"B"], @"Banana", @"Kumquat"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } } - (void)testCoalesceExpressionObject { diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index 4e7b3e7e4b..a21774a8f3 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -216,64 +216,64 @@ mustRoundTrip:NO]; } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]]; + NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO]; + NSArray *expected = @[@"match", @[@"get", @"a"], @[@"b", @"c"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), { 'b', 'c' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"]; + NSArray *expected = @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index f70330300b..27ade8b4b0 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,6 +8,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * The minimum deployment target for this SDK is now iOS 9.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) +### Style layers + +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) + ## 4.0.1 - May 14, 2018 ### Packaging diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index ab71e62ec6..08093f2415 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -6,6 +6,10 @@ * The minimum deployment target for this SDK is now macOS 10.11.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) +### Style layers + +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) + ## 0.7.1 ### Style layers -- cgit v1.2.1