[core] Add number-format expression
This commit is contained in:
@@ -19,13 +19,18 @@ target_link_libraries(mbgl-core PRIVATE
|
||||
wagyu
|
||||
)
|
||||
|
||||
# linux uses ICU from mason, other platforms use vendored ICU
|
||||
if(NOT MBGL_PLATFORM STREQUAL "linux")
|
||||
set(ICU_LIBRARY "icu")
|
||||
endif()
|
||||
|
||||
# FIXME: We should not leak these many
|
||||
# libraries in our public interface.
|
||||
target_link_libraries(mbgl-core PUBLIC
|
||||
boost
|
||||
geojson.hpp
|
||||
geometry.hpp
|
||||
icu
|
||||
${ICU_LIBRARY}
|
||||
optional
|
||||
polylabel
|
||||
protozero
|
||||
|
||||
@@ -8,6 +8,7 @@ elseif(MBGL_PLATFORM STREQUAL "linux")
|
||||
mason_use(libuv VERSION 1.9.1)
|
||||
mason_use(libpng VERSION 1.6.25)
|
||||
mason_use(libjpeg-turbo VERSION 1.5.0)
|
||||
mason_use(icu VERSION 63.1-min-static-data)
|
||||
|
||||
if(WITH_EGL)
|
||||
mason_use(swiftshader VERSION 2018-05-31)
|
||||
|
||||
@@ -142,7 +142,8 @@ enum class Kind : int32_t {
|
||||
All,
|
||||
Comparison,
|
||||
FormatExpression,
|
||||
FormatSectionOverride
|
||||
FormatSectionOverride,
|
||||
NumberFormat
|
||||
};
|
||||
|
||||
class Expression {
|
||||
|
||||
40
include/mbgl/style/expression/number_format.hpp
Normal file
40
include/mbgl/style/expression/number_format.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/expression/expression.hpp>
|
||||
#include <mbgl/style/expression/parsing_context.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace expression {
|
||||
|
||||
class NumberFormat final : public Expression {
|
||||
public:
|
||||
NumberFormat(std::unique_ptr<Expression> number_,
|
||||
std::unique_ptr<Expression> locale_,
|
||||
std::unique_ptr<Expression> currency_,
|
||||
std::unique_ptr<Expression> minFractionDigits_,
|
||||
std::unique_ptr<Expression> maxFractionDigits_);
|
||||
|
||||
~NumberFormat();
|
||||
|
||||
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
|
||||
|
||||
EvaluationResult evaluate(const EvaluationContext& params) const override;
|
||||
void eachChild(const std::function<void(const Expression&)>& visit) const override;
|
||||
bool operator==(const Expression& e) const override;
|
||||
std::vector<optional<Value>> possibleOutputs() const override;
|
||||
|
||||
mbgl::Value serialize() const override;
|
||||
std::string getOperator() const override { return "number-format"; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Expression> number;
|
||||
std::unique_ptr<Expression> locale;
|
||||
std::unique_ptr<Expression> currency;
|
||||
std::unique_ptr<Expression> minFractionDigits;
|
||||
std::unique_ptr<Expression> maxFractionDigits;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -16,6 +16,9 @@ std::string lowercase(const std::string &string);
|
||||
// Gets the name of the current thread.
|
||||
std::string getCurrentThreadName();
|
||||
|
||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||
uint8_t minFractionDigits, uint8_t maxFractionDigits);
|
||||
|
||||
// Set the name of the current thread, truncated at 15.
|
||||
void setCurrentThreadName(const std::string& name);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"platform/android/src/style/value.cpp",
|
||||
"platform/android/src/text/collator.cpp",
|
||||
"platform/android/src/text/local_glyph_rasterizer.cpp",
|
||||
"platform/android/src/text/format_number.cpp",
|
||||
"platform/android/src/gl_functions.cpp",
|
||||
"platform/android/src/thread.cpp",
|
||||
"platform/android/src/timer.cpp",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#endif
|
||||
#include "text/collator_jni.hpp"
|
||||
#include "text/local_glyph_rasterizer_jni.hpp"
|
||||
#include "text/format_number_jni.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
namespace mbgl {
|
||||
@@ -200,6 +201,7 @@ void registerNatives(JavaVM *vm) {
|
||||
Locale::registerNative(env);
|
||||
Collator::registerNative(env);
|
||||
StringUtils::registerNative(env);
|
||||
NumberFormat::registerNative(env);
|
||||
|
||||
// Logger
|
||||
Logger::registerNative(env);
|
||||
|
||||
81
platform/android/src/text/format_number.cpp
Normal file
81
platform/android/src/text/format_number.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <mbgl/style/expression/collator.hpp>
|
||||
#include <mbgl/text/language_tag.hpp>
|
||||
#include <mbgl/util/platform.hpp>
|
||||
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
#include "../attach_env.hpp"
|
||||
#include "format_number_jni.hpp"
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
void NumberFormat::registerNative(jni::JNIEnv& env) {
|
||||
jni::Class<NumberFormat>::Singleton(env);
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<NumberFormat>> NumberFormat::getInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
|
||||
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
|
||||
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat> (jni::Object<Locale>)>(env, "getInstance");
|
||||
return javaClass.Call(env, method, locale);
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<NumberFormat>> NumberFormat::getCurrencyInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
|
||||
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
|
||||
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat> (jni::Object<Locale>)>(env, "getCurrencyInstance");
|
||||
return javaClass.Call(env, method, locale);
|
||||
}
|
||||
|
||||
jni::Local<jni::String> NumberFormat::format(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jdouble number) {
|
||||
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<jni::String (jni::jdouble)>(env, "format");
|
||||
return nf.Call(env, method, number);
|
||||
}
|
||||
|
||||
void NumberFormat::setMinimumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
|
||||
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::jint)>(env, "setMinimumFractionDigits");
|
||||
return nf.Call(env, method, value);
|
||||
}
|
||||
|
||||
void NumberFormat::setMaximumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
|
||||
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::jint)>(env, "setMaximumFractionDigits");
|
||||
return nf.Call(env, method, value);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
namespace platform {
|
||||
|
||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
||||
|
||||
auto env{ android::AttachEnv() };
|
||||
|
||||
jni::Global<jni::Object<android::Locale>> locale;
|
||||
LanguageTag languageTag = !localeId.empty() ? LanguageTag::fromBCP47(localeId) : LanguageTag();
|
||||
if (!languageTag.language) {
|
||||
locale = jni::NewGlobal(*env, android::Locale::getDefault(*env));
|
||||
} else if (!languageTag.region) {
|
||||
locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language)));
|
||||
} else {
|
||||
locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language),
|
||||
jni::Make<jni::String>(*env, *languageTag.region)));
|
||||
}
|
||||
|
||||
jni::Global<jni::Object<android::NumberFormat>> formatter;
|
||||
if (currency.empty()) {
|
||||
formatter = jni::NewGlobal(*env, android::NumberFormat::getInstance(*env, locale));
|
||||
android::NumberFormat::setMinimumFractionDigits(*env, formatter, static_cast<jni::jint>(minFractionDigits));
|
||||
android::NumberFormat::setMaximumFractionDigits(*env, formatter, static_cast<jni::jint>(maxFractionDigits));
|
||||
} else {
|
||||
formatter = jni::NewGlobal(*env, android::NumberFormat::getCurrencyInstance(*env, locale));
|
||||
}
|
||||
|
||||
auto result = android::NumberFormat::format(*env, formatter, static_cast<jni::jdouble>(number));
|
||||
return jni::Make<std::string>(*env, result);
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace mbgl
|
||||
29
platform/android/src/text/format_number_jni.hpp
Normal file
29
platform/android/src/text/format_number_jni.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
#include "collator_jni.hpp"
|
||||
|
||||
/*
|
||||
android::NumberFormat is the JNI wrapper
|
||||
of java/text/NumberFormat.
|
||||
*/
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class NumberFormat {
|
||||
public:
|
||||
static constexpr auto Name() { return "java/text/NumberFormat"; };
|
||||
|
||||
static jni::Local<jni::Object<NumberFormat>> getInstance(jni::JNIEnv&, const jni::Object<Locale>&);
|
||||
static jni::Local<jni::Object<NumberFormat>> getCurrencyInstance(jni::JNIEnv&, const jni::Object<Locale>&);
|
||||
static jni::Local<jni::String> format(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jdouble);
|
||||
static void setMinimumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
|
||||
static void setMaximumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -27,5 +27,28 @@ std::string lowercase(const std::string &string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
||||
|
||||
static NSNumberFormatter *numberFormatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
});
|
||||
|
||||
numberFormatter.locale = !localeId.empty() ? [NSLocale localeWithLocaleIdentifier:@(localeId.c_str())] : nil;
|
||||
numberFormatter.currencyCode = !currency.empty() ? @(currency.c_str()) : nil;
|
||||
if (currency.empty()) {
|
||||
numberFormatter.minimumFractionDigits = minFractionDigits;
|
||||
numberFormatter.maximumFractionDigits = maxFractionDigits;
|
||||
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
} else {
|
||||
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
}
|
||||
NSString *formatted = [numberFormatter stringFromNumber:@(number)];
|
||||
std::string result = std::string([formatted UTF8String]);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
35
platform/default/src/mbgl/util/format_number.cpp
Normal file
35
platform/default/src/mbgl/util/format_number.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <mbgl/util/platform.hpp>
|
||||
|
||||
#include <unicode/numberformatter.h>
|
||||
|
||||
namespace mbgl {
|
||||
namespace platform {
|
||||
|
||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString ustr;
|
||||
std::string formatted;
|
||||
|
||||
icu::Locale locale = icu::Locale(localeId.c_str());
|
||||
// Print the value as currency
|
||||
if (!currency.empty()) {
|
||||
icu::UnicodeString ucurrency = icu::UnicodeString::fromUTF8(currency);
|
||||
ustr = icu::number::NumberFormatter::with()
|
||||
.unit(icu::CurrencyUnit(ucurrency.getBuffer(), status))
|
||||
.locale(locale)
|
||||
.formatDouble(number, status)
|
||||
.toString();
|
||||
} else {
|
||||
ustr = icu::number::NumberFormatter::with()
|
||||
.precision(icu::number::Precision::minMaxFraction(minFractionDigits, maxFractionDigits))
|
||||
.locale(locale)
|
||||
.formatDouble(number, status)
|
||||
.toString();
|
||||
}
|
||||
return ustr.toUTF8String(formatted);
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace mbgl
|
||||
@@ -56,6 +56,7 @@ macro(mbgl_platform_core)
|
||||
PRIVATE platform/default/src/mbgl/text/unaccent.cpp
|
||||
PRIVATE platform/default/include/mbgl/text/unaccent.hpp
|
||||
PRIVATE platform/default/src/mbgl/util/utf.cpp
|
||||
PRIVATE platform/default/src/mbgl/util/format_number.cpp
|
||||
|
||||
# Image handling
|
||||
PRIVATE platform/default/src/mbgl/util/image.cpp
|
||||
@@ -83,10 +84,21 @@ macro(mbgl_platform_core)
|
||||
|
||||
target_add_mason_package(mbgl-core PUBLIC libpng)
|
||||
target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo)
|
||||
target_add_mason_package(mbgl-core PRIVATE icu)
|
||||
|
||||
# Ignore warning caused by ICU header unistr.h in some CI environments
|
||||
set_source_files_properties(platform/default/src/mbgl/util/format_number.cpp PROPERTIES COMPILE_FLAGS -Wno-error=shadow)
|
||||
|
||||
# Link all ICU libraries (by default only libicuuc is linked)
|
||||
find_library(LIBICUI18N NAMES icui18n HINTS ${MASON_PACKAGE_icu_INCLUDE_DIRS}/../lib)
|
||||
find_library(LIBICUUC NAMES icuuc HINTS ${MASON_PACKAGE_icu_INCLUDE_DIRS}/../lib)
|
||||
find_library(LIBICUDATA NAMES icudata HINTS ${MASON_PACKAGE_icu_INCLUDE_DIRS}/../lib)
|
||||
|
||||
target_link_libraries(mbgl-core
|
||||
PRIVATE ${LIBICUI18N}
|
||||
PRIVATE ${LIBICUUC}
|
||||
PRIVATE ${LIBICUDATA}
|
||||
PRIVATE nunicode
|
||||
PRIVATE icu
|
||||
PUBLIC -lz
|
||||
)
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ type::Type parseType(v8::Local<v8::Object> type) {
|
||||
{"object", type::Object},
|
||||
{"color", type::Color},
|
||||
{"value", type::Value},
|
||||
{"formatted", type::Formatted}
|
||||
{"formatted", type::Formatted},
|
||||
{"number-format", type::String}
|
||||
};
|
||||
|
||||
v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked();
|
||||
|
||||
@@ -35,6 +35,7 @@ set(MBGL_QT_CORE_FILES
|
||||
PRIVATE platform/qt/src/timer_impl.hpp
|
||||
PRIVATE platform/qt/src/utf.cpp
|
||||
PRIVATE platform/qt/src/gl_functions.cpp
|
||||
PRIVATE platform/qt/src/format_number.cpp
|
||||
|
||||
PRIVATE platform/default/src/mbgl/text/collator.cpp
|
||||
PRIVATE platform/default/src/mbgl/text/unaccent.cpp
|
||||
|
||||
26
platform/qt/src/format_number.cpp
Normal file
26
platform/qt/src/format_number.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <mbgl/util/platform.hpp>
|
||||
|
||||
#include <QLocale>
|
||||
#include <QString>
|
||||
|
||||
namespace mbgl {
|
||||
namespace platform {
|
||||
|
||||
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
|
||||
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
|
||||
|
||||
QString formatted;
|
||||
// Qt Locale::toString() API takes only one precision argument
|
||||
(void)minFractionDigits;
|
||||
QLocale locale = QLocale(QString::fromStdString(localeId));
|
||||
|
||||
if (!currency.empty()) {
|
||||
formatted = locale.toCurrencyString(number);
|
||||
} else {
|
||||
formatted = locale.toString(number, 'f', maxFractionDigits);
|
||||
}
|
||||
return formatted.toStdString();
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace mbgl
|
||||
@@ -192,6 +192,7 @@
|
||||
"src/mbgl/style/expression/let.cpp",
|
||||
"src/mbgl/style/expression/literal.cpp",
|
||||
"src/mbgl/style/expression/match.cpp",
|
||||
"src/mbgl/style/expression/number_format.cpp",
|
||||
"src/mbgl/style/expression/parsing_context.cpp",
|
||||
"src/mbgl/style/expression/step.cpp",
|
||||
"src/mbgl/style/expression/util.cpp",
|
||||
@@ -416,6 +417,7 @@
|
||||
"mbgl/style/expression/let.hpp": "include/mbgl/style/expression/let.hpp",
|
||||
"mbgl/style/expression/literal.hpp": "include/mbgl/style/expression/literal.hpp",
|
||||
"mbgl/style/expression/match.hpp": "include/mbgl/style/expression/match.hpp",
|
||||
"mbgl/style/expression/number_format.hpp": "include/mbgl/style/expression/number_format.hpp",
|
||||
"mbgl/style/expression/parsing_context.hpp": "include/mbgl/style/expression/parsing_context.hpp",
|
||||
"mbgl/style/expression/step.hpp": "include/mbgl/style/expression/step.hpp",
|
||||
"mbgl/style/expression/type.hpp": "include/mbgl/style/expression/type.hpp",
|
||||
|
||||
215
src/mbgl/style/expression/number_format.cpp
Normal file
215
src/mbgl/style/expression/number_format.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include <mbgl/style/expression/number_format.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
#include <mbgl/util/platform.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace expression {
|
||||
|
||||
const char localeKey[] = "locale";
|
||||
const char currencyKey[] = "currency";
|
||||
const char minFractionDigitsKey[] = "min-fraction-digits";
|
||||
const char maxFractionDigitsKey[] = "max-fraction-digits";
|
||||
|
||||
NumberFormat::NumberFormat(std::unique_ptr<Expression> number_,
|
||||
std::unique_ptr<Expression> locale_,
|
||||
std::unique_ptr<Expression> currency_,
|
||||
std::unique_ptr<Expression> minFractionDigits_,
|
||||
std::unique_ptr<Expression> maxFractionDigits_)
|
||||
: Expression(Kind::NumberFormat, type::String),
|
||||
number(std::move(number_)),
|
||||
locale(std::move(locale_)),
|
||||
currency(std::move(currency_)),
|
||||
minFractionDigits(std::move(minFractionDigits_)),
|
||||
maxFractionDigits(std::move(maxFractionDigits_))
|
||||
{}
|
||||
|
||||
NumberFormat::~NumberFormat() = default;
|
||||
|
||||
EvaluationResult NumberFormat::evaluate(const EvaluationContext& params) const {
|
||||
auto numberResult = number->evaluate(params);
|
||||
if (!numberResult) {
|
||||
return numberResult.error();
|
||||
}
|
||||
double evaluatedNumber = numberResult->get<double>();
|
||||
|
||||
std::string evaluatedLocale;
|
||||
if (locale) {
|
||||
auto localeResult = locale->evaluate(params);
|
||||
if (!localeResult) {
|
||||
return localeResult.error();
|
||||
}
|
||||
evaluatedLocale = toString(*localeResult);
|
||||
}
|
||||
|
||||
std::string evaluatedCurrency;
|
||||
if (currency) {
|
||||
auto currencyResult = currency->evaluate(params);
|
||||
if (!currencyResult) {
|
||||
return currencyResult.error();
|
||||
}
|
||||
evaluatedCurrency = toString(*currencyResult);
|
||||
}
|
||||
|
||||
uint8_t evaluatedMinFractionDigits = 0;
|
||||
if (minFractionDigits) {
|
||||
auto minDigitsResult = minFractionDigits->evaluate(params);
|
||||
if (!minDigitsResult) {
|
||||
return minDigitsResult.error();
|
||||
}
|
||||
evaluatedMinFractionDigits = minDigitsResult->get<double>();
|
||||
}
|
||||
|
||||
uint8_t evaluatedMaxFractionDigits = 3;
|
||||
if (maxFractionDigits) {
|
||||
auto maxDigitsResult = maxFractionDigits->evaluate(params);
|
||||
if (!maxDigitsResult) {
|
||||
return maxDigitsResult.error();
|
||||
}
|
||||
evaluatedMaxFractionDigits = maxDigitsResult->get<double>();
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output = platform::formatNumber(evaluatedNumber,
|
||||
evaluatedLocale,
|
||||
evaluatedCurrency,
|
||||
evaluatedMinFractionDigits,
|
||||
evaluatedMaxFractionDigits);
|
||||
return output;
|
||||
}
|
||||
|
||||
void NumberFormat::eachChild(const std::function<void(const Expression&)>& visit) const {
|
||||
visit(*number);
|
||||
if (locale) visit(*locale);
|
||||
if (currency) visit(*currency);
|
||||
if (minFractionDigits) visit(*minFractionDigits);
|
||||
if (maxFractionDigits) visit(*maxFractionDigits);
|
||||
}
|
||||
|
||||
bool NumberFormat::operator==(const Expression& e) const {
|
||||
if (e.getKind() == Kind::NumberFormat) {
|
||||
auto rhs = static_cast<const NumberFormat*>(&e);
|
||||
if ((locale && (!rhs->locale || *locale != *rhs->locale)) ||
|
||||
(!locale && rhs->locale)) {
|
||||
return false;
|
||||
}
|
||||
if ((currency && (!rhs->currency || *currency != *rhs->currency)) ||
|
||||
(!currency && rhs->currency)) {
|
||||
return false;
|
||||
}
|
||||
if ((minFractionDigits && (!rhs->minFractionDigits || *minFractionDigits != *rhs->minFractionDigits)) ||
|
||||
(!minFractionDigits && rhs->minFractionDigits)) {
|
||||
return false;
|
||||
}
|
||||
if ((maxFractionDigits && (!rhs->maxFractionDigits || *maxFractionDigits != *rhs->maxFractionDigits)) ||
|
||||
(!maxFractionDigits && rhs->maxFractionDigits)) {
|
||||
return false;
|
||||
}
|
||||
return *number == *rhs->number;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<optional<Value>> NumberFormat::possibleOutputs() const {
|
||||
return { nullopt };
|
||||
}
|
||||
|
||||
using namespace mbgl::style::conversion;
|
||||
ParseResult NumberFormat::parse(const Convertible& value, ParsingContext& ctx) {
|
||||
std::size_t length = arrayLength(value);
|
||||
|
||||
if (length != 3) {
|
||||
ctx.error("Expected two arguments, but found " + util::toString(length) + " instead.");
|
||||
return ParseResult();
|
||||
}
|
||||
|
||||
ParseResult numberResult = ctx.parse(arrayMember(value, 1), 1, {type::Number});
|
||||
if (!numberResult) {
|
||||
ctx.error("Failed to parse the number.");
|
||||
return ParseResult();
|
||||
}
|
||||
|
||||
type::Type type = (*numberResult)->getType();
|
||||
if (!type.is<type::NumberType>()) {
|
||||
ctx.error("Expected argument of type number, but found " + toString(type) + " instead.");
|
||||
return ParseResult();
|
||||
}
|
||||
|
||||
auto options = arrayMember(value, 2);
|
||||
if (!isObject(options)) {
|
||||
ctx.error("Number-format options argument must be an object.");
|
||||
return ParseResult();
|
||||
}
|
||||
|
||||
const optional<Convertible> localeOption = objectMember(options, localeKey);
|
||||
ParseResult localeResult;
|
||||
if (localeOption) {
|
||||
localeResult = ctx.parse(*localeOption, 1, {type::String});
|
||||
if (!localeResult) {
|
||||
ctx.error("Number-format locale parsing failed.");
|
||||
return ParseResult();
|
||||
}
|
||||
}
|
||||
|
||||
const optional<Convertible> currencyOption = objectMember(options, currencyKey);
|
||||
ParseResult currencyResult;
|
||||
if (currencyOption) {
|
||||
currencyResult = ctx.parse(*currencyOption, 1, {type::String});
|
||||
if (!currencyResult) {
|
||||
ctx.error("Number-format currency parsing failed.");
|
||||
return ParseResult();
|
||||
}
|
||||
}
|
||||
|
||||
const optional<Convertible> minFractionDigitsOption = objectMember(options, minFractionDigitsKey);
|
||||
ParseResult minFractionDigitsResult;
|
||||
if (minFractionDigitsOption) {
|
||||
minFractionDigitsResult = ctx.parse(*minFractionDigitsOption, 1, {type::Number});
|
||||
if (!minFractionDigitsResult) {
|
||||
ctx.error("Number-format min-fraction-digits parsing failed.");
|
||||
return ParseResult();
|
||||
}
|
||||
}
|
||||
|
||||
const optional<Convertible> maxFractionDigitsOption = objectMember(options, maxFractionDigitsKey);
|
||||
ParseResult maxFractionDigitsResult;
|
||||
if (maxFractionDigitsOption) {
|
||||
maxFractionDigitsResult = ctx.parse(*maxFractionDigitsOption, 1, {type::Number});
|
||||
if (!maxFractionDigitsResult) {
|
||||
ctx.error("Number-format max-fraction-digits parsing failed.");
|
||||
return ParseResult();
|
||||
}
|
||||
}
|
||||
|
||||
return ParseResult(std::make_unique<NumberFormat>(std::move(*numberResult),
|
||||
localeResult ? std::move(*localeResult) : nullptr,
|
||||
currencyResult? std::move(*currencyResult) : nullptr,
|
||||
minFractionDigitsResult ? std::move(*minFractionDigitsResult) : nullptr,
|
||||
maxFractionDigitsResult ? std::move(*maxFractionDigitsResult) : nullptr));
|
||||
}
|
||||
|
||||
mbgl::Value NumberFormat::serialize() const {
|
||||
std::vector<mbgl::Value> serialized{{ getOperator() }};
|
||||
serialized.emplace_back(number->serialize());
|
||||
|
||||
std::unordered_map<std::string, mbgl::Value> options;
|
||||
if (locale) {
|
||||
options[localeKey] = locale->serialize();
|
||||
}
|
||||
if (currency) {
|
||||
options[currencyKey] = currency->serialize();
|
||||
}
|
||||
if (minFractionDigits) {
|
||||
options[minFractionDigitsKey] = minFractionDigits->serialize();
|
||||
}
|
||||
if (maxFractionDigits) {
|
||||
options[maxFractionDigitsKey] = maxFractionDigits->serialize();
|
||||
}
|
||||
serialized.emplace_back(options);
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <mbgl/style/expression/coercion.hpp>
|
||||
#include <mbgl/style/expression/compound_expression.hpp>
|
||||
#include <mbgl/style/expression/comparison.hpp>
|
||||
#include <mbgl/style/expression/number_format.hpp>
|
||||
#include <mbgl/style/expression/format_expression.hpp>
|
||||
#include <mbgl/style/expression/interpolate.hpp>
|
||||
#include <mbgl/style/expression/length.hpp>
|
||||
@@ -120,6 +121,7 @@ MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry = mapbox::eternal::hash_m
|
||||
{"literal", Literal::parse},
|
||||
{"match", parseMatch},
|
||||
{"number", Assertion::parse},
|
||||
{"number-format", NumberFormat::parse},
|
||||
{"object", Assertion::parse},
|
||||
{"step", Step::parse},
|
||||
{"string", Assertion::parse},
|
||||
|
||||
9
test/fixtures/expression_equality/number-format-currency.a.json
vendored
Normal file
9
test/fixtures/expression_equality/number-format-currency.a.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[
|
||||
"number-format",
|
||||
123456.789,
|
||||
{
|
||||
"locale": "de-DE",
|
||||
"currency": "EUR"
|
||||
}
|
||||
]
|
||||
|
||||
9
test/fixtures/expression_equality/number-format-currency.b.json
vendored
Normal file
9
test/fixtures/expression_equality/number-format-currency.b.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[
|
||||
"number-format",
|
||||
123456.789,
|
||||
{
|
||||
"locale": "ja-JP",
|
||||
"currency": "JPY"
|
||||
}
|
||||
]
|
||||
|
||||
5
test/fixtures/expression_equality/number-format-default.a.json
vendored
Normal file
5
test/fixtures/expression_equality/number-format-default.a.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
"number-format",
|
||||
123456.789, {}
|
||||
]
|
||||
|
||||
5
test/fixtures/expression_equality/number-format-default.b.json
vendored
Normal file
5
test/fixtures/expression_equality/number-format-default.b.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
"number-format",
|
||||
-123456.789, {}
|
||||
]
|
||||
|
||||
10
test/fixtures/expression_equality/number-format-precision.a.json
vendored
Normal file
10
test/fixtures/expression_equality/number-format-precision.a.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
"number-format",
|
||||
987654321.23456789,
|
||||
{
|
||||
"locale": "en-US",
|
||||
"min-fraction-digits": 15,
|
||||
"max-fraction-digits": 20
|
||||
}
|
||||
]
|
||||
|
||||
10
test/fixtures/expression_equality/number-format-precision.b.json
vendored
Normal file
10
test/fixtures/expression_equality/number-format-precision.b.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
"number-format",
|
||||
987654321.23456789,
|
||||
{
|
||||
"locale": "en-US",
|
||||
"min-fraction-digits": 2,
|
||||
"max-fraction-digits": 4
|
||||
}
|
||||
]
|
||||
|
||||
@@ -36,10 +36,8 @@ TEST(Expression, IsExpression) {
|
||||
// TODO: "feature-state": https://github.com/mapbox/mapbox-gl-native/issues/12613
|
||||
// TODO: "interpolate-hcl": https://github.com/mapbox/mapbox-gl-native/issues/8720
|
||||
// TODO: "interpolate-lab": https://github.com/mapbox/mapbox-gl-native/issues/8720
|
||||
// TODO: "number-format": https://github.com/mapbox/mapbox-gl-native/issues/13632
|
||||
// TODO: "accumulated": https://github.com/mapbox/mapbox-gl-native/issues/14043
|
||||
if (name == "feature-state" || name == "interpolate-hcl" || name == "interpolate-lab" || name == "number-format" ||
|
||||
name == "accumulated") {
|
||||
if (name == "feature-state" || name == "interpolate-hcl" || name == "interpolate-lab" || name == "accumulated") {
|
||||
if (expression::isExpression(conversion::Convertible(expression))) {
|
||||
ASSERT_TRUE(false) << "Expression name" << name << "is implemented - please update Expression.IsExpression test.";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user