From 83ab68c9adc481bf375dc43951e39cfc3ffc15a2 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 14 Mar 2018 12:26:54 -0400 Subject: [PATCH 1/4] [ios, macos] Change the format for case expressions to a flat structure. --- platform/darwin/src/NSExpression+MGLAdditions.mm | 12 +++++++++++- platform/darwin/test/MGLExpressionTests.mm | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 5ad565c3986..c053b2bc003 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -690,7 +690,17 @@ - (id)mgl_jsonExpressionObject { } case NSConditionalExpressionType: { - NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, self.trueExpression.mgl_jsonExpressionObject, nil]; + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, nil]; + + if (self.trueExpression.expressionType == NSConditionalExpressionType) { + // Fold nested conditionals into a single case expression. + NSArray *trueArguments = self.trueExpression.mgl_jsonExpressionObject; + trueArguments = [trueArguments subarrayWithRange:NSMakeRange(1, trueArguments.count - 1)]; + [arguments addObjectsFromArray:trueArguments]; + } else { + [arguments addObject:self.trueExpression.mgl_jsonExpressionObject]; + } + if (self.falseExpression.expressionType == NSConditionalExpressionType) { // Fold nested conditionals into a single case expression. NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject; diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index a5ed2f7bf54..83d4ab0d64b 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -577,6 +577,10 @@ - (void)testConditionalExpressionObject { // https://github.com/mapbox/mapbox-gl-native/issues/11007 if (@available(iOS 9.0, *)) { { + + NSExpression *nested = [NSExpression expressionWithFormat:@"TERNARY( 1== 2, TERNARY(0==1, FALSE, TERNARY(4==5, FALSE, FALSE)), TERNARY(1==1, TRUE, FALSE))"]; + NSLog(@"%@", nested.mgl_jsonExpressionObject); + NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; From 5bb19a8c68ae8d51bd73b5a4c7050baafd355de3 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 14 Mar 2018 19:16:04 -0400 Subject: [PATCH 2/4] [ios, macos] Add support for multiple branches case expression. --- .../darwin/src/NSExpression+MGLAdditions.mm | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index c053b2bc003..5493c593810 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -465,15 +465,34 @@ + (instancetype)mgl_expressionWithJSONObject:(id)object { return [NSExpression expressionForVariable:argumentObjects.firstObject]; } else if ([op isEqualToString:@"case"]) { NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:argumentObjects.firstObject]; - NSExpression *trueExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[1]]; + NSExpression *trueExpression; + NSInteger rightBranchIndex = 2; + + if ([argumentObjects[1] isKindOfClass:[NSArray class]] ) { + NSInteger length = 1; + for (NSInteger index = 1; index < argumentObjects.count - 2; index++) { + length++; + if (![argumentObjects[index] isKindOfClass:[NSArray class]] && ![argumentObjects[index + 1] isKindOfClass:[NSArray class]]) { + break; + } + } + NSArray *trueObjects = [@[@"case"] arrayByAddingObjectsFromArray: + [argumentObjects subarrayWithRange:NSMakeRange(1, length)]]; + trueExpression = [NSExpression mgl_expressionWithJSONObject:trueObjects]; + rightBranchIndex = length + 1; + } else { + trueExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[1]]; + } + NSExpression *falseExpression; - if (argumentObjects.count > 3) { + if ([argumentObjects[rightBranchIndex] isKindOfClass:[NSArray class]] ) { NSArray *falseObjects = [@[@"case"] arrayByAddingObjectsFromArray: - [argumentObjects subarrayWithRange:NSMakeRange(2, argumentObjects.count - 2)]]; + [argumentObjects subarrayWithRange:NSMakeRange(rightBranchIndex, argumentObjects.count - rightBranchIndex)]]; falseExpression = [NSExpression mgl_expressionWithJSONObject:falseObjects]; } else { - falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[2]]; + falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[rightBranchIndex]]; } + return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; } else { [NSException raise:NSInvalidArgumentException From 7439c428a59a6964228f0b0715e110df2b89f111 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 14 Mar 2018 19:20:26 -0400 Subject: [PATCH 3/4] [ios, macos] Add multiple branch tests to case expressions. --- platform/darwin/test/MGLExpressionTests.mm | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 83d4ab0d64b..2e1c0e80faa 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -577,10 +577,6 @@ - (void)testConditionalExpressionObject { // https://github.com/mapbox/mapbox-gl-native/issues/11007 if (@available(iOS 9.0, *)) { { - - NSExpression *nested = [NSExpression expressionWithFormat:@"TERNARY( 1== 2, TERNARY(0==1, FALSE, TERNARY(4==5, FALSE, FALSE)), TERNARY(1==1, TRUE, FALSE))"]; - NSLog(@"%@", nested.mgl_jsonExpressionObject); - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; @@ -598,6 +594,20 @@ - (void)testConditionalExpressionObject { XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TERNARY(1 = 2, TRUE, FALSE), TRUE)"]; + NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @[@"==", @1, @2], @YES, @NO, @YES]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *nestedExpression = [NSExpression expressionWithFormat:@"TERNARY(1 == 2, TERNARY(0 == 1, FALSE, TERNARY(4 == 5, FALSE, FALSE)), TERNARY(1 == 1, TRUE, FALSE))"]; + NSArray *nestedJSONExpression = @[@"case", @[@"==", @1, @2], @[@"==", @0, @1], @NO, @[@"==", @4, @5], @NO, @NO, @[@"==", @1, @1], @YES, @NO]; + XCTAssertEqualObjects(nestedExpression.mgl_jsonExpressionObject, nestedJSONExpression); + XCTAssertEqualObjects([nestedExpression expressionValueWithObject:nil context:nil], @YES); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:nestedJSONExpression], nestedExpression); + } } } From a832f826b5ebfd054a6266b8b9dc55e021e61b5f Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 21 Mar 2018 15:17:11 -0400 Subject: [PATCH 4/4] [ios, macos] Case operator now has iOS 8 support. --- .../darwin/src/NSExpression+MGLAdditions.mm | 54 +++++++++--------- .../darwin/src/NSPredicate+MGLAdditions.h | 2 + .../darwin/src/NSPredicate+MGLAdditions.mm | 25 ++++++++ platform/darwin/test/MGLExpressionTests.mm | 57 +++++++------------ 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 5493c593810..bd747dc97ef 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -464,36 +464,22 @@ + (instancetype)mgl_expressionWithJSONObject:(id)object { } else if ([op isEqualToString:@"var"]) { return [NSExpression expressionForVariable:argumentObjects.firstObject]; } else if ([op isEqualToString:@"case"]) { - NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:argumentObjects.firstObject]; - NSExpression *trueExpression; - NSInteger rightBranchIndex = 2; - - if ([argumentObjects[1] isKindOfClass:[NSArray class]] ) { - NSInteger length = 1; - for (NSInteger index = 1; index < argumentObjects.count - 2; index++) { - length++; - if (![argumentObjects[index] isKindOfClass:[NSArray class]] && ![argumentObjects[index + 1] isKindOfClass:[NSArray class]]) { - break; - } - } - NSArray *trueObjects = [@[@"case"] arrayByAddingObjectsFromArray: - [argumentObjects subarrayWithRange:NSMakeRange(1, length)]]; - trueExpression = [NSExpression mgl_expressionWithJSONObject:trueObjects]; - rightBranchIndex = length + 1; - } else { - trueExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[1]]; - } + NSArray *caseExpressions = argumentObjects; + NSExpression *firstConditional = [NSExpression expressionWithFormat:@"%@", [NSPredicate mgl_predicateWithJSONObject:caseExpressions[0]]]; + NSMutableArray *arguments = [NSMutableArray array]; - NSExpression *falseExpression; - if ([argumentObjects[rightBranchIndex] isKindOfClass:[NSArray class]] ) { - NSArray *falseObjects = [@[@"case"] arrayByAddingObjectsFromArray: - [argumentObjects subarrayWithRange:NSMakeRange(rightBranchIndex, argumentObjects.count - rightBranchIndex)]]; - falseExpression = [NSExpression mgl_expressionWithJSONObject:falseObjects]; - } else { - falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[rightBranchIndex]]; + for (NSUInteger index = 1; index < caseExpressions.count; index++) { + if ([caseExpressions[index] isKindOfClass:[NSArray class]]) { + NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:caseExpressions[index]]; + NSExpression *argument = [NSExpression expressionWithFormat:@"%@", conditional]; + [arguments addObject:argument]; + } else { + [arguments addObject:[NSExpression mgl_expressionWithJSONObject:caseExpressions[index]]]; + } + } - return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + return [NSExpression expressionForFunction:firstConditional selectorName:@"mgl_case:" arguments:arguments]; } else { [NSException raise:NSInvalidArgumentException format:@"Expression operator %@ not yet implemented.", op]; @@ -684,6 +670,20 @@ - (id)mgl_jsonExpressionObject { [expressionObject addObject:obj.mgl_jsonExpressionObject]; }]; [expressionObject addObject:self.operand.mgl_jsonExpressionObject]; + return expressionObject; + } else if ([function isEqualToString:@"mgl_case:"]) { + NSPredicate *firstConditional = (NSPredicate *)self.operand.constantValue; + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"case", firstConditional.mgl_jsonExpressionObject, nil]; + + for (NSExpression *option in self.arguments) { + if ([option respondsToSelector:@selector(constantValue)] && [option.constantValue isKindOfClass:[NSComparisonPredicate class]]) { + NSPredicate *predicate = (NSPredicate *)option.constantValue; + [expressionObject addObject:predicate.mgl_jsonExpressionObject]; + } else { + [expressionObject addObject:option.mgl_jsonExpressionObject]; + } + } + return expressionObject; } else if ([function isEqualToString:@"median:"] || [function isEqualToString:@"mode:"] || diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h index 89e9e65c64d..cce7561adda 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.h +++ b/platform/darwin/src/NSPredicate+MGLAdditions.h @@ -16,4 +16,6 @@ @property (nonatomic, readonly) id mgl_jsonExpressionObject; +- (id)mgl_case:(id)firstValue, ...; + @end diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 63c8307803a..2d5b646ff28 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -324,4 +324,29 @@ - (id)mgl_jsonExpressionObject { return nil; } +- (id)mgl_case:(id)firstValue, ... { + + if ([self evaluateWithObject:nil]) { + return firstValue; + } + + id eachExpression; + va_list argumentList; + va_start(argumentList, firstValue); + + while ((eachExpression = va_arg(argumentList, id))) { + if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) { + id valueExpression = va_arg(argumentList, id); + if ([eachExpression evaluateWithObject:nil]) { + return valueExpression; + } + } else { + return eachExpression; + } + } + va_end(argumentList); + + return nil; +} + @end diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 2e1c0e80faa..3f36a0cf617 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -573,42 +573,29 @@ - (void)testInterpolationExpressionObject { } - (void)testConditionalExpressionObject { - // FIXME: This test crashes because iOS 8 doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`. - // https://github.com/mapbox/mapbox-gl-native/issues/11007 - if (@available(iOS 9.0, *)) { - { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; - NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; - NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; - NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; - NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); - } - { - NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; - NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); - } - { - NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TERNARY(1 = 2, TRUE, FALSE), TRUE)"]; - NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @[@"==", @1, @2], @YES, @NO, @YES]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); - } - { - NSExpression *nestedExpression = [NSExpression expressionWithFormat:@"TERNARY(1 == 2, TERNARY(0 == 1, FALSE, TERNARY(4 == 5, FALSE, FALSE)), TERNARY(1 == 1, TRUE, FALSE))"]; - NSArray *nestedJSONExpression = @[@"case", @[@"==", @1, @2], @[@"==", @0, @1], @NO, @[@"==", @4, @5], @NO, @NO, @[@"==", @1, @1], @YES, @NO]; - XCTAssertEqualObjects(nestedExpression.mgl_jsonExpressionObject, nestedJSONExpression); - XCTAssertEqualObjects([nestedExpression expressionValueWithObject:nil context:nil], @YES); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:nestedJSONExpression], nestedExpression); - } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_case:', %@, %@)", + [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]], + MGLConstantExpression(@YES), + MGLConstantExpression(@NO)]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_case:', %@, %@, %@, %@)", + [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]], + MGLConstantExpression(@YES), + [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 1"]], + MGLConstantExpression(@YES), + MGLConstantExpression(@NO)]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @[@"==", @1, @1], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES); + } + } @end