diff options
author | ygorshenin <mipt.vi002@gmail.com> | 2016-07-20 22:52:09 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-20 22:52:09 +0300 |
commit | 612799d306a422d3b27d9cabd4388f2d54b4ccbc (patch) | |
tree | 478009719e632ef3cb97848898576d92a45db065 /indexer | |
parent | 6b872bc6ea680eb2d8aeedba7474736c5ce8b108 (diff) | |
parent | 99f189642f6c5ae0139cc36ba5ba5106680d9de5 (diff) |
Merge pull request #3788 from milchakov/MAPSME-1583_default_name
[editor][ios][android] new way to edit names of place
Diffstat (limited to 'indexer')
-rw-r--r-- | indexer/editable_map_object.cpp | 139 | ||||
-rw-r--r-- | indexer/editable_map_object.hpp | 22 | ||||
-rw-r--r-- | indexer/indexer_tests/editable_map_object_test.cpp | 99 | ||||
-rw-r--r-- | indexer/osm_editor.cpp | 18 | ||||
-rw-r--r-- | indexer/osm_editor.hpp | 2 |
5 files changed, 269 insertions, 11 deletions
diff --git a/indexer/editable_map_object.cpp b/indexer/editable_map_object.cpp index 164705edcf..e87414f626 100644 --- a/indexer/editable_map_object.cpp +++ b/indexer/editable_map_object.cpp @@ -1,6 +1,7 @@ +#include "indexer/editable_map_object.hpp" #include "indexer/classificator.hpp" #include "indexer/cuisines.hpp" -#include "indexer/editable_map_object.hpp" +#include "indexer/osm_editor.hpp" #include "indexer/postcodes_matcher.hpp" #include "base/macros.hpp" @@ -9,6 +10,50 @@ #include "std/cctype.hpp" #include "std/cmath.hpp" + +namespace +{ +bool ExtractName(StringUtf8Multilang const & names, int8_t const langCode, + vector<osm::LocalizedName> & result) +{ + if (StringUtf8Multilang::kUnsupportedLanguageCode == langCode || + StringUtf8Multilang::kDefaultCode == langCode) + { + return false; + } + + // Exclude languages that are already present. + auto const it = + find_if(result.begin(), result.end(), [langCode](osm::LocalizedName const & localizedName) { + return localizedName.m_code == langCode; + }); + + if (result.end() != it) + return false; + + string name; + names.GetString(langCode, name); + result.emplace_back(langCode, name); + + return true; +} + +size_t PushMwmLanguages(StringUtf8Multilang const & names, vector<int8_t> const & mwmLanguages, + vector<osm::LocalizedName> & result) +{ + size_t count = 0; + static size_t const kMaxCountMwmLanguages = 2; + + for (size_t i = 0; i < mwmLanguages.size() && count < kMaxCountMwmLanguages; ++i) + { + if (ExtractName(names, mwmLanguages[i], result)) + ++count; + } + + return count; +} +} // namespace + namespace osm { // LocalizedName ----------------------------------------------------------------------------------- @@ -49,14 +94,54 @@ vector<feature::Metadata::EType> const & EditableMapObject::GetEditableFields() StringUtf8Multilang const & EditableMapObject::GetName() const { return m_name; } -vector<LocalizedName> EditableMapObject::GetLocalizedNames() const +NamesDataSource EditableMapObject::GetNamesDataSource() const { - vector<LocalizedName> result; - m_name.ForEach([&result](int8_t code, string const & name) -> bool - { - result.push_back({code, name}); - return true; - }); + const auto mwmInfo = GetID().m_mwmId.GetInfo(); + + if (!mwmInfo) + return NamesDataSource(); + + vector<int8_t> mwmLanguages; + mwmInfo->GetRegionData().GetLanguages(mwmLanguages); + + auto const userLangCode = StringUtf8Multilang::GetLangIndex(languages::GetCurrentNorm()); + + return GetNamesDataSource(m_name, mwmLanguages, userLangCode); +} + +// static +NamesDataSource EditableMapObject::GetNamesDataSource(StringUtf8Multilang const & source, + vector<int8_t> const & mwmLanguages, + int8_t const userLangCode) +{ + NamesDataSource result; + auto & names = result.names; + auto & mandatoryCount = result.mandatoryNamesCount; + // Push Mwm languages. + mandatoryCount = PushMwmLanguages(source, mwmLanguages, names); + + // Push user's language. + if (ExtractName(source, userLangCode, names)) + ++mandatoryCount; + + // Push other languages. + source.ForEach([&names, mandatoryCount](int8_t const code, string const & name) -> bool { + // Exclude default name. + if (StringUtf8Multilang::kDefaultCode == code) + return true; + + auto const mandatoryNamesEnd = names.begin() + mandatoryCount; + // Exclude languages which are already in container (languages with top priority). + auto const it = find_if( + names.begin(), mandatoryNamesEnd, + [code](LocalizedName const & localizedName) { return localizedName.m_code == code; }); + + if (mandatoryNamesEnd == it) + names.emplace_back(code, name); + + return true; + }); + return result; } @@ -83,8 +168,42 @@ void EditableMapObject::SetName(StringUtf8Multilang const & name) { m_name = nam void EditableMapObject::SetName(string name, int8_t langCode) { strings::Trim(name); - if (!name.empty()) - m_name.AddString(langCode, name); + if (name.empty()) + return; + + ASSERT_NOT_EQUAL(StringUtf8Multilang::kDefaultCode, langCode, + ("Direct editing of default name is deprecated.")); + + if (!Editor::Instance().OriginalFeatureHasDefaultName(GetID())) + { + const auto mwmInfo = GetID().m_mwmId.GetInfo(); + + if (mwmInfo) + { + vector<int8_t> mwmLanguages; + mwmInfo->GetRegionData().GetLanguages(mwmLanguages); + + if (CanUseAsDefaultName(langCode, mwmLanguages)) + m_name.AddString(StringUtf8Multilang::kDefaultCode, name); + } + } + + m_name.AddString(langCode, name); +} + +// static +bool EditableMapObject::CanUseAsDefaultName(int8_t const lang, vector<int8_t> const & mwmLanguages) +{ + for (auto const & mwmLang : mwmLanguages) + { + if (StringUtf8Multilang::kUnsupportedLanguageCode == mwmLang) + continue; + + if (lang == mwmLang) + return true; + } + + return false; } void EditableMapObject::SetMercator(m2::PointD const & center) { m_mercator = center; } diff --git a/indexer/editable_map_object.hpp b/indexer/editable_map_object.hpp index 365b23d20b..d033bc3d04 100644 --- a/indexer/editable_map_object.hpp +++ b/indexer/editable_map_object.hpp @@ -46,6 +46,17 @@ struct LocalizedName string const m_name; }; +// Class which contains vector of localized names with following priority: +// 1. Names for Mwm languages +// 2. User`s language name +// 3. Other names +// and mandatoryNamesCount - count of names which should be always shown. +struct NamesDataSource +{ + vector<LocalizedName> names; + size_t mandatoryNamesCount = 0; +}; + struct LocalizedStreet { string m_defaultName; @@ -67,7 +78,8 @@ public: vector<feature::Metadata::EType> const & GetEditableFields() const; StringUtf8Multilang const & GetName() const; - vector<LocalizedName> GetLocalizedNames() const; + // See comment for NamesDataSource class. + NamesDataSource GetNamesDataSource() const; LocalizedStreet const & GetStreet() const; vector<LocalizedStreet> const & GetNearbyStreets() const; string const & GetHouseNumber() const; @@ -117,6 +129,14 @@ public: static bool ValidateWebsite(string const & site); static bool ValidateEmail(string const & email); + // Check whether langCode can be used as default name. + static bool CanUseAsDefaultName(int8_t const langCode, vector<int8_t> const & nativeMwmLanguages); + + // See comment for NamesDataSource class. + static NamesDataSource GetNamesDataSource(StringUtf8Multilang const & source, + vector<int8_t> const & nativeMwmLanguages, + int8_t const userLanguage); + private: string m_houseNumber; LocalizedStreet m_street; diff --git a/indexer/indexer_tests/editable_map_object_test.cpp b/indexer/indexer_tests/editable_map_object_test.cpp index 059db03572..cfad66817d 100644 --- a/indexer/indexer_tests/editable_map_object_test.cpp +++ b/indexer/indexer_tests/editable_map_object_test.cpp @@ -6,6 +6,11 @@ namespace { using osm::EditableMapObject; +int8_t const GetLangCode(char const * ch) +{ + return StringUtf8Multilang::GetLangIndex(ch); +} + UNIT_TEST(EditableMapObject_SetWebsite) { EditableMapObject emo; @@ -125,4 +130,98 @@ UNIT_TEST(EditableMapObject_ValidateEmail) TEST(!EditableMapObject::ValidateEmail("emai@l_.ab"), ()); TEST(!EditableMapObject::ValidateEmail("email@e#$%&'*+-/=?^`_{}|~.com"), ()); } + +UNIT_TEST(EditableMapObject_CanUseAsDefaultName) +{ + EditableMapObject emo; + vector<int8_t> const nativeMwmLanguages {GetLangCode("de"), GetLangCode("fr")}; + + TEST(EditableMapObject::CanUseAsDefaultName(GetLangCode("de"), nativeMwmLanguages), + ("Check possibility to use Mwm language code")); + TEST(EditableMapObject::CanUseAsDefaultName(GetLangCode("fr"), nativeMwmLanguages), + ("Check possibility to use Mwm language code")); + TEST(!EditableMapObject::CanUseAsDefaultName(GetLangCode("int_name"), nativeMwmLanguages), + ("Check possibility to use international language code")); + TEST(!EditableMapObject::CanUseAsDefaultName(100, nativeMwmLanguages), + ("Incorrect language code is not supported")); + TEST(!EditableMapObject::CanUseAsDefaultName(GetLangCode("en"), {GetLangCode("abcd")}), + ("Incorrect Mwm language name is not supported")); + TEST(!EditableMapObject::CanUseAsDefaultName(GetLangCode("en"), nativeMwmLanguages), + ("Can not to use language which not Mwm language or international")); + TEST(!EditableMapObject::CanUseAsDefaultName(GetLangCode("ru"), nativeMwmLanguages), + ("Check possibility to use user`s language code")); + + // Trying to use language codes in reverse priority. + StringUtf8Multilang names; + names.AddString(GetLangCode("fr"), "second mwm language"); + emo.SetName(names); + + TEST(EditableMapObject::CanUseAsDefaultName(GetLangCode("fr"), nativeMwmLanguages), + ("It is possible to fix typo")); + + names.AddString(GetLangCode("de"), "first mwm language"); + emo.SetName(names); + + TEST(EditableMapObject::CanUseAsDefaultName(GetLangCode("de"), nativeMwmLanguages), + ("It is possible to fix typo")); + TEST(EditableMapObject::CanUseAsDefaultName(GetLangCode("fr"), nativeMwmLanguages), + ("It is possible to fix typo")); +} + +UNIT_TEST(EditableMapObject_GetNamesDataSource) +{ + EditableMapObject emo; + StringUtf8Multilang names; + + names.AddString(GetLangCode("default"), "Default name"); + names.AddString(GetLangCode("en"), "Eng name"); + names.AddString(GetLangCode("int_name"), "Int name"); + names.AddString(GetLangCode("de"), "De name"); + names.AddString(GetLangCode("ru"), "Ru name"); + names.AddString(GetLangCode("sv"), "Sv name"); + names.AddString(GetLangCode("be"), "By name"); + names.AddString(GetLangCode("ko"), "Ko name"); + names.AddString(GetLangCode("it"), "It name"); + emo.SetName(names); + + vector<int8_t> nativeMwmLanguages = {GetLangCode("de"), GetLangCode("fr")}; + + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), nativeMwmLanguages, GetLangCode("ko")); + + TEST_EQUAL(namesDataSource.names.size(), 9, ("All names except the default should be pushed into " + "data source plus empty mandatory names")); + TEST_EQUAL(namesDataSource.mandatoryNamesCount, 3, + ("Mandatory names count should be equal == Mwm languages + user`s language")); + TEST_EQUAL(namesDataSource.names[0].m_code, GetLangCode("de"), + ("Deutsch name should be first as first language in Mwm")); + TEST_EQUAL(namesDataSource.names[1].m_code, GetLangCode("fr"), + ("French name should be second as second language in Mwm")); + TEST_EQUAL(namesDataSource.names[2].m_code, GetLangCode("ko"), + ("Korean name should be third because user`s language should be followed by Mwm languages")); + + { + vector<int8_t> nativeMwmLanguages = {GetLangCode("de"), GetLangCode("fr")}; + + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), nativeMwmLanguages, GetLangCode("fr")); + TEST_EQUAL(namesDataSource.names.size(), 9, + ("All names except the default should be pushed into data source")); + TEST_EQUAL(namesDataSource.mandatoryNamesCount, 2, + ("Mandatory names count should be equal == Mwm languages + user`s language. " + "Excluding repetiton")); + } + { + vector<int8_t> nativeMwmLanguages = {GetLangCode("fr"), GetLangCode("fr")}; + + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), nativeMwmLanguages, GetLangCode("fr")); + TEST_EQUAL(namesDataSource.names.size(), 9, + ("All names except the default should be pushed into " + "data source plus empty mandatory names")); + TEST_EQUAL(namesDataSource.mandatoryNamesCount, 1, + ("Mandatory names count should be equal == Mwm languages + user`s language. " + "Excluding repetiton")); + } +} } // namespace diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp index 76e55a5197..1b428c35f0 100644 --- a/indexer/osm_editor.cpp +++ b/indexer/osm_editor.cpp @@ -426,6 +426,24 @@ bool Editor::IsCreatedFeature(FeatureID const & fid) return fid.m_index >= kStartIndexForCreatedFeatures; } +bool Editor::OriginalFeatureHasDefaultName(FeatureID const & fid) const +{ + if (IsCreatedFeature(fid)) + return false; + + auto const originalFeaturePtr = m_getOriginalFeatureFn(fid); + if (!originalFeaturePtr) + { + LOG(LERROR, ("A feature with id", fid, "cannot be loaded.")); + alohalytics::LogEvent("Editor_MissingFeature_Error"); + return false; + } + + auto const & names = originalFeaturePtr->GetNames(); + + return names.HasString(StringUtf8Multilang::kDefaultCode); +} + /// Several cases should be handled while saving changes: /// 1) a feature is not in editor's cache /// I. a feature was created diff --git a/indexer/osm_editor.hpp b/indexer/osm_editor.hpp index a17da2477f..0ccfe8f264 100644 --- a/indexer/osm_editor.hpp +++ b/indexer/osm_editor.hpp @@ -169,6 +169,8 @@ public: // Use GetFeatureStatus(fid) instead. This function is used when a feature is // not yet saved and we have to know if it was modified or created. static bool IsCreatedFeature(FeatureID const & fid); + // Returns true if the original feature has default name. + bool OriginalFeatureHasDefaultName(FeatureID const & fid) const; private: // TODO(AlexZ): Synchronize Save call/make it on a separate thread. |