#import "NSExpression+MGLAdditions.h" #import "MGLTypes.h" #if TARGET_OS_IPHONE #import "UIColor+MGLAdditions.h" #else #import "NSColor+MGLAdditions.h" #endif @implementation NSExpression (MGLAdditions) - (std::vector)mgl_aggregateMBGLValue { if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) { std::vector convertedValues; for (id value in self.constantValue) { NSExpression *expression = value; if (![expression isKindOfClass:[NSExpression class]]) { expression = [NSExpression expressionForConstantValue:expression]; } convertedValues.push_back(expression.mgl_constantMBGLValue); } return convertedValues; } [NSException raise:NSInvalidArgumentException format:@"Constant value expression must contain an array or set."]; return {}; } - (mbgl::Value)mgl_constantMBGLValue { id value = self.constantValue; if ([value isKindOfClass:NSString.class]) { return { std::string([(NSString *)value UTF8String]) }; } else if ([value isKindOfClass:NSNumber.class]) { NSNumber *number = (NSNumber *)value; if ((strcmp([number objCType], @encode(char)) == 0) || (strcmp([number objCType], @encode(BOOL)) == 0)) { // char: 32-bit boolean // BOOL: 64-bit boolean return { (bool)number.boolValue }; } else if (strcmp([number objCType], @encode(double)) == 0) { // Double values on all platforms are interpreted precisely. return { (double)number.doubleValue }; } else if (strcmp([number objCType], @encode(float)) == 0) { // Float values when taken as double introduce precision problems, // so warn the user to avoid them. This would require them to // explicitly use -[NSNumber numberWithFloat:] arguments anyway. // We still do this conversion in order to provide a valid value. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"Float value in expression will be converted to a double; some imprecision may result. " @"Use double values explicitly when specifying constant expression values and " @"when specifying arguments to predicate and expression format strings. " @"This will be logged only once."); }); return { (double)number.doubleValue }; } else if ([number compare:@(0)] == NSOrderedDescending || [number compare:@(0)] == NSOrderedSame) { // Positive integer or zero; use uint64_t per mbgl::Value definition. // We use unsigned long long here to avoid any truncation. return { (uint64_t)number.unsignedLongLongValue }; } else if ([number compare:@(0)] == NSOrderedAscending) { // Negative integer; use int64_t per mbgl::Value definition. // We use long long here to avoid any truncation. return { (int64_t)number.longLongValue }; } } else if ([value isKindOfClass:[MGLColor class]]) { auto hexString = [(MGLColor *)value mgl_color].stringify(); return { hexString }; } else if (value && value != [NSNull null]) { [NSException raise:NSInvalidArgumentException format:@"Can’t convert %s:%@ to mbgl::Value", [value objCType], value]; } return {}; } - (std::vector)mgl_aggregateFeatureType { if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) { std::vector convertedValues; for (id value in self.constantValue) { NSExpression *expression = value; if (![expression isKindOfClass:[NSExpression class]]) { expression = [NSExpression expressionForConstantValue:expression]; } convertedValues.push_back(expression.mgl_featureType); } return convertedValues; } [NSException raise:NSInvalidArgumentException format:@"Constant value expression must contain an array or set."]; return {}; } - (mbgl::FeatureType)mgl_featureType { id value = self.constantValue; if ([value isKindOfClass:NSString.class]) { if ([value isEqualToString:@"Point"]) { return mbgl::FeatureType::Point; } if ([value isEqualToString:@"LineString"]) { return mbgl::FeatureType::LineString; } if ([value isEqualToString:@"Polygon"]) { return mbgl::FeatureType::Polygon; } } else if ([value isKindOfClass:NSNumber.class]) { switch ([value integerValue]) { case 1: return mbgl::FeatureType::Point; case 2: return mbgl::FeatureType::LineString; case 3: return mbgl::FeatureType::Polygon; default: break; } } return mbgl::FeatureType::Unknown; } - (std::vector)mgl_aggregateFeatureIdentifier { if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) { std::vector convertedValues; for (id value in self.constantValue) { NSExpression *expression = value; if (![expression isKindOfClass:[NSExpression class]]) { expression = [NSExpression expressionForConstantValue:expression]; } convertedValues.push_back(expression.mgl_featureIdentifier); } return convertedValues; } [NSException raise:NSInvalidArgumentException format:@"Constant value expression must contain an array or set."]; return {}; } - (mbgl::FeatureIdentifier)mgl_featureIdentifier { mbgl::Value mbglValue = self.mgl_constantMBGLValue; if (mbglValue.is()) { return mbglValue.get(); } if (mbglValue.is()) { return mbglValue.get(); } if (mbglValue.is()) { return mbglValue.get(); } if (mbglValue.is()) { return mbglValue.get(); } return {}; } @end