Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios, macos] Change the format for case expressions to a flat structure. #11450

Merged
merged 4 commits into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions platform/darwin/src/NSExpression+MGLAdditions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -464,17 +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 = [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];
Expand Down Expand Up @@ -665,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:"] ||
Expand All @@ -690,7 +709,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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple conditional would look like this:

// If conditional, then trueExpression, else falseExpression
TERNARY(conditional, trueExpression, falseExpression)

A nested conditional would look like this:

// If firstConditional, then firstExpression,
//   else if secondConditional, then secondExpression,
//     else falseExpression
TERNARY(firstConditional, firstExpression,
        TERNARY(secondConditional, secondExpression,
                falseExpression))

So it’s the falseExpression that we need to examine here, not the trueExpression.

// 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;
Expand Down
2 changes: 2 additions & 0 deletions platform/darwin/src/NSPredicate+MGLAdditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@

@property (nonatomic, readonly) id mgl_jsonExpressionObject;

- (id)mgl_case:(id)firstValue, ...;

@end
25 changes: 25 additions & 0 deletions platform/darwin/src/NSPredicate+MGLAdditions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
43 changes: 22 additions & 21 deletions platform/darwin/test/MGLExpressionTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -573,28 +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:@"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