From dd301fbc0f9f988b47ad7b6532249833b3aa848c Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Mon, 26 Mar 2018 12:53:34 -0400 Subject: [ios, macos] Change the format for case expressions to a flat structure. (#11450) * [ios, macos] Change the format for case expressions to a flat structure. * [ios, macos] Add support for multiple branches case expression. * [ios, macos] Add multiple branch tests to case expressions. * [ios, macos] Case operator now has iOS 8 support. --- platform/darwin/src/NSExpression+MGLAdditions.mm | 51 +++++++++++++++++++----- platform/darwin/src/NSPredicate+MGLAdditions.h | 2 + platform/darwin/src/NSPredicate+MGLAdditions.mm | 25 ++++++++++++ platform/darwin/test/MGLExpressionTests.mm | 43 ++++++++++---------- 4 files changed, 89 insertions(+), 32 deletions(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 5ad565c398..bd747dc97e 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -464,17 +464,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } else if ([op isEqualToString:@"var"]) { 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 *falseExpression; - if (argumentObjects.count > 3) { - NSArray *falseObjects = [@[@"case"] arrayByAddingObjectsFromArray: - [argumentObjects subarrayWithRange:NSMakeRange(2, argumentObjects.count - 2)]]; - falseExpression = [NSExpression mgl_expressionWithJSONObject:falseObjects]; - } else { - falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[2]]; + NSArray *caseExpressions = argumentObjects; + NSExpression *firstConditional = [NSExpression expressionWithFormat:@"%@", [NSPredicate mgl_predicateWithJSONObject:caseExpressions[0]]]; + NSMutableArray *arguments = [NSMutableArray array]; + + 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]; @@ -665,6 +670,20 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { [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:"] || @@ -690,7 +709,17 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } 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/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h index 89e9e65c64..cce7561add 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 63c8307803..2d5b646ff2 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -324,4 +324,29 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { 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 a5ed2f7bf5..3f36a0cf61 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -573,28 +573,29 @@ using namespace std::string_literals; } - (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:@"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 -- cgit v1.2.1