From 9c8295c6a64b6f5b2c9b67fd01633a156481a3c8 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Mon, 23 Sep 2019 15:42:06 +0300 Subject: [core][android][darwin] Move platform specific Collator impls from expression Platform specific Collator implementations should not be part of an expression APIs. --- include/mbgl/i18n/collator.hpp | 24 +++ include/mbgl/style/expression/collator.hpp | 6 +- next/CMakeLists.txt | 1 + next/platform/android/android.cmake | 4 +- next/platform/linux/linux.cmake | 2 +- next/platform/qt/qt.cmake | 2 +- platform/android/core-files.json | 4 +- platform/android/src/i18n/collator.cpp | 194 +++++++++++++++++++++++ platform/android/src/i18n/collator_jni.hpp | 62 ++++++++ platform/android/src/jni_native.cpp | 4 +- platform/android/src/text/collator.cpp | 195 ------------------------ platform/android/src/text/collator_jni.hpp | 64 -------- platform/android/src/text/format_number_jni.hpp | 2 +- platform/darwin/src/collator.mm | 9 +- platform/default/src/mbgl/i18n/collator.cpp | 101 ++++++++++++ platform/default/src/mbgl/text/collator.cpp | 116 -------------- platform/linux/config.cmake | 2 +- src/core-files.json | 1 + src/mbgl/style/expression/collator.cpp | 24 +++ 19 files changed, 422 insertions(+), 395 deletions(-) create mode 100644 include/mbgl/i18n/collator.hpp create mode 100644 platform/android/src/i18n/collator.cpp create mode 100644 platform/android/src/i18n/collator_jni.hpp mode change 100755 => 100644 platform/android/src/jni_native.cpp delete mode 100644 platform/android/src/text/collator.cpp delete mode 100644 platform/android/src/text/collator_jni.hpp create mode 100644 platform/default/src/mbgl/i18n/collator.cpp delete mode 100644 platform/default/src/mbgl/text/collator.cpp create mode 100644 src/mbgl/style/expression/collator.cpp diff --git a/include/mbgl/i18n/collator.hpp b/include/mbgl/i18n/collator.hpp new file mode 100644 index 0000000000..9db1f804aa --- /dev/null +++ b/include/mbgl/i18n/collator.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace platform { + +class Collator { +public: + explicit Collator(bool caseSensitive, bool diacriticSensitive, optional locale = nullopt); + int compare(const std::string& lhs, const std::string& rhs) const; + std::string resolvedLocale() const; + bool operator==(const Collator& other) const; + +private: + class Impl; + std::shared_ptr impl; +}; + +} // namespace platform +} // namespace mbgl diff --git a/include/mbgl/style/expression/collator.hpp b/include/mbgl/style/expression/collator.hpp index 2a79e55556..51ba426fad 100644 --- a/include/mbgl/style/expression/collator.hpp +++ b/include/mbgl/style/expression/collator.hpp @@ -1,10 +1,9 @@ #pragma once -#include +#include #include #include -#include namespace mbgl { namespace style { @@ -20,8 +19,7 @@ public: std::string resolvedLocale() const; private: - class Impl; - std::shared_ptr impl; + platform::Collator collator; }; } // namespace expression diff --git a/next/CMakeLists.txt b/next/CMakeLists.txt index dae8ce09a1..bc15f04420 100644 --- a/next/CMakeLists.txt +++ b/next/CMakeLists.txt @@ -596,6 +596,7 @@ add_library( ${MBGL_ROOT}/src/mbgl/style/expression/check_subtype.cpp ${MBGL_ROOT}/src/mbgl/style/expression/coalesce.cpp ${MBGL_ROOT}/src/mbgl/style/expression/coercion.cpp + ${MBGL_ROOT}/src/mbgl/style/expression/collator.cpp ${MBGL_ROOT}/src/mbgl/style/expression/collator_expression.cpp ${MBGL_ROOT}/src/mbgl/style/expression/comparison.cpp ${MBGL_ROOT}/src/mbgl/style/expression/compound_expression.cpp diff --git a/next/platform/android/android.cmake b/next/platform/android/android.cmake index 240d4ccfdf..edc74576dc 100644 --- a/next/platform/android/android.cmake +++ b/next/platform/android/android.cmake @@ -187,8 +187,8 @@ target_sources( ${MBGL_ROOT}/platform/android/src/style/transition_options.hpp ${MBGL_ROOT}/platform/android/src/style/value.cpp ${MBGL_ROOT}/platform/android/src/style/value.hpp - ${MBGL_ROOT}/platform/android/src/text/collator.cpp - ${MBGL_ROOT}/platform/android/src/text/collator_jni.hpp + ${MBGL_ROOT}/platform/android/src/i18n/collator.cpp + ${MBGL_ROOT}/platform/android/src/i18n/collator_jni.hpp ${MBGL_ROOT}/platform/android/src/text/format_number.cpp ${MBGL_ROOT}/platform/android/src/text/format_number_jni.hpp ${MBGL_ROOT}/platform/android/src/text/local_glyph_rasterizer.cpp diff --git a/next/platform/linux/linux.cmake b/next/platform/linux/linux.cmake index 457ff441b8..993d3512e3 100644 --- a/next/platform/linux/linux.cmake +++ b/next/platform/linux/linux.cmake @@ -29,7 +29,7 @@ target_sources( ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp ${MBGL_ROOT}/platform/default/src/mbgl/text/bidi.cpp - ${MBGL_ROOT}/platform/default/src/mbgl/text/collator.cpp + ${MBGL_ROOT}/platform/default/src/mbgl/i18n/collator.cpp ${MBGL_ROOT}/platform/default/src/mbgl/text/local_glyph_rasterizer.cpp ${MBGL_ROOT}/platform/default/src/mbgl/util/async_task.cpp ${MBGL_ROOT}/platform/default/src/mbgl/util/compression.cpp diff --git a/next/platform/qt/qt.cmake b/next/platform/qt/qt.cmake index 4d9275aaae..59aa00788f 100644 --- a/next/platform/qt/qt.cmake +++ b/next/platform/qt/qt.cmake @@ -29,6 +29,7 @@ target_sources( ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp + ${MBGL_ROOT}/platform/default/src/mbgl/i18n/collator.cpp ${MBGL_ROOT}/platform/default/src/mbgl/layermanager/layer_manager.cpp ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp @@ -41,7 +42,6 @@ target_sources( ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp - ${MBGL_ROOT}/platform/default/src/mbgl/text/collator.cpp ${MBGL_ROOT}/platform/default/src/mbgl/util/compression.cpp ${MBGL_ROOT}/platform/qt/src/async_task.cpp ${MBGL_ROOT}/platform/qt/src/async_task_impl.hpp diff --git a/platform/android/core-files.json b/platform/android/core-files.json index 9d502bf33e..4057b21000 100644 --- a/platform/android/core-files.json +++ b/platform/android/core-files.json @@ -80,7 +80,7 @@ "platform/android/src/style/sources/vector_source.cpp", "platform/android/src/style/transition_options.cpp", "platform/android/src/style/value.cpp", - "platform/android/src/text/collator.cpp", + "platform/android/src/i18n/collator.cpp", "platform/android/src/text/local_glyph_rasterizer.cpp", "platform/android/src/text/format_number.cpp", "platform/android/src/gl_functions.cpp", @@ -185,7 +185,7 @@ "style/sources/vector_source.hpp": "platform/android/src/style/sources/vector_source.hpp", "style/transition_options.hpp": "platform/android/src/style/transition_options.hpp", "style/value.hpp": "platform/android/src/style/value.hpp", - "text/collator_jni.hpp": "platform/android/src/text/collator_jni.hpp", + "text/collator_jni.hpp": "platform/android/src/i18n/collator_jni.hpp", "text/local_glyph_rasterizer_jni.hpp": "platform/android/src/text/local_glyph_rasterizer_jni.hpp" } } diff --git a/platform/android/src/i18n/collator.cpp b/platform/android/src/i18n/collator.cpp new file mode 100644 index 0000000000..b3dd8e21b3 --- /dev/null +++ b/platform/android/src/i18n/collator.cpp @@ -0,0 +1,194 @@ +#include +#include + +#include + +#include "../attach_env.hpp" +#include "collator_jni.hpp" + +namespace mbgl { +namespace android { + +void Collator::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local> Collator::getInstance(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "getInstance"); + return javaClass.Call(env, method, locale); +} + +void Collator::setStrength(jni::JNIEnv& env, const jni::Object& collator, jni::jint strength) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "setStrength"); + collator.Call(env, method, strength); +} + +jni::jint Collator::compare(jni::JNIEnv& env, const jni::Object& collator, const jni::String& lhs, const jni::String& rhs) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "compare"); + return collator.Call(env, method, lhs, rhs); +} + +void StringUtils::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local StringUtils::unaccent(jni::JNIEnv& env, const jni::String& value) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod(env, "unaccent"); + return javaClass.Call(env, method, value); +} + +void Locale::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +/* +We would prefer to use for/toLanguageTag, but they're only available in API level 21+ + +jni::Object Locale::forLanguageTag(jni::JNIEnv& env, jni::String languageTag) { + using Signature = jni::Object(jni::String); + auto method = javaClass.GetStaticMethod(env, "forLanguageTag"); + return javaClass.Call(env, method, languageTag); +} + +jni::String Locale::toLanguageTag(jni::JNIEnv& env, jni::Object locale) { + using Signature = jni::String(); + auto static method = javaClass.GetMethod(env, "toLanguageTag"); + return locale.Call(env, method); +} +*/ + +jni::Local Locale::getLanguage(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "getLanguage"); + return locale.Call(env, method); +} + +jni::Local Locale::getCountry(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "getCountry"); + return locale.Call(env, method); +} + +jni::Local> Locale::getDefault(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod ()>(env, "getDefault"); + return javaClass.Call(env, method); +} + +jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, language); +} + +jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language, const jni::String& region) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, language, region); +} + +} // namespace android + +namespace platform { + +class Collator::Impl { +public: + Impl(bool caseSensitive_, bool diacriticSensitive_, optional locale_) + : caseSensitive(caseSensitive_) + , diacriticSensitive(diacriticSensitive_) + , env(android::AttachEnv()) + { + LanguageTag languageTag = locale_ ? LanguageTag::fromBCP47(*locale_) : 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(*env, *languageTag.language))); + } else { + locale = jni::NewGlobal(*env, + android::Locale::New(*env, jni::Make(*env, *languageTag.language), + jni::Make(*env, *languageTag.region))); + } + collator = jni::NewGlobal(*env, android::Collator::getInstance(*env, locale)); + if (!diacriticSensitive && !caseSensitive) { + android::Collator::setStrength(*env, collator, 0 /*PRIMARY*/); + } else if (diacriticSensitive && !caseSensitive) { + android::Collator::setStrength(*env, collator, 1 /*SECONDARY*/); + } else if (caseSensitive) { + // If we're case-sensitive and diacritic-sensitive, we use a case-sensitive collator + // and a fallback implementation of diacritic-insensitivity. + android::Collator::setStrength(*env, collator, 2 /*TERTIARY*/); + } + } + + bool operator==(const Impl& other) const { + return caseSensitive == other.caseSensitive && + diacriticSensitive == other.diacriticSensitive && + resolvedLocale() == other.resolvedLocale(); + } + + int compare(const std::string& lhs, const std::string& rhs) const { + bool useUnaccent = !diacriticSensitive && caseSensitive; + // java.text.Collator doesn't support a diacritic-insensitive/case-sensitive collation + // order, so we have to compromise here. We use Android's case-sensitive Collator + // against strings that have been "unaccented" using non-locale-aware nunicode logic. + // Because of the difference in locale-awareness, this means turning on case-sensitivity + // can _potentially_ change compare results for strings that don't actually have any case + // differences. + jni::Local jlhs = useUnaccent + ? android::StringUtils::unaccent(*env, jni::Make(*env, lhs)) + : jni::Make(*env, lhs); + jni::Local jrhs = useUnaccent + ? android::StringUtils::unaccent(*env, jni::Make(*env, rhs)) + : jni::Make(*env, rhs); + + jni::jint result = android::Collator::compare(*env, collator, jlhs, jrhs); + + return result; + } + + std::string resolvedLocale() const { + std::string language = jni::Make(*env, android::Locale::getLanguage(*env, locale)); + std::string region = jni::Make(*env, android::Locale::getCountry(*env, locale)); + + optional resultLanguage; + if (!language.empty()) resultLanguage = language; + optional resultRegion; + if (!region.empty()) resultRegion = region; + + return LanguageTag(resultLanguage, {}, resultRegion).toBCP47(); + } + +private: + bool caseSensitive; + bool diacriticSensitive; + + android::UniqueEnv env; + jni::Global> collator; + jni::Global> locale; +}; + + +Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale_) + : impl(std::make_shared(caseSensitive, diacriticSensitive, std::move(locale_))) +{} + +bool Collator::operator==(const Collator& other) const { + return *impl == *(other.impl); +} + +int Collator::compare(const std::string& lhs, const std::string& rhs) const { + return impl->compare(lhs, rhs); +} + +std::string Collator::resolvedLocale() const { + return impl->resolvedLocale(); +} + +} // namespace platform +} // namespace mbgl diff --git a/platform/android/src/i18n/collator_jni.hpp b/platform/android/src/i18n/collator_jni.hpp new file mode 100644 index 0000000000..612f9b49f3 --- /dev/null +++ b/platform/android/src/i18n/collator_jni.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +/* + android::Collator and android::Locale are + the JNI wrappers of + java/text/Collator and java/util/Locale + + mbgl::Collator is the portable interface + Both implementations are in collator.cpp + */ + +namespace mbgl { +namespace android { + +class Locale { +public: + static constexpr auto Name() { return "java/util/Locale"; }; + + /* Requires API level 21+ in order to support script/variant + static jni::Object forLanguageTag(jni::JNIEnv&, jni::String); + static jni::String toLanguageTag(jni::JNIEnv&, jni::Object); + */ + static jni::Local> getDefault(jni::JNIEnv&); + static jni::Local getLanguage(jni::JNIEnv&, const jni::Object&); + static jni::Local getCountry(jni::JNIEnv&, const jni::Object&); + + static jni::Local> New(jni::JNIEnv&, const jni::String&); + static jni::Local> New(jni::JNIEnv&, const jni::String&, const jni::String&); + + + static void registerNative(jni::JNIEnv&); +}; + +class Collator { +public: + static constexpr auto Name() { return "java/text/Collator"; }; + + static jni::Local> getInstance(jni::JNIEnv&, const jni::Object&); + + static void setStrength(jni::JNIEnv&, const jni::Object&, jni::jint); + + static jni::jint compare(jni::JNIEnv&, const jni::Object&, const jni::String&, const jni::String&); + + + static void registerNative(jni::JNIEnv&); +}; + + +class StringUtils { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/utils/StringUtils"; }; + + static jni::Local unaccent(jni::JNIEnv&, const jni::String&); + + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/jni_native.cpp b/platform/android/src/jni_native.cpp old mode 100755 new mode 100644 index df96ba9759..966dc6a007 --- a/platform/android/src/jni_native.cpp +++ b/platform/android/src/jni_native.cpp @@ -50,10 +50,10 @@ #include "snapshotter/map_snapshotter.hpp" #include "snapshotter/map_snapshot.hpp" #endif -#include "text/collator_jni.hpp" +#include "i18n/collator_jni.hpp" +#include "logger.hpp" #include "text/local_glyph_rasterizer_jni.hpp" #include "text/format_number_jni.hpp" -#include "logger.hpp" namespace mbgl { namespace android { diff --git a/platform/android/src/text/collator.cpp b/platform/android/src/text/collator.cpp deleted file mode 100644 index 1cd6f3cab0..0000000000 --- a/platform/android/src/text/collator.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include -#include - -#include - -#include "../attach_env.hpp" -#include "collator_jni.hpp" - -namespace mbgl { -namespace android { - -void Collator::registerNative(jni::JNIEnv& env) { - jni::Class::Singleton(env); -} - -jni::Local> Collator::getInstance(jni::JNIEnv& env, const jni::Object& locale) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "getInstance"); - return javaClass.Call(env, method, locale); -} - -void Collator::setStrength(jni::JNIEnv& env, const jni::Object& collator, jni::jint strength) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetMethod(env, "setStrength"); - collator.Call(env, method, strength); -} - -jni::jint Collator::compare(jni::JNIEnv& env, const jni::Object& collator, const jni::String& lhs, const jni::String& rhs) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetMethod(env, "compare"); - return collator.Call(env, method, lhs, rhs); -} - -void StringUtils::registerNative(jni::JNIEnv& env) { - jni::Class::Singleton(env); -} - -jni::Local StringUtils::unaccent(jni::JNIEnv& env, const jni::String& value) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetStaticMethod(env, "unaccent"); - return javaClass.Call(env, method, value); -} - -void Locale::registerNative(jni::JNIEnv& env) { - jni::Class::Singleton(env); -} - -/* -We would prefer to use for/toLanguageTag, but they're only available in API level 21+ - -jni::Object Locale::forLanguageTag(jni::JNIEnv& env, jni::String languageTag) { - using Signature = jni::Object(jni::String); - auto method = javaClass.GetStaticMethod(env, "forLanguageTag"); - return javaClass.Call(env, method, languageTag); -} - -jni::String Locale::toLanguageTag(jni::JNIEnv& env, jni::Object locale) { - using Signature = jni::String(); - auto static method = javaClass.GetMethod(env, "toLanguageTag"); - return locale.Call(env, method); -} -*/ - -jni::Local Locale::getLanguage(jni::JNIEnv& env, const jni::Object& locale) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetMethod(env, "getLanguage"); - return locale.Call(env, method); -} - -jni::Local Locale::getCountry(jni::JNIEnv& env, const jni::Object& locale) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetMethod(env, "getCountry"); - return locale.Call(env, method); -} - -jni::Local> Locale::getDefault(jni::JNIEnv& env) { - static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetStaticMethod ()>(env, "getDefault"); - return javaClass.Call(env, method); -} - -jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language) { - static auto& javaClass = jni::Class::Singleton(env); - static auto constructor = javaClass.GetConstructor(env); - return javaClass.New(env, constructor, language); -} - -jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language, const jni::String& region) { - static auto& javaClass = jni::Class::Singleton(env); - static auto constructor = javaClass.GetConstructor(env); - return javaClass.New(env, constructor, language, region); -} - -} // namespace android - -namespace style { -namespace expression { - -class Collator::Impl { -public: - Impl(bool caseSensitive_, bool diacriticSensitive_, optional locale_) - : caseSensitive(caseSensitive_) - , diacriticSensitive(diacriticSensitive_) - , env(android::AttachEnv()) - { - LanguageTag languageTag = locale_ ? LanguageTag::fromBCP47(*locale_) : 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(*env, *languageTag.language))); - } else { - locale = jni::NewGlobal(*env, - android::Locale::New(*env, jni::Make(*env, *languageTag.language), - jni::Make(*env, *languageTag.region))); - } - collator = jni::NewGlobal(*env, android::Collator::getInstance(*env, locale)); - if (!diacriticSensitive && !caseSensitive) { - android::Collator::setStrength(*env, collator, 0 /*PRIMARY*/); - } else if (diacriticSensitive && !caseSensitive) { - android::Collator::setStrength(*env, collator, 1 /*SECONDARY*/); - } else if (caseSensitive) { - // If we're case-sensitive and diacritic-sensitive, we use a case-sensitive collator - // and a fallback implementation of diacritic-insensitivity. - android::Collator::setStrength(*env, collator, 2 /*TERTIARY*/); - } - } - - bool operator==(const Impl& other) const { - return caseSensitive == other.caseSensitive && - diacriticSensitive == other.diacriticSensitive && - resolvedLocale() == other.resolvedLocale(); - } - - int compare(const std::string& lhs, const std::string& rhs) const { - bool useUnaccent = !diacriticSensitive && caseSensitive; - // java.text.Collator doesn't support a diacritic-insensitive/case-sensitive collation - // order, so we have to compromise here. We use Android's case-sensitive Collator - // against strings that have been "unaccented" using non-locale-aware nunicode logic. - // Because of the difference in locale-awareness, this means turning on case-sensitivity - // can _potentially_ change compare results for strings that don't actually have any case - // differences. - jni::Local jlhs = useUnaccent ? android::StringUtils::unaccent(*env, jni::Make(*env, lhs)) - : jni::Make(*env, lhs); - jni::Local jrhs = useUnaccent ? android::StringUtils::unaccent(*env, jni::Make(*env, rhs)) - : jni::Make(*env, rhs); - - jni::jint result = android::Collator::compare(*env, collator, jlhs, jrhs); - - return result; - } - - std::string resolvedLocale() const { - std::string language = jni::Make(*env, android::Locale::getLanguage(*env, locale)); - std::string region = jni::Make(*env, android::Locale::getCountry(*env, locale)); - - optional resultLanguage; - if (!language.empty()) resultLanguage = language; - optional resultRegion; - if (!region.empty()) resultRegion = region; - - return LanguageTag(resultLanguage, {}, resultRegion).toBCP47(); - } - -private: - bool caseSensitive; - bool diacriticSensitive; - - android::UniqueEnv env; - jni::Global> collator; - jni::Global> locale; -}; - - -Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale_) - : impl(std::make_shared(caseSensitive, diacriticSensitive, std::move(locale_))) -{} - -bool Collator::operator==(const Collator& other) const { - return *impl == *(other.impl); -} - -int Collator::compare(const std::string& lhs, const std::string& rhs) const { - return impl->compare(lhs, rhs); -} - -std::string Collator::resolvedLocale() const { - return impl->resolvedLocale(); -} - -} // namespace expression -} // namespace style -} // namespace mbgl diff --git a/platform/android/src/text/collator_jni.hpp b/platform/android/src/text/collator_jni.hpp deleted file mode 100644 index dd3f845662..0000000000 --- a/platform/android/src/text/collator_jni.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include - -#include - -/* - android::Collator and android::Locale are - the JNI wrappers of - java/text/Collator and java/util/Locale - - mbgl::Collator is the portable interface - Both implementations are in collator.cpp - */ - -namespace mbgl { -namespace android { - -class Locale { -public: - static constexpr auto Name() { return "java/util/Locale"; }; - - /* Requires API level 21+ in order to support script/variant - static jni::Object forLanguageTag(jni::JNIEnv&, jni::String); - static jni::String toLanguageTag(jni::JNIEnv&, jni::Object); - */ - static jni::Local> getDefault(jni::JNIEnv&); - static jni::Local getLanguage(jni::JNIEnv&, const jni::Object&); - static jni::Local getCountry(jni::JNIEnv&, const jni::Object&); - - static jni::Local> New(jni::JNIEnv&, const jni::String&); - static jni::Local> New(jni::JNIEnv&, const jni::String&, const jni::String&); - - - static void registerNative(jni::JNIEnv&); -}; - -class Collator { -public: - static constexpr auto Name() { return "java/text/Collator"; }; - - static jni::Local> getInstance(jni::JNIEnv&, const jni::Object&); - - static void setStrength(jni::JNIEnv&, const jni::Object&, jni::jint); - - static jni::jint compare(jni::JNIEnv&, const jni::Object&, const jni::String&, const jni::String&); - - - static void registerNative(jni::JNIEnv&); -}; - - -class StringUtils { -public: - static constexpr auto Name() { return "com/mapbox/mapboxsdk/utils/StringUtils"; }; - - static jni::Local unaccent(jni::JNIEnv&, const jni::String&); - - - static void registerNative(jni::JNIEnv&); -}; - -} // namespace android -} // namespace mbgl diff --git a/platform/android/src/text/format_number_jni.hpp b/platform/android/src/text/format_number_jni.hpp index 1720038925..1b63012c6e 100644 --- a/platform/android/src/text/format_number_jni.hpp +++ b/platform/android/src/text/format_number_jni.hpp @@ -2,7 +2,7 @@ #include -#include "collator_jni.hpp" +#include "../i18n/collator_jni.hpp" /* android::NumberFormat is the JNI wrapper diff --git a/platform/darwin/src/collator.mm b/platform/darwin/src/collator.mm index fe2b46ffa7..0f010c1df8 100644 --- a/platform/darwin/src/collator.mm +++ b/platform/darwin/src/collator.mm @@ -1,12 +1,11 @@ -#include +#include #include #import namespace mbgl { -namespace style { -namespace expression { +namespace platform { class Collator::Impl { public: @@ -48,7 +47,6 @@ private: NSLocale* locale; }; - Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale_) : impl(std::make_shared(caseSensitive, diacriticSensitive, std::move(locale_))) {} @@ -65,6 +63,5 @@ std::string Collator::resolvedLocale() const { return impl->resolvedLocale(); } -} // namespace expression -} // namespace style +} // namespace platform } // namespace mbgl diff --git a/platform/default/src/mbgl/i18n/collator.cpp b/platform/default/src/mbgl/i18n/collator.cpp new file mode 100644 index 0000000000..f46accff8a --- /dev/null +++ b/platform/default/src/mbgl/i18n/collator.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#include +#include + +/* + The default implementation of Collator ignores locale. + Case sensitivity and collation order are based on + Default Unicode Collation Element Table (DUCET). + + Diacritic-insensitivity is implemented with nunicode's + non-standard "unaccent" functionality, which is tailored + to European languages. + + It would be possible to implement locale awareness using ICU, + but would require bundling locale data. +*/ + +namespace { +std::string unaccent(const std::string& str) { + std::stringstream output; + char const *itr = str.c_str(), *nitr; + char const* end = itr + str.length(); + char lo[5] = {0}; + + for (; itr < end; itr = nitr) { + uint32_t code_point = 0; + char const* buf = nullptr; + + nitr = _nu_tounaccent(itr, end, nu_utf8_read, &code_point, &buf, nullptr); + if (buf != nullptr) { + do { + buf = NU_CASEMAP_DECODING_FUNCTION(buf, &code_point); + if (code_point == 0) break; + output.write(lo, nu_utf8_write(code_point, lo) - lo); + } while (code_point != 0); + } else { + output.write(itr, nitr - itr); + } + } + + return output.str(); +} +} // namespace + +namespace mbgl { +namespace platform { + +class Collator::Impl { +public: + Impl(bool caseSensitive_, bool diacriticSensitive_, optional) + : caseSensitive(caseSensitive_) + , diacriticSensitive(diacriticSensitive_) + {} + + bool operator==(const Impl& other) const { + return caseSensitive == other.caseSensitive && diacriticSensitive == other.diacriticSensitive; + } + + int compare(const std::string& lhs, const std::string& rhs) const { + if (caseSensitive && diacriticSensitive) { + return nu_strcoll(lhs.c_str(), rhs.c_str(), + nu_utf8_read, nu_utf8_read); + } else if (!caseSensitive && diacriticSensitive) { + return nu_strcasecoll(lhs.c_str(), rhs.c_str(), + nu_utf8_read, nu_utf8_read); + } else if (caseSensitive && !diacriticSensitive) { + return nu_strcoll(unaccent(lhs).c_str(), unaccent(rhs).c_str(), nu_utf8_read, nu_utf8_read); + } else { + return nu_strcasecoll(unaccent(lhs).c_str(), unaccent(rhs).c_str(), nu_utf8_read, nu_utf8_read); + } + } + + std::string resolvedLocale() const { + return ""; + } + +private: + bool caseSensitive; + bool diacriticSensitive; +}; + +int Collator::compare(const std::string& lhs, const std::string& rhs) const { + return impl->compare(lhs, rhs); +} + +bool Collator::operator==(const Collator& other) const { + return *impl == *(other.impl); +} + +std::string Collator::resolvedLocale() const { + return impl->resolvedLocale(); +} + +Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale) + : impl(std::make_shared(caseSensitive, diacriticSensitive, std::move(locale))) {} + +} // namespace platform +} // namespace mbgl diff --git a/platform/default/src/mbgl/text/collator.cpp b/platform/default/src/mbgl/text/collator.cpp deleted file mode 100644 index 2d85b2d466..0000000000 --- a/platform/default/src/mbgl/text/collator.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -/* - The default implementation of Collator ignores locale. - Case sensitivity and collation order are based on - Default Unicode Collation Element Table (DUCET). - - Diacritic-insensitivity is implemented with nunicode's - non-standard "unaccent" functionality, which is tailored - to European languages. - - It would be possible to implement locale awareness using ICU, - but would require bundling locale data. -*/ - -namespace { -std::string unaccent(const std::string& str) -{ - std::stringstream output; - char const *itr = str.c_str(), *nitr; - char const *end = itr + str.length(); - char lo[5] = { 0 }; - - for (; itr < end; itr = nitr) - { - uint32_t code_point = 0; - char const* buf = nullptr; - - nitr = _nu_tounaccent(itr, end, nu_utf8_read, &code_point, &buf, nullptr); - if (buf != nullptr) - { - do - { - buf = NU_CASEMAP_DECODING_FUNCTION(buf, &code_point); - if (code_point == 0) break; - output.write(lo, nu_utf8_write(code_point, lo) - lo); - } - while (code_point != 0); - } - else - { - output.write(itr, nitr - itr); - } - } - - return output.str(); -} -} // namespace - -namespace mbgl { -namespace style { -namespace expression { - -class Collator::Impl { -public: - Impl(bool caseSensitive_, bool diacriticSensitive_, optional) - : caseSensitive(caseSensitive_) - , diacriticSensitive(diacriticSensitive_) - {} - - bool operator==(const Impl& other) const { - return caseSensitive == other.caseSensitive && - diacriticSensitive == other.diacriticSensitive; - } - - int compare(const std::string& lhs, const std::string& rhs) const { - if (caseSensitive && diacriticSensitive) { - return nu_strcoll(lhs.c_str(), rhs.c_str(), - nu_utf8_read, nu_utf8_read); - } else if (!caseSensitive && diacriticSensitive) { - return nu_strcasecoll(lhs.c_str(), rhs.c_str(), - nu_utf8_read, nu_utf8_read); - } else if (caseSensitive && !diacriticSensitive) { - return nu_strcoll(unaccent(lhs).c_str(), unaccent(rhs).c_str(), - nu_utf8_read, nu_utf8_read); - } else { - return nu_strcasecoll(unaccent(lhs).c_str(), unaccent(rhs).c_str(), - nu_utf8_read, nu_utf8_read); - } - } - - std::string resolvedLocale() const { - return ""; - } -private: - bool caseSensitive; - bool diacriticSensitive; -}; - - -Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale_) - : impl(std::make_shared(caseSensitive, diacriticSensitive, std::move(locale_))) -{} - -bool Collator::operator==(const Collator& other) const { - return *impl == *(other.impl); -} - -int Collator::compare(const std::string& lhs, const std::string& rhs) const { - return impl->compare(lhs, rhs); -} - -std::string Collator::resolvedLocale() const { - return impl->resolvedLocale(); -} - - -} // namespace expression -} // namespace style -} // namespace mbgl diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index 3a0fd91d36..01c46c9fcb 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -50,7 +50,7 @@ macro(mbgl_platform_core) PRIVATE platform/default/src/mbgl/util/string_stdlib.cpp PRIVATE platform/default/src/mbgl/util/thread.cpp PRIVATE platform/default/src/mbgl/text/bidi.cpp - PRIVATE platform/default/src/mbgl/text/collator.cpp + PRIVATE platform/default/src/mbgl/i18n/collator.cpp PRIVATE platform/default/src/mbgl/layermanager/layer_manager.cpp PRIVATE platform/default/src/mbgl/text/local_glyph_rasterizer.cpp PRIVATE platform/default/src/mbgl/util/thread_local.cpp diff --git a/src/core-files.json b/src/core-files.json index d881e6259d..2f609e92ec 100644 --- a/src/core-files.json +++ b/src/core-files.json @@ -178,6 +178,7 @@ "src/mbgl/style/expression/check_subtype.cpp", "src/mbgl/style/expression/coalesce.cpp", "src/mbgl/style/expression/coercion.cpp", + "src/mbgl/style/expression/collator.cpp", "src/mbgl/style/expression/collator_expression.cpp", "src/mbgl/style/expression/comparison.cpp", "src/mbgl/style/expression/compound_expression.cpp", diff --git a/src/mbgl/style/expression/collator.cpp b/src/mbgl/style/expression/collator.cpp new file mode 100644 index 0000000000..185d713150 --- /dev/null +++ b/src/mbgl/style/expression/collator.cpp @@ -0,0 +1,24 @@ +#include + +namespace mbgl { +namespace style { +namespace expression { + +Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional locale) + : collator(platform::Collator(caseSensitive, diacriticSensitive, std::move(locale))) {} + +bool Collator::operator==(const Collator& other) const { + return collator == other.collator; +} + +int Collator::compare(const std::string& lhs, const std::string& rhs) const { + return collator.compare(lhs, rhs); +} + +std::string Collator::resolvedLocale() const { + return collator.resolvedLocale(); +} + +} // namespace expression +} // namespace style +} // namespace mbgl -- cgit v1.2.1