1 #include <mbgl/style/conversion/filter.hpp>
2 #include <mbgl/style/expression/literal.hpp>
3 #include <mbgl/util/geometry.hpp>
4 #include <mbgl/style/expression/expression.hpp>
5 #include <mbgl/style/expression/type.hpp>
6 #include <mbgl/style/expression/compound_expression.hpp>
7 #include <mbgl/style/expression/boolean_operator.hpp>
8
9 namespace mbgl {
10 namespace style {
11 namespace conversion {
12
13 using namespace mbgl::style::expression;
14
15 static bool isExpression(const Convertible& filter);
16 ParseResult convertLegacyFilter(const Convertible& values, Error& error);
17 optional<mbgl::Value> serializeLegacyFilter(const Convertible& values);
18
operator ()(const Convertible & value,Error & error) const19 optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
20 if (isExpression(value)) {
21 ParsingContext parsingContext(type::Boolean);
22 ParseResult parseResult = parsingContext.parseExpression(value);
23 if (!parseResult) {
24 error = { parsingContext.getCombinedErrors() };
25 return {};
26 } else {
27 return { Filter(std::move(parseResult)) };
28 }
29 } else {
30 ParseResult expression = convertLegacyFilter(value, error);
31 if (!expression) {
32 assert(error.message.size() > 0);
33 return {};
34 }
35 return Filter(optional<std::unique_ptr<Expression>>(std::move(*expression)), serializeLegacyFilter(value));
36 }
37 }
38
39 // This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js
isExpression(const Convertible & filter)40 bool isExpression(const Convertible& filter) {
41 if (!isArray(filter) || arrayLength(filter) == 0) {
42 return false;
43 }
44
45 optional<std::string> op = toString(arrayMember(filter, 0));
46
47 if (!op) {
48 return false;
49
50 } else if (*op == "has") {
51 if (arrayLength(filter) < 2) return false;
52 optional<std::string> operand = toString(arrayMember(filter, 1));
53 return operand && *operand != "$id" && *operand != "$type";
54
55 } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") {
56 return false;
57
58 } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") {
59 return arrayLength(filter) != 3 || isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2));
60
61 } else if (*op == "any" || *op == "all") {
62 for (std::size_t i = 1; i < arrayLength(filter); i++) {
63 Convertible f = arrayMember(filter, i);
64 if (!isExpression(f) && !toBool(f)) {
65 return false;
66 }
67 }
68 return true;
69
70 } else {
71 return true;
72 }
73 }
74
createExpression(std::string op,optional<std::vector<std::unique_ptr<Expression>>> args,Error & error)75 ParseResult createExpression(std::string op, optional<std::vector<std::unique_ptr<Expression>>> args, Error& error) {
76 if (!args) return {};
77 assert(std::all_of(args->begin(), args->end(), [](const std::unique_ptr<Expression> &e) {
78 return bool(e.get());
79 }));
80
81 if (op == "any") {
82 return {std::make_unique<Any>(std::move(*args))};
83 } else if (op == "all") {
84 return {std::make_unique<All>(std::move(*args))};
85 } else {
86 ParsingContext parsingContext(type::Boolean);
87 ParseResult parseResult = createCompoundExpression(op, std::move(*args), parsingContext);
88 if (!parseResult) {
89 error = { parsingContext.getCombinedErrors() };
90 return {};
91 } else {
92 return parseResult;
93 }
94 }
95 }
96
createExpression(std::string op,ParseResult arg,Error & error)97 ParseResult createExpression(std::string op, ParseResult arg, Error& error) {
98 if (!arg) {
99 return {};
100 }
101
102 std::vector<std::unique_ptr<Expression>> args;
103 args.push_back(std::move(*arg));
104 return createExpression(op, std::move(args), error);
105 }
106
convertLiteral(const Convertible & convertible,Error & error)107 ParseResult convertLiteral(const Convertible& convertible, Error& error) {
108 ParsingContext parsingContext;
109 ParseResult parseResult = Literal::parse(convertible, parsingContext);
110 if (parseResult) {
111 return parseResult;
112 } else {
113 error = { parsingContext.getCombinedErrors() };
114 return {};
115 }
116 }
117
convertLiteralArray(const Convertible & input,Error & error,std::size_t startIndex=0)118 optional<std::vector<std::unique_ptr<Expression>>> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
119 std::vector<std::unique_ptr<Expression>> output;
120 for (std::size_t i = startIndex; i < arrayLength(input); i++) {
121 ParseResult literal = convertLiteral(arrayMember(input, i), error);
122 if (!literal) {
123 return {};
124 }
125 output.push_back(std::move(*literal));
126 }
127 return {std::move(output)};
128 }
129
convertLegacyComparisonFilter(const Convertible & values,Error & error,optional<std::string> opOverride={})130 ParseResult convertLegacyComparisonFilter(const Convertible& values, Error& error, optional<std::string> opOverride = {}) {
131 optional<std::string> op = opOverride ? opOverride : toString(arrayMember(values, 0));
132 optional<std::string> property = toString(arrayMember(values, 1));
133
134 if (!property) {
135 error = { "filter property must be a string" };
136 return {};
137 } else if (*property == "$type") {
138 return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error);
139 } else if (*property == "$id") {
140 return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error);
141 } else {
142 return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error);
143 }
144 }
145
convertLegacyHasFilter(const Convertible & values,Error & error)146 ParseResult convertLegacyHasFilter(const Convertible& values, Error& error) {
147 optional<std::string> property = toString(arrayMember(values, 1));
148
149 if (!property) {
150 error = { "filter property must be a string" };
151 return {};
152 } else if (*property == "$type") {
153 return {std::make_unique<Literal>(true)};
154 } else if (*property == "$id") {
155 return createExpression("filter-has-id", std::vector<std::unique_ptr<Expression>>(), error);
156 } else {
157 return createExpression("filter-has", {std::make_unique<Literal>(*property)}, error);
158 }
159 }
160
convertLegacyInFilter(const Convertible & values,Error & error)161 ParseResult convertLegacyInFilter(const Convertible& values, Error& error) {
162 optional<std::string> property = toString(arrayMember(values, 1));
163
164 if (!property) {
165 error = { "filter property must be a string" };
166 return {};
167 } else if (arrayLength(values) == 0) {
168 return {std::make_unique<Literal>(false)};
169 } else if (*property == "$type") {
170 return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error);
171 } else if (*property == "$id") {
172 return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error);
173 } else {
174 return createExpression("filter-in", convertLiteralArray(values, error, 1), error);
175 }
176 }
177
convertLegacyFilterArray(const Convertible & input,Error & error,std::size_t startIndex=0)178 optional<std::vector<std::unique_ptr<Expression>>> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
179 std::vector<std::unique_ptr<Expression>> output;
180 for (std::size_t i = startIndex; i < arrayLength(input); i++) {
181 optional<std::unique_ptr<Expression>> child = convertLegacyFilter(arrayMember(input, i), error);
182 if (!child) {
183 return {};
184 }
185 output.push_back(std::move(*child));
186 }
187 return {std::move(output)};
188 }
189
convertLegacyFilter(const Convertible & values,Error & error)190 ParseResult convertLegacyFilter(const Convertible& values, Error& error) {
191 if (isUndefined(values)) {
192 return {std::make_unique<Literal>(true)};
193 }
194
195 optional<std::string> op = toString(arrayMember(values, 0));
196
197 if (!op) {
198 error = { "filter operator must be a string" };
199 return {};
200 } else if (arrayLength(values) <= 1) {
201 return {std::make_unique<Literal>(*op != "any")};
202 } else {
203 return {
204 *op == "==" ||
205 *op == "<" ||
206 *op == ">" ||
207 *op == "<=" ||
208 *op == ">=" ? convertLegacyComparisonFilter(values, error) :
209 *op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) :
210 *op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) :
211 *op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) :
212 *op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) :
213 *op == "in" ? convertLegacyInFilter(values, error) :
214 *op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) :
215 *op == "has" ? convertLegacyHasFilter(values, error) :
216 *op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) :
217 ParseResult(std::make_unique<Literal>(true))
218 };
219 }
220 }
221
serializeLegacyFilter(const Convertible & values)222 optional<mbgl::Value> serializeLegacyFilter(const Convertible& values) {
223 if (isUndefined(values)) {
224 return {};
225 } else if (isArray(values)) {
226 std::vector<mbgl::Value> result;
227 for (std::size_t i = 0; i < arrayLength(values); i++) {
228 auto arrayValue = serializeLegacyFilter(arrayMember(values, i));
229 if (arrayValue) {
230 result.push_back(*arrayValue);
231 } else {
232 result.push_back(NullValue());
233 }
234 }
235 return (mbgl::Value)result;
236 }
237 return toValue(values);
238 }
239
240 } // namespace conversion
241 } // namespace style
242 } // namespace mbgl
243