[ios, macos] Convert token strings to expressions on input
Removes mgl_expressionByReplacingTokensWithKeyPaths and associated code. Converting on output is no longer necessary: from the prior commit, core converts token strings to expressions at parse time; all that's necessary is to ensure that the runtime styling API does so as well.
This commit is contained in:
@@ -116,8 +116,7 @@
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultBackgroundPattern();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setBackgroundPatternTransition:(MGLTransition )transition {
|
||||
|
||||
@@ -231,8 +231,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultFillExtrusionPattern();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
|
||||
|
||||
@@ -220,8 +220,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultFillPattern();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setFillPatternTransition:(MGLTransition )transition {
|
||||
|
||||
@@ -390,8 +390,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultLinePattern();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setLinePatternTransition:(MGLTransition )transition {
|
||||
|
||||
@@ -121,6 +121,16 @@ namespace mbgl {
|
||||
- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> {
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
<% if (property.tokens) { -%>
|
||||
if (<%- objCName(property) %> && <%- objCName(property) %>.expressionType == NSConstantValueExpressionType) {
|
||||
std::string string = ((NSString *)<%- objCName(property) %>.constantValue).UTF8String;
|
||||
if (mbgl::style::conversion::hasTokens(string)) {
|
||||
self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbgl::style::DataDrivenPropertyValue<std::string>(
|
||||
mbgl::style::conversion::convertTokenStringToExpression(string)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
<% } -%>
|
||||
<% if (isDataDriven(property)) { -%>
|
||||
auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
|
||||
<% } else { -%>
|
||||
@@ -136,12 +146,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
|
||||
}
|
||||
<% if (property.type === 'string') { -%>
|
||||
NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
<% } else { -%>
|
||||
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
|
||||
<% } -%>
|
||||
}
|
||||
|
||||
<% if (property.original) { -%>
|
||||
@@ -183,12 +188,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
|
||||
}
|
||||
<% if (property.type === 'string') { -%>
|
||||
NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
<% } else { -%>
|
||||
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
|
||||
<% } -%>
|
||||
}
|
||||
<% if (property["transition"]) { -%>
|
||||
|
||||
|
||||
@@ -222,6 +222,14 @@ namespace mbgl {
|
||||
- (void)setIconImageName:(NSExpression *)iconImageName {
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
if (iconImageName && iconImageName.expressionType == NSConstantValueExpressionType) {
|
||||
std::string string = ((NSString *)iconImageName.constantValue).UTF8String;
|
||||
if (mbgl::style::conversion::hasTokens(string)) {
|
||||
self.rawLayer->setIconImage(mbgl::style::DataDrivenPropertyValue<std::string>(
|
||||
mbgl::style::conversion::convertTokenStringToExpression(string)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(iconImageName);
|
||||
self.rawLayer->setIconImage(mbglValue);
|
||||
}
|
||||
@@ -233,8 +241,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultIconImage();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setIconImage:(NSExpression *)iconImage {
|
||||
@@ -568,6 +575,14 @@ namespace mbgl {
|
||||
- (void)setText:(NSExpression *)text {
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
if (text && text.expressionType == NSConstantValueExpressionType) {
|
||||
std::string string = ((NSString *)text.constantValue).UTF8String;
|
||||
if (mbgl::style::conversion::hasTokens(string)) {
|
||||
self.rawLayer->setTextField(mbgl::style::DataDrivenPropertyValue<std::string>(
|
||||
mbgl::style::conversion::convertTokenStringToExpression(string)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(text);
|
||||
self.rawLayer->setTextField(mbglValue);
|
||||
}
|
||||
@@ -579,8 +594,7 @@ namespace mbgl {
|
||||
if (propertyValue.isUndefined()) {
|
||||
propertyValue = self.rawLayer->getDefaultTextField();
|
||||
}
|
||||
NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
return expression.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
|
||||
}
|
||||
|
||||
- (void)setTextField:(NSExpression *)textField {
|
||||
|
||||
@@ -389,112 +389,6 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier =
|
||||
return {};
|
||||
}
|
||||
|
||||
// Selectors of functions that can contain tokens in arguments.
|
||||
static NSArray * const MGLTokenizedFunctions = @[
|
||||
@"mgl_interpolateWithCurveType:parameters:stops:",
|
||||
@"mgl_interpolate:withCurveType:parameters:stops:",
|
||||
@"mgl_stepWithMinimum:stops:",
|
||||
@"mgl_step:from:stops:",
|
||||
];
|
||||
|
||||
/**
|
||||
Returns a copy of the given collection with tokens replaced by key path
|
||||
expressions.
|
||||
|
||||
If no replacements take place, this method returns the original collection.
|
||||
*/
|
||||
NSArray<NSExpression *> *MGLCollectionByReplacingTokensWithKeyPaths(NSArray<NSExpression *> *collection) {
|
||||
__block NSMutableArray *upgradedCollection;
|
||||
[collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
if (upgradedItem != item) {
|
||||
if (!upgradedCollection) {
|
||||
upgradedCollection = [collection mutableCopy];
|
||||
}
|
||||
upgradedCollection[idx] = upgradedItem;
|
||||
}
|
||||
}];
|
||||
return upgradedCollection ?: collection;
|
||||
};
|
||||
|
||||
/**
|
||||
Returns a copy of the given stop dictionary with tokens replaced by key path
|
||||
expressions.
|
||||
|
||||
If no replacements take place, this method returns the original stop
|
||||
dictionary.
|
||||
*/
|
||||
NSDictionary<NSNumber *, NSExpression *> *MGLStopDictionaryByReplacingTokensWithKeyPaths(NSDictionary<NSNumber *, NSExpression *> *stops) {
|
||||
__block NSMutableDictionary *upgradedStops;
|
||||
[stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
|
||||
if (![value isKindOfClass:[NSExpression class]]) {
|
||||
value = [NSExpression expressionForConstantValue:value];
|
||||
}
|
||||
NSExpression *upgradedValue = value.mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
if (upgradedValue != value) {
|
||||
if (!upgradedStops) {
|
||||
upgradedStops = [stops mutableCopy];
|
||||
}
|
||||
upgradedStops[zoomLevel] = upgradedValue;
|
||||
}
|
||||
}];
|
||||
return upgradedStops ?: stops;
|
||||
};
|
||||
|
||||
- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths {
|
||||
switch (self.expressionType) {
|
||||
case NSConstantValueExpressionType: {
|
||||
NSString *constantValue = self.constantValue;
|
||||
if ([constantValue isKindOfClass:[NSString class]] &&
|
||||
[constantValue containsString:@"{"] && [constantValue containsString:@"}"]) {
|
||||
NSMutableArray *components = [NSMutableArray array];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:constantValue];
|
||||
scanner.charactersToBeSkipped = nil;
|
||||
while (!scanner.isAtEnd) {
|
||||
NSString *string;
|
||||
if ([scanner scanUpToString:@"{" intoString:&string]) {
|
||||
[components addObject:[NSExpression expressionForConstantValue:string]];
|
||||
}
|
||||
|
||||
NSString *token;
|
||||
if ([scanner scanString:@"{" intoString:NULL]
|
||||
&& [scanner scanUpToString:@"}" intoString:&token]
|
||||
&& [scanner scanString:@"}" intoString:NULL]) {
|
||||
[components addObject:[NSExpression expressionForKeyPath:token]];
|
||||
}
|
||||
}
|
||||
if (components.count == 1) {
|
||||
return components.firstObject;
|
||||
}
|
||||
return [NSExpression expressionForFunction:@"mgl_join:"
|
||||
arguments:@[[NSExpression expressionForAggregate:components]]];
|
||||
}
|
||||
NSDictionary *stops = self.constantValue;
|
||||
if ([stops isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary *localizedStops = MGLStopDictionaryByReplacingTokensWithKeyPaths(stops);
|
||||
if (localizedStops != stops) {
|
||||
return [NSExpression expressionForConstantValue:localizedStops];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
case NSFunctionExpressionType: {
|
||||
if ([MGLTokenizedFunctions containsObject:self.function]) {
|
||||
NSArray *arguments = self.arguments;
|
||||
NSArray *localizedArguments = MGLCollectionByReplacingTokensWithKeyPaths(arguments);
|
||||
if (localizedArguments != arguments) {
|
||||
return [NSExpression expressionForFunction:self.operand selectorName:self.function arguments:localizedArguments];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
default:
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSObject (MGLExpressionAdditions)
|
||||
|
||||
@@ -27,11 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
|
||||
@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
|
||||
|
||||
/**
|
||||
Returns a copy of the receiver with tokens replaced by key path expressions.
|
||||
*/
|
||||
- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSNull (MGLExpressionAdditions)
|
||||
|
||||
@@ -995,47 +995,6 @@ using namespace std::string_literals;
|
||||
|
||||
#pragma mark - Localization tests
|
||||
|
||||
- (void)testTokenReplacement {
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"");
|
||||
NSExpression *expected = original;
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"{");
|
||||
NSExpression *expected = original;
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"{token");
|
||||
NSExpression *expected = original;
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"{token}");
|
||||
NSExpression *expected = [NSExpression expressionForKeyPath:@"token"];
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"{token} {token}");
|
||||
NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_join({token, ' ', token})"];
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
{
|
||||
NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, '{short}', %@)", @{
|
||||
@1: MGLConstantExpression(@"{short}"),
|
||||
@2: @"…",
|
||||
@3: @"{long}",
|
||||
}];
|
||||
NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
|
||||
@1: [NSExpression expressionForKeyPath:@"short"],
|
||||
@2: @"…",
|
||||
@3: [NSExpression expressionForKeyPath:@"long"],
|
||||
}];
|
||||
XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testLocalization {
|
||||
{
|
||||
NSExpression *original = MGLConstantExpression(@"");
|
||||
|
||||
@@ -155,6 +155,23 @@
|
||||
MGLTransition <%- camelizeWithLeadingLowercase(originalPropertyName(property)) %>Transition = layer.<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %>Transition;
|
||||
XCTAssertEqual(<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %>Transition.delay, transitionTest.delay);
|
||||
XCTAssertEqual(<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %>Transition.duration, transitionTest.duration);
|
||||
<% } -%>
|
||||
<% if (property.tokens) { -%>
|
||||
|
||||
// Tokens test
|
||||
layer.<%- objCName(property) %> = [NSExpression expressionForConstantValue:@"{token}"];
|
||||
|
||||
{
|
||||
using namespace mbgl::style::expression::dsl;
|
||||
propertyValue = mbgl::style::PropertyExpression<<%- mbglType(property) %>>(
|
||||
toString(get(literal("token")))
|
||||
);
|
||||
}
|
||||
|
||||
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
|
||||
@"Setting <%- objCName(property) %> to a constant string with tokens should convert to an expression.");
|
||||
XCTAssertEqualObjects(layer.<%- objCName(property) %>, [NSExpression expressionWithFormat:@"CAST(token, \"NSString\")"],
|
||||
@"Setting <%- objCName(property) %> to a constant string with tokens should convert to an expression.");
|
||||
<% } -%>
|
||||
}
|
||||
<% } -%>
|
||||
|
||||
@@ -211,6 +211,21 @@
|
||||
@"Unsetting iconImageName should return icon-image to the default value.");
|
||||
XCTAssertEqualObjects(layer.iconImageName, defaultExpression,
|
||||
@"iconImageName should return the default value after being unset.");
|
||||
|
||||
// Tokens test
|
||||
layer.iconImageName = [NSExpression expressionForConstantValue:@"{token}"];
|
||||
|
||||
{
|
||||
using namespace mbgl::style::expression::dsl;
|
||||
propertyValue = mbgl::style::PropertyExpression<std::string>(
|
||||
toString(get(literal("token")))
|
||||
);
|
||||
}
|
||||
|
||||
XCTAssertEqual(rawLayer->getIconImage(), propertyValue,
|
||||
@"Setting iconImageName to a constant string with tokens should convert to an expression.");
|
||||
XCTAssertEqualObjects(layer.iconImageName, [NSExpression expressionWithFormat:@"CAST(token, \"NSString\")"],
|
||||
@"Setting iconImageName to a constant string with tokens should convert to an expression.");
|
||||
}
|
||||
|
||||
// icon-offset
|
||||
@@ -1065,6 +1080,21 @@
|
||||
@"Unsetting text should return text-field to the default value.");
|
||||
XCTAssertEqualObjects(layer.text, defaultExpression,
|
||||
@"text should return the default value after being unset.");
|
||||
|
||||
// Tokens test
|
||||
layer.text = [NSExpression expressionForConstantValue:@"{token}"];
|
||||
|
||||
{
|
||||
using namespace mbgl::style::expression::dsl;
|
||||
propertyValue = mbgl::style::PropertyExpression<std::string>(
|
||||
toString(get(literal("token")))
|
||||
);
|
||||
}
|
||||
|
||||
XCTAssertEqual(rawLayer->getTextField(), propertyValue,
|
||||
@"Setting text to a constant string with tokens should convert to an expression.");
|
||||
XCTAssertEqualObjects(layer.text, [NSExpression expressionWithFormat:@"CAST(token, \"NSString\")"],
|
||||
@"Setting text to a constant string with tokens should convert to an expression.");
|
||||
}
|
||||
|
||||
// text-allow-overlap
|
||||
|
||||
Reference in New Issue
Block a user