summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2018-07-05 15:08:24 -0700
committerMinh Nguyễn <mxn@1ec5.org>2018-07-09 16:02:40 -0700
commitf11ab91fc448aca1155b42a53aaa77cfce62f412 (patch)
treea463898359e31efa6852dde646ff88f7b575399a
parentd6a454ba8eb4546a52d941c63bf0331353873ad6 (diff)
downloadqtlocation-mapboxgl-f11ab91fc448aca1155b42a53aaa77cfce62f412.tar.gz
[ios, macos] Convert predicate options to collators
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs1
-rw-r--r--platform/darwin/docs/guides/Predicates and Expressions.md14
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm14
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm9
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm60
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm73
-rw-r--r--platform/ios/CHANGELOG.md2
-rw-r--r--platform/ios/docs/guides/For Style Authors.md1
-rw-r--r--platform/macos/CHANGELOG.md2
-rw-r--r--platform/macos/docs/guides/For Style Authors.md1
10 files changed, 149 insertions, 28 deletions
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
index 60177e57c2..b8112d6fec 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -331,6 +331,7 @@ In style specification | Method, function, or predicate type | Format string syn
-----------------------|-------------------------------------|---------------------
`array` | |
`boolean` | |
+`collator` | `NSComparisonPredicateOptions` | `'Québec' =[cd] 'QUEBEC'`
`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
`number` | |
`string` | |
diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md
index e0d4755d4a..4ef9ad6707 100644
--- a/platform/darwin/docs/guides/Predicates and Expressions.md
+++ b/platform/darwin/docs/guides/Predicates and Expressions.md
@@ -53,9 +53,17 @@ The following aggregate operators are supported:
`NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }`
`NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key`
-Operator modifiers such as `c` (for case insensitivity), `d` (for diacritic
-insensitivity), and `l` (for locale sensitivity) are unsupported for comparison
-and aggregate operators that are used in the predicate.
+The following comparison predicate options are supported for comparison and
+aggregate operators that are used in the predicate:
+
+`NSComparisonPredicateOptions` | Format string syntax
+----------------------------------------|---------------------
+`NSCaseInsensitivePredicateOption` | `'QUEBEC' =[c] 'Quebec'`
+`NSDiacriticInsensitivePredicateOption` | `'Québec' =[d] 'Quebec'`
+
+Other comparison predicate options are unsupported, namely `l`
+(for locale sensitivity) and `n` (for normalization). A comparison is
+locale-sensitive as long as it is case- or diacritic-insensitive.
### Operands
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index 15aa71419d..af9216f9ce 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -140,10 +140,18 @@
[NSException raise:NSInvalidArgumentException
format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
}
- if (op) {
- return @[op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject];
+ if (!op) {
+ return nil;
}
- return nil;
+ NSArray *comparisonArray = @[op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject];
+ if (self.options) {
+ NSDictionary *collatorObject = @{
+ @"case-sensitive": @(!(self.options & NSCaseInsensitivePredicateOption)),
+ @"diacritic-sensitive": @(!(self.options & NSDiacriticInsensitivePredicateOption)),
+ };
+ return [comparisonArray arrayByAddingObject:@[@"collator", collatorObject]];
+ }
+ return comparisonArray;
}
@end
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index 653e3d67e6..d03d7dbaec 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -784,6 +784,9 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
return [NSExpression expressionForFunction:functionName
arguments:subexpressions];
+ } else if ([op isEqualToString:@"collator"]) {
+ // Avoid wrapping collator options object in literal expression.
+ return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:array];
} else if ([op isEqualToString:@"literal"]) {
if ([argumentObjects.firstObject isKindOfClass:[NSArray class]]) {
return [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects.firstObject)];
@@ -1208,6 +1211,12 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
[NSException raise:NSInvalidArgumentException
format:@"Casting expression to %@ not yet implemented.", type];
} else if ([function isEqualToString:@"MGL_FUNCTION"]) {
+ NSExpression *op = self.arguments.firstObject;
+ if (op.expressionType == NSConstantValueExpressionType
+ && [op.constantValue isEqualToString:@"collator"]) {
+ // Avoid wrapping collator options object in literal expression.
+ return @[@"collator", self.arguments[1].constantValue];
+ }
return self.arguments.mgl_jsonExpressionObject;
} else if (op == [MGLColor class] && [function isEqualToString:@"colorWithRed:green:blue:alpha:"]) {
NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index 4b9a4177cb..d01b2c8f83 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -46,6 +46,15 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
return subpredicates;
}
+static NSDictionary * const MGLPredicateOperatorTypesByJSONOperator = @{
+ @"==": @(NSEqualToPredicateOperatorType),
+ @"!=": @(NSNotEqualToPredicateOperatorType),
+ @"<": @(NSLessThanPredicateOperatorType),
+ @"<=": @(NSLessThanOrEqualToPredicateOperatorType),
+ @">": @(NSGreaterThanPredicateOperatorType),
+ @">=": @(NSGreaterThanOrEqualToPredicateOperatorType),
+};
+
+ (instancetype)predicateWithMGLJSONObject:(id)object {
if ([object isEqual:@YES]) {
return [NSPredicate predicateWithValue:YES];
@@ -58,30 +67,37 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
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:@">="]) {
+ NSNumber *operatorTypeNumber = MGLPredicateOperatorTypesByJSONOperator[op];
+ if (operatorTypeNumber) {
+ NSPredicateOperatorType operatorType = (NSPredicateOperatorType)[operatorTypeNumber unsignedIntegerValue];
+
+ NSComparisonPredicateOptions options = 0;
+ if (objects.count > 3) {
+ NSArray *collatorExpression = objects[3];
+ NSCAssert([collatorExpression isKindOfClass:[NSArray class]], @"Collators must be dictionaries.");
+ NSCAssert(collatorExpression.count == 2, @"Malformed collator expression");
+ NSDictionary *collator = collatorExpression[1];
+ NSCAssert([collator isKindOfClass:[NSDictionary class]], @"Malformed collator in collator expression");
+
+ // Predicate options can’t express specific locales as collators can.
+ if (!collator[@"locale"]) {
+ if ([(collator[@"case-sensitive"] ?: @YES) isEqual:@NO]) {
+ options |= NSCaseInsensitivePredicateOption;
+ }
+ if ([(collator[@"diacritic-sensitive"] ?: @YES) isEqual:@NO]) {
+ options |= NSDiacriticInsensitivePredicateOption;
+ }
+ }
+ }
+
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
- return [NSPredicate predicateWithFormat:@"%@ >= %@" argumentArray:subexpressions];
+ return [NSComparisonPredicate predicateWithLeftExpression:subexpressions[0]
+ rightExpression:subexpressions[1]
+ modifier:NSDirectPredicateModifier
+ type:operatorType
+ options:options];
}
+
if ([op isEqualToString:@"!"]) {
NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
if (subpredicates.count > 1) {
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
index 41c2d56868..ed92c920f6 100644
--- a/platform/darwin/test/MGLPredicateTests.mm
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -280,6 +280,79 @@
}
}
+- (void)testComparisonPredicatesWithOptions {
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[c] 'b'"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @YES}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[d] 'b'"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @YES, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a =[cd] 'b'"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a !=[cd] 'b'"];
+ NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <[cd] 'b'"];
+ NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <=[cd] 'b'"];
+ NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >[cd] 'b'"];
+ NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >=[cd] 'b'"];
+ NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b", @[@"collator", @{@"case-sensitive": @NO, @"diacritic-sensitive": @NO}]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"TRUE = MGL_FUNCTION('==', a, 'b', MGL_FUNCTION('collator', %@))", @{
+ @"case-sensitive": @NO,
+ @"diacritic-sensitive": @NO,
+ @"locale": @"tlh",
+ }];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b",
+ @[@"collator",
+ @{@"case-sensitive": @NO,
+ @"diacritic-sensitive": @NO,
+ @"locale": @"tlh"}]];
+ XCTAssertEqualObjects([predicate.mgl_jsonExpressionObject lastObject], jsonExpression);
+ }
+}
+
- (void)testCompoundPredicates {
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 0f6aba3cfd..115a608918 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -12,6 +12,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Added an `MGLRasterStyleLayer.rasterResamplingMode` property for configuring how raster style layers are overscaled. ([#12176](https://github.com/mapbox/mapbox-gl-native/pull/12176))
* `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` can automatically localize labels into Japanese or Korean based on the system’s language settings. ([#12286](https://github.com/mapbox/mapbox-gl-native/pull/12286))
+* The `c` and `d` options are supported within comparison predicates for case and diacritic insensitivity, respectively. ([#12329](https://github.com/mapbox/mapbox-gl-native/pull/12329))
+* Added the `collator` and `resolved-locale` expression operators to more precisely compare strings in style JSON. A subset of this functionality is available through predicate options when creating an `NSPredicate`. ([#11869](https://github.com/mapbox/mapbox-gl-native/pull/11869))
* Fixed a crash when trying to parse expressions containing legacy filters. ([#12263](https://github.com/mapbox/mapbox-gl-native/pull/12263))
### Networking and storage
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index e3e2eca603..24a2f7d3a4 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -322,6 +322,7 @@ In style specification | Method, function, or predicate type | Format string syn
-----------------------|-------------------------------------|---------------------
`array` | |
`boolean` | |
+`collator` | `NSComparisonPredicateOptions` | `'Québec' =[cd] 'QUEBEC'`
`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
`number` | |
`string` | |
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 0dff689010..1d5eab97fb 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -6,6 +6,8 @@
* Added an `MGLRasterStyleLayer.rasterResamplingMode` property for configuring how raster style layers are overscaled. ([#12176](https://github.com/mapbox/mapbox-gl-native/pull/12176))
* `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` can automatically localize labels into Japanese or Korean based on the system’s language settings. ([#12286](https://github.com/mapbox/mapbox-gl-native/pull/12286))
+* The `c` and `d` options are supported within comparison predicates for case and diacritic insensitivity, respectively. ([#12329](https://github.com/mapbox/mapbox-gl-native/pull/12329))
+* Added the `collator` and `resolved-locale` expression operators to more precisely compare strings in style JSON. A subset of this functionality is available through predicate options when creating an `NSPredicate`. ([#11869](https://github.com/mapbox/mapbox-gl-native/pull/11869))
* Fixed a crash in `-[MGLStyle localizeLabelsIntoLocale:]` on macOS 10.11. ([#12123](https://github.com/mapbox/mapbox-gl-native/pull/12123))
* Fixed a crash when trying to parse expressions containing legacy filters. ([#12263](https://github.com/mapbox/mapbox-gl-native/pull/12263))
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index e2d63a6506..40d1edef22 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -315,6 +315,7 @@ In style specification | Method, function, or predicate type | Format string syn
-----------------------|-------------------------------------|---------------------
`array` | |
`boolean` | |
+`collator` | `NSComparisonPredicateOptions` | `'Québec' =[cd] 'QUEBEC'`
`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary`
`number` | |
`string` | |