#import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" #import "MGLStyleValue_Private.h" #include @implementation NSPredicate (MGLPrivateAdditions) - (mbgl::style::Filter)mgl_filter { mbgl::style::conversion::Error valueError; NSArray *jsonObject = self.mgl_jsonExpressionObject; auto value = mbgl::style::conversion::convert(mbgl::style::conversion::makeConvertible(jsonObject), valueError); if (!value) { [NSException raise:NSInvalidArgumentException format:@"Invalid filter value: %@", @(valueError.message.c_str())]; return {}; } mbgl::style::Filter filter = std::move(*value); return filter; } + (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter { if (filter.expression) { id jsonObject = MGLJSONObjectFromMBGLExpression(**filter.expression); return [NSPredicate predicateWithMGLJSONObject:jsonObject]; } else { return nil; } } @end @implementation NSPredicate (MGLAdditions) NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { NSMutableArray *subpredicates = [NSMutableArray arrayWithCapacity:objects.count]; for (id object in objects) { NSPredicate *predicate = [NSPredicate predicateWithMGLJSONObject:object]; [subpredicates addObject:predicate]; } return subpredicates; } + (instancetype)predicateWithMGLJSONObject:(id)object { if ([object isEqual:@YES]) { return [NSPredicate predicateWithValue:YES]; } if ([object isEqual:@NO]) { return [NSPredicate predicateWithValue:NO]; } NSAssert([object isKindOfClass:[NSArray class]], @"Condition for case expression should be an expression."); NSArray *objects = (NSArray *)object; NSString *op = objects.firstObject; if ([op isEqualToString:@"=="]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ == %@" argumentArray:subexpressions]; } if ([op isEqualToString:@"!="]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ != %@" argumentArray:subexpressions]; } if ([op isEqualToString:@"<"]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ < %@" argumentArray:subexpressions]; } if ([op isEqualToString:@"<="]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ <= %@" argumentArray:subexpressions]; } if ([op isEqualToString:@">"]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ > %@" argumentArray:subexpressions]; } if ([op isEqualToString:@">="]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSPredicate predicateWithFormat:@"%@ >= %@" argumentArray:subexpressions]; } if ([op isEqualToString:@"!"]) { NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); if (subpredicates.count > 1) { NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; return [NSCompoundPredicate notPredicateWithSubpredicate:predicate]; } if (subpredicates.count) { return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; } return [NSPredicate predicateWithValue:YES]; } if ([op isEqualToString:@"all"]) { NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); if (subpredicates.count == 2) { // Determine if the expression is of BETWEEN type if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] && [subpredicates[1] isKindOfClass:[NSComparisonPredicate class]]) { NSComparisonPredicate *leftCondition = (NSComparisonPredicate *)subpredicates[0]; NSComparisonPredicate *rightCondition = (NSComparisonPredicate *)subpredicates[1]; NSArray *limits; NSExpression *leftConditionExpression; if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType && rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) { limits = @[leftCondition.rightExpression, rightCondition.rightExpression]; leftConditionExpression = leftCondition.leftExpression; } else if (leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType && rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) { limits = @[leftCondition.leftExpression, rightCondition.rightExpression]; leftConditionExpression = leftCondition.rightExpression; } else if(leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType && rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) { limits = @[leftCondition.leftExpression, rightCondition.leftExpression]; leftConditionExpression = leftCondition.rightExpression; } else if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType && rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) { limits = @[leftCondition.rightExpression, rightCondition.leftExpression]; leftConditionExpression = leftCondition.leftExpression; } if (limits && leftConditionExpression) { return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression, [NSExpression expressionForAggregate:limits]]; } } } return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; } if ([op isEqualToString:@"any"]) { NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; } NSExpression *expression = [NSExpression expressionWithMGLJSONObject:object]; return [NSComparisonPredicate predicateWithLeftExpression:expression rightExpression:[NSExpression expressionForConstantValue:@YES] modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0]; } - (id)mgl_jsonExpressionObject { if ([self isEqual:[NSPredicate predicateWithValue:YES]]) { return @YES; } if ([self isEqual:[NSPredicate predicateWithValue:NO]]) { return @NO; } if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("]) { [NSException raise:NSInvalidArgumentException format:@"Block-based predicates are not supported."]; } [NSException raise:NSInvalidArgumentException format:@"Unrecognized predicate type."]; return nil; } @end @implementation NSPredicate (MGLExpressionAdditions) - (id)mgl_if:(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; } - (id)mgl_match:(NSExpression *)firstCase, ... { [NSException raise:NSInvalidArgumentException format:@"Match expressions lack underlying Objective-C implementations."]; return nil; } @end