#include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace conversion { using namespace mbgl::style::expression; static bool isExpression(const Convertible& filter); ParseResult convertLegacyFilter(const Convertible& values, Error& error); optional serializeLegacyFilter(const Convertible& values); optional Converter::operator()(const Convertible& value, Error& error) const { if (isExpression(value)) { ParsingContext parsingContext(type::Boolean); ParseResult parseResult = parsingContext.parseExpression(value); if (!parseResult) { error.message = parsingContext.getCombinedErrors(); return nullopt; } else { return { Filter(std::move(parseResult)) }; } } else { ParseResult expression = convertLegacyFilter(value, error); if (!expression) { assert(error.message.size() > 0); return nullopt; } return Filter(optional>(std::move(*expression)), serializeLegacyFilter(value)); } } // This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js bool isExpression(const Convertible& filter) { if (!isArray(filter) || arrayLength(filter) == 0) { return false; } optional op = toString(arrayMember(filter, 0)); if (!op) { return false; } else if (*op == "has") { if (arrayLength(filter) < 2) return false; optional operand = toString(arrayMember(filter, 1)); return operand && *operand != "$id" && *operand != "$type"; } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") { return false; } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") { return arrayLength(filter) != 3 || isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2)); } else if (*op == "any" || *op == "all") { for (std::size_t i = 1; i < arrayLength(filter); i++) { Convertible f = arrayMember(filter, i); if (!isExpression(f) && !toBool(f)) { return false; } } return true; } else { return true; } } ParseResult createExpression(std::string op, optional>> args, Error& error) { if (!args) return {}; assert(std::all_of(args->begin(), args->end(), [](const std::unique_ptr &e) { return bool(e.get()); })); if (op == "any") { return {std::make_unique(std::move(*args))}; } else if (op == "all") { return {std::make_unique(std::move(*args))}; } else { ParsingContext parsingContext(type::Boolean); ParseResult parseResult = createCompoundExpression(op, std::move(*args), parsingContext); if (!parseResult) { error.message = parsingContext.getCombinedErrors(); return {}; } else { return parseResult; } } } ParseResult createExpression(std::string op, ParseResult arg, Error& error) { if (!arg) { return {}; } std::vector> args; args.push_back(std::move(*arg)); return createExpression(op, std::move(args), error); } ParseResult convertLiteral(const Convertible& convertible, Error& error) { ParsingContext parsingContext; ParseResult parseResult = Literal::parse(convertible, parsingContext); if (parseResult) { return parseResult; } else { error.message = parsingContext.getCombinedErrors(); return {}; } } optional>> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { std::vector> output; output.reserve(arrayLength(input)); for (std::size_t i = startIndex; i < arrayLength(input); ++i) { ParseResult literal = convertLiteral(arrayMember(input, i), error); if (!literal) { return nullopt; } output.push_back(std::move(*literal)); } return {std::move(output)}; } ParseResult convertLegacyComparisonFilter(const Convertible& values, Error& error, optional opOverride = {}) { optional op = opOverride ? opOverride : toString(arrayMember(values, 0)); optional property = toString(arrayMember(values, 1)); if (!property) { error.message = "filter property must be a string"; return {}; } else if (*property == "$type") { return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error); } else if (*property == "$id") { return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error); } else { return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error); } } ParseResult convertLegacyHasFilter(const Convertible& values, Error& error) { optional property = toString(arrayMember(values, 1)); if (!property) { error.message = "filter property must be a string"; return {}; } else if (*property == "$type") { return {std::make_unique(true)}; } else if (*property == "$id") { return createExpression("filter-has-id", std::vector>(), error); } else { return createExpression("filter-has", {std::make_unique(*property)}, error); } } ParseResult convertLegacyInFilter(const Convertible& values, Error& error) { optional property = toString(arrayMember(values, 1)); if (!property) { error.message = "filter property must be a string"; return {}; } else if (arrayLength(values) == 0) { return {std::make_unique(false)}; } else if (*property == "$type") { return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error); } else if (*property == "$id") { return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error); } else { return createExpression("filter-in", convertLiteralArray(values, error, 1), error); } } optional>> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { std::vector> output; output.reserve(arrayLength(input)); for (std::size_t i = startIndex; i < arrayLength(input); ++i) { optional> child = convertLegacyFilter(arrayMember(input, i), error); if (!child) { return nullopt; } output.push_back(std::move(*child)); } return {std::move(output)}; } ParseResult convertLegacyFilter(const Convertible& values, Error& error) { if (isUndefined(values)) { return {std::make_unique(true)}; } optional op = toString(arrayMember(values, 0)); if (!op) { error.message = "filter operator must be a string"; return {}; } else if (arrayLength(values) <= 1) { return {std::make_unique(*op != "any")}; } else { return { *op == "==" || *op == "<" || *op == ">" || *op == "<=" || *op == ">=" ? convertLegacyComparisonFilter(values, error) : *op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) : *op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) : *op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) : *op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) : *op == "in" ? convertLegacyInFilter(values, error) : *op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) : *op == "has" ? convertLegacyHasFilter(values, error) : *op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) : ParseResult(std::make_unique(true)) }; } } optional serializeLegacyFilter(const Convertible& values) { if (isUndefined(values)) { return nullopt; } else if (isArray(values)) { std::vector result; result.reserve(arrayLength(values)); for (std::size_t i = 0; i < arrayLength(values); ++i) { auto arrayValue = serializeLegacyFilter(arrayMember(values, i)); if (arrayValue) { result.push_back(*arrayValue); } else { result.emplace_back(NullValue()); } } return (mbgl::Value)result; } return toValue(values); } } // namespace conversion } // namespace style } // namespace mbgl