diff options
author | Arsentiy Milchakov <a.milchakov@corp.mail.ru> | 2016-07-15 20:16:41 +0300 |
---|---|---|
committer | Arsentiy Milchakov <a.milchakov@corp.mail.ru> | 2016-07-19 14:43:41 +0300 |
commit | fe4ccf09c2c21d9b1499429f5531b765d9c928e2 (patch) | |
tree | 96aab836723ffd13cf44e4adf954c63a02513c26 /indexer | |
parent | 751e6ea7a91f0ba8e9248206a61721137504946c (diff) |
Direct editing of default name is locked, editing of multilingual names is allowed, changes for android are included
Diffstat (limited to 'indexer')
-rw-r--r-- | indexer/editable_map_object.cpp | 145 | ||||
-rw-r--r-- | indexer/editable_map_object.hpp | 33 | ||||
-rw-r--r-- | indexer/indexer_tests/editable_map_object_test.cpp | 100 | ||||
-rw-r--r-- | indexer/osm_editor.cpp | 19 | ||||
-rw-r--r-- | indexer/osm_editor.hpp | 2 |
5 files changed, 288 insertions, 11 deletions
diff --git a/indexer/editable_map_object.cpp b/indexer/editable_map_object.cpp index 164705edcf..3515027f97 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,54 @@ #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<string> const & mwmLanguages, vector<osm::LocalizedName> & result) +{ + size_t count = 0; + auto langCode = StringUtf8Multilang::kUnsupportedLanguageCode; + static size_t const kMaxCountMwmLanguages = 2; + + for (auto const & language : mwmLanguages) + { + langCode = StringUtf8Multilang::GetLangIndex(language); + if (ExtractName(names, langCode, result)) + ++count; + + if (count >= kMaxCountMwmLanguages) + return count; + } + + return count; +} +} + namespace osm { // LocalizedName ----------------------------------------------------------------------------------- @@ -49,14 +98,49 @@ 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; - }); + return GetNamesDataSource(m_name, GetMwmLanguages(), languages::GetCurrentNorm()); +} + +// static +NamesDataSource EditableMapObject::GetNamesDataSource(StringUtf8Multilang const & source, + vector<string> const & nativeMwmLanguages, + string const & userLanguage) +{ + NamesDataSource result; + auto & names = result.names; + auto & mandatoryCount = result.mandatoryNamesCount; + // Push Mwm languages. + mandatoryCount = PushMwmLanguages(source, nativeMwmLanguages, names); + + // Push user's language. + auto const langCode = StringUtf8Multilang::GetLangIndex(userLanguage); + if (ExtractName(source, langCode, names)) + ++mandatoryCount; + + // Push international language. + if (ExtractName(source, StringUtf8Multilang::kInternationalCode, names)) + ++mandatoryCount; + + // Push other languages. + source.ForEach([&names, mandatoryCount](int8_t code, string const & name) -> bool { + // Exclude default name. + if (StringUtf8Multilang::kDefaultCode == code) + return true; + + auto const mandatoryNamesEnd = names.begin() + mandatoryCount; + // Exclude languages which 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 +167,49 @@ 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, + ("You trying to set ", name, + " as default, but direct editing of default name is deprecated.")); + + if (!Editor::Instance().WasDefaultNameSaved(GetID()) && + CanUseAsDefaultName(langCode, m_name, GetMwmLanguages())) + { + m_name.AddString(StringUtf8Multilang::kDefaultCode, name); + } + + m_name.AddString(langCode, name); +} + +// static +bool EditableMapObject::CanUseAsDefaultName(int8_t const langCode, StringUtf8Multilang const & name, + vector<string> const & nativeMwmLanguages) +{ + auto index = StringUtf8Multilang::kUnsupportedLanguageCode; + string unused; + + // Languages priority: 1. Mwm languages 2. International language. + for (auto const & language : nativeMwmLanguages) + { + index = StringUtf8Multilang::GetLangIndex(language); + + if (StringUtf8Multilang::kUnsupportedLanguageCode == index) + return false; + + if (langCode == index) + return true; + + // A name with a higher priority was added already. + if (name.GetString(index, unused)) + return false; + } + + if (langCode == StringUtf8Multilang::kInternationalCode) + 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..e89aa543ad 100644 --- a/indexer/editable_map_object.hpp +++ b/indexer/editable_map_object.hpp @@ -46,6 +46,18 @@ 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. International name +// 4. 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 +79,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 +130,24 @@ public: static bool ValidateWebsite(string const & site); static bool ValidateEmail(string const & email); + // TODO dummy, should be removed. + static vector<string> const & GetMwmLanguages() + { + static vector<string> const kNativelanguagesForMwm = {"de", "fr"}; + + return kNativelanguagesForMwm; + } + // TODO dummy, should be removed. + + // Check whether langCode can be used as default name. + static bool CanUseAsDefaultName(int8_t const langCode, StringUtf8Multilang const & name, + vector<string> const & nativeMwmLanguages); + + // See comment for NamesDataSource class. + static NamesDataSource GetNamesDataSource(StringUtf8Multilang const & source, + vector<string> const & nativeMwmLanguages, + string 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..d281221726 100644 --- a/indexer/indexer_tests/editable_map_object_test.cpp +++ b/indexer/indexer_tests/editable_map_object_test.cpp @@ -125,4 +125,104 @@ UNIT_TEST(EditableMapObject_ValidateEmail) TEST(!EditableMapObject::ValidateEmail("emai@l_.ab"), ()); TEST(!EditableMapObject::ValidateEmail("email@e#$%&'*+-/=?^`_{}|~.com"), ()); } + +UNIT_TEST(EditableMapObject_CanUseAsDefaultName) +{ + EditableMapObject emo; + + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("de"), emo.GetName(), {"de", "fr"}), + ("Check possibility to use Mwm language code")); + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("fr"), emo.GetName(), {"de", "fr"}), + ("Check possibility to use Mwm language code")); + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("int_name"), emo.GetName(), {"de", "fr"}), + ("Check possibility to use international language code")); + + TEST(!EditableMapObject::CanUseAsDefaultName(100, emo.GetName(), {"de", "fr"}), + ("Incorrect language code is not supported")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("en"), emo.GetName(), {"abcd"}), + ("Incorrect Mwm language name is not supported")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("en"), emo.GetName(), {"de", "fr"}), + ("Can not to use language which not Mwm language or international")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("ru"), emo.GetName(), {"de", "fr"}), + ("Check possibility to use user`s language code")); + + // Trying to use language codes in reverse priority. + StringUtf8Multilang names; + names.AddString(StringUtf8Multilang::GetLangIndex("int_name"), "international name"); + emo.SetName(names); + + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("int_name"), emo.GetName(), {"de", "fr"}), + ("It is possible to fix typo for international language")); + + names.AddString(StringUtf8Multilang::GetLangIndex("fr"), "second mwm language"); + emo.SetName(names); + + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("fr"), emo.GetName(), {"de", "fr"}), + ("It is possible to fix typo")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("int_name"), emo.GetName(), {"de", "fr"}), + ("Name on language with high priority was already entered")); + + names.AddString(StringUtf8Multilang::GetLangIndex("de"), "first mwm language"); + emo.SetName(names); + + TEST(EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("de"), emo.GetName(), {"de", "fr"}), + ("It is possible to fix typo")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("fr"), emo.GetName(), {"de", "fr"}), + ("Name on language with high priority was already entered")); + TEST(!EditableMapObject::CanUseAsDefaultName(StringUtf8Multilang::GetLangIndex("int_name"), emo.GetName(), {"de", "fr"}), + ("Name on language with high priority was already entered")); +} + +UNIT_TEST(EditableMapObject_GetNamesDataSource) +{ + EditableMapObject emo; + StringUtf8Multilang names; + names.AddString(0, "Default name"); + names.AddString(1, "Eng name"); + names.AddString(7, "Int name"); + names.AddString(6, "De name"); + names.AddString(8, "Ru name"); + names.AddString(9, "Sv name"); + names.AddString(12, "By name"); + names.AddString(14, "Ko name"); + names.AddString(20, "It name"); + emo.SetName(names); + + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), {"de", "fr"}, "ko"); + + TEST(namesDataSource.names.size() == 9, ("All names except the default should be pushed into " + "data source plus empty mandatory names")); + TEST(namesDataSource.mandatoryNamesCount == 4, + ("Mandatory names count should be equal == Mwm languages + user`s language + international " + "language")); + TEST(namesDataSource.names[0].m_code == 6 /*de*/, + ("Deutsch name should be first as first language in Mwm")); + TEST(namesDataSource.names[1].m_code == 3 /*fr*/, + ("French name should be second as second language in Mwm")); + TEST(namesDataSource.names[2].m_code == 14 /*ko*/, + ("Korean name should be third because user`s language should be followed by Mwm languages")); + TEST(namesDataSource.names[3].m_code == 7 /*int*/, + ("International name should be fourth because International language should be followed by " + "user`s language")); + + { + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), {"int_name"}, "int_name"); + TEST(namesDataSource.names.size() == 8, + ("All names except the default should be pushed into data source")); + TEST(namesDataSource.mandatoryNamesCount == 1, + ("Mandatory names count should be equal == Mwm languages + user`s language + " + "international language. Excluding repetiton")); + } + { + auto const namesDataSource = + EditableMapObject::GetNamesDataSource(emo.GetName(), {"fr", "fr"}, "fr"); + TEST(namesDataSource.names.size() == 9, ("All names except the default should be pushed into " + "data source plus empty mandatory names")); + TEST(namesDataSource.mandatoryNamesCount == 2, + ("Mandatory names count should be equal == Mwm languages + user`s language + " + "international language. Excluding repetiton")); + } +} } // namespace diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp index b98f934c7c..6c9ac30912 100644 --- a/indexer/osm_editor.cpp +++ b/indexer/osm_editor.cpp @@ -426,6 +426,25 @@ bool Editor::IsCreatedFeature(FeatureID const & fid) return fid.m_index >= kStartIndexForCreatedFeatures; } +bool Editor::WasDefaultNameSaved(FeatureID const & fid) const +{ + if (IsCreatedFeature(fid)) + return false; + + if (FeatureStatus::Created == GetFeatureStatus(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; + } + + return originalFeaturePtr->HasName(); +} + /// 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 109b089600..d11ff810be 100644 --- a/indexer/osm_editor.hpp +++ b/indexer/osm_editor.hpp @@ -167,6 +167,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 in case when original feature from mwm has default name + bool WasDefaultNameSaved(FeatureID const & fid) const; private: // TODO(AlexZ): Synchronize Save call/make it on a separate thread. |