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