diff --git a/indexer/indexer_tests/xml_feature_test.cpp b/indexer/indexer_tests/xml_feature_test.cpp index 66ffc68fb2..cbe81ef914 100644 --- a/indexer/indexer_tests/xml_feature_test.cpp +++ b/indexer/indexer_tests/xml_feature_test.cpp @@ -25,63 +25,63 @@ using namespace indexer; -UNIT_TEST(FeatureToXml) -{ - XMLFeature feature; - feature.SetModificationTime(my::StringToTimestamp("2015-11-27T21:13:32Z")); +// UNIT_TEST(FeatureToXml) +// { +// XMLFeature feature; +// feature.SetModificationTime(my::StringToTimestamp("2015-11-27T21:13:32Z")); - feature.SetCenter({64.234234, 53.31242}); - feature.SetTagValue("opening_hours", "Mo-Fr 08:15-17:30"); +// feature.SetCenter({64.234234, 53.31242}); +// feature.SetTagValue("opening_hours", "Mo-Fr 08:15-17:30"); - feature.SetInterationalName("Gorki Park"); +// feature.SetInterationalName("Gorki Park"); - StringUtf8Multilang names; - names.AddString("en", "Gorki Park"); - names.AddString("ru", "Парк Горького"); +// StringUtf8Multilang names; +// names.AddString("en", "Gorki Park"); +// names.AddString("ru", "Парк Горького"); - feature.SetMultilungName(names); +// feature.SetMultilungName(names); - StringNumericOptimal house; - house.Set(10); - feature.SetHouse(house); +// StringNumericOptimal house; +// house.Set(10); +// feature.SetHouse(house); - pugi::xml_document document; - feature.ToXMLDocument(document); +// pugi::xml_document document; +// feature.ToXMLDocument(document); - stringstream sstr; - document.save(sstr, "\t", pugi::format_indent_attributes); +// stringstream sstr; +// document.save(sstr, "\t", pugi::format_indent_attributes); - auto const expectedString = R"( - - - - - - - -)"; +// auto const expectedString = R"( +// +// +// +// +// +// +// +// )"; - TEST_EQUAL(sstr.str(), expectedString, ()); -} +// TEST_EQUAL(sstr.str(), expectedString, ()); +// } UNIT_TEST(FeatureFromXml) { auto const srcString = R"( )"; - pugi::xml_document srcDocument; - srcDocument.load_string(srcString); - auto const feature = XMLFeature::FromXMLDocument(srcDocument); + XMLFeature feature(srcString); - pugi::xml_document dstDocument; - TEST(feature.ToXMLDocument(dstDocument), ()); stringstream sstr; - dstDocument.save(sstr, "\t", pugi::format_indent_attributes); + feature.GetXMLDocument().save(sstr, "\t", pugi::format_indent_attributes); + TEST_EQUAL(srcString, sstr.str(), ()); - TEST_EQUAL(sstr.str(), srcString, ()); + TEST(feature.HasTag("opening_hours"), ()); + TEST(!feature.HasTag("FooBarBaz"), ()); + + TEST_EQUAL(feature.GetHouse(), "10", ()); + TEST_EQUAL(feature.GetCenter(), m2::PointD(64.2342340, 53.3124200), ()); + + TEST_EQUAL(feature.GetName(), "Gorki Park", ()); + TEST_EQUAL(feature.GetName("default"), "Gorki Park", ()); + TEST_EQUAL(feature.GetName("en"), "Gorki Park", ()); + TEST_EQUAL(feature.GetName("ru"), "Парк Горького", ()); + TEST_EQUAL(feature.GetName("No such language"), "", ()); + + TEST_EQUAL(feature.GetTagValue("opening_hours"), "Mo-Fr 08:15-17:30", ()); + TEST_EQUAL(my::TimestampToString(feature.GetModificationTime()), "2015-11-27T21:13:32Z", ()); } diff --git a/indexer/xml_feature.cpp b/indexer/xml_feature.cpp index f01ea6d598..54b23f56a0 100644 --- a/indexer/xml_feature.cpp +++ b/indexer/xml_feature.cpp @@ -5,13 +5,15 @@ #include "std/cstring.hpp" +#include "3party/pugixml/src/pugixml.hpp" + namespace { string ToString(m2::PointD const & p) { ostringstream out; - out.precision(20); + out.precision(7); out << p.x << ", " << p.y; return out.str(); } @@ -34,17 +36,24 @@ void AddTag(string const & key, string const & value, pugi::xml_node & node) tag.append_attribute("k") = key.data(); tag.append_attribute("v") = value.data(); } + +pugi::xpath_node FindTag(pugi::xml_document const & document, string const & key) +{ + return document.select_node(("//tag[@k='" + key + "']").data()); } +} // namespace -#include namespace indexer { -XMLFeature XMLFeature::FromXMLDocument(pugi::xml_document const & document) -{ - XMLFeature feature; +XMLFeature::XMLFeature(): m_documentPtr(new pugi::xml_document) {} - auto const & node = document.child("node"); +XMLFeature::XMLFeature(string const & xml): + XMLFeature() +{ + m_documentPtr->load(xml.data()); + + auto const node = m_documentPtr->child("node"); if (!node) MYTHROW(XMLFeatureError, ("Document has no node")); @@ -55,57 +64,78 @@ XMLFeature XMLFeature::FromXMLDocument(pugi::xml_document const & document) m2::PointD center; if (!FromString(attr.value(), center)) MYTHROW(XMLFeatureError, ("Can't parse center attribute: " + string(attr.value()))); - feature.SetCenter(center); if (!(attr = node.attribute("timestamp"))) MYTHROW(XMLFeatureError, ("Node has no timestamp attribute")); - feature.SetModificationTime(my::StringToTimestamp(attr.value())); - - for (auto const tag : node.children()) - { - auto const tagName = tag.attribute("k").value(); - auto const tagValue = tag.attribute("v").value(); - if (strings::StartsWith(tagName, "name::")) - { - auto const lang = tagName + strlen("name::"); - feature.m_name.AddString(lang, tagValue); - } - else if (strcmp("name", tagName) == 0) - { - feature.SetInterationalName(tagValue); - } - else if (strcmp("addr::housenumber", tagName) == 0) - { - feature.m_house.Set(tagValue); - } - else - { - feature.m_tags[tagName] = tagValue; - } - } - - return feature; } -bool XMLFeature::ToXMLDocument(pugi::xml_document & document) const +pugi::xml_document const & XMLFeature::GetXMLDocument() const { - auto node = document.append_child("node"); - node.append_attribute("center") = ToString(GetCenter()).data(); - node.append_attribute("timestamp") = my::TimestampToString(GetModificationTime()).data(); - - AddTag("name", GetInternationalName(), node); - - GetMultilangName().ForEachRef([&node](int8_t lang, string const & name) { - AddTag("name::" + string(StringUtf8Multilang::GetLangByCode(lang)), name, node); - return true; - }); - - if (!GetHouse().Get().empty()) - AddTag("addr:housenumber", GetHouse().Get(), node); - - for (auto const & tag : m_tags) - AddTag(tag.first, tag.second, node); - - return true; + return *m_documentPtr; } + +m2::PointD XMLFeature::GetCenter() const +{ + auto const node = m_documentPtr->child("node"); + m2::PointD center; + FromString(node.attribute("center").value(), center); + return center; +} + +string const XMLFeature::GetName(string const & lang) const +{ + auto const suffix = lang == "default" || lang.empty() ? "" : "::" + lang; + return GetTagValue("name" + suffix); +} + +string const XMLFeature::GetName(uint8_t const langCode) const +{ + return GetName(StringUtf8Multilang::GetLangByCode(langCode)); +} + +string const XMLFeature::GetHouse() const +{ + return GetTagValue("addr:housenumber"); +} + +time_t XMLFeature::GetModificationTime() const +{ + auto const node = m_documentPtr->child("node"); + return my::StringToTimestamp(node.attribute("timestamp").value()); +} + +bool XMLFeature::HasTag(string const & key) const +{ + return FindTag(*m_documentPtr, key); +} + +string XMLFeature::GetTagValue(string const & key) const +{ + auto const tag = FindTag(*m_documentPtr, key); + return tag.node().attribute("v").value(); +} + +// bool XMLFeature::ToXMLDocument(pugi::xml_document & document) const +// { +// auto node = document.append_child("node"); +// node.append_attribute("center") = ToString(GetCenter()).data(); +// node.append_attribute("timestamp") = my::TimestampToString(GetModificationTime()).data(); + +// AddTag("name", GetInternationalName(), node); + +// GetMultilangName().ForEachRef([&node](int8_t lang, string const & name) { +// AddTag("name::" + string(StringUtf8Multilang::GetLangByCode(lang)), name, node); +// return true; +// }); + +// if (!GetHouse().Get().empty()) +// AddTag("addr:housenumber", GetHouse().Get(), node); + +// for (auto const & tag : m_tags) +// AddTag(tag.first, tag.second, node); + +// return true; +// } + + } // namespace indexer diff --git a/indexer/xml_feature.hpp b/indexer/xml_feature.hpp index f6e0425960..08bfe448db 100644 --- a/indexer/xml_feature.hpp +++ b/indexer/xml_feature.hpp @@ -7,77 +7,41 @@ #include "std/ctime.hpp" #include "std/map.hpp" +#include "std/unique_ptr.hpp" -#include "3party/pugixml/src/pugixml.hpp" + +namespace pugi +{ +class xml_document; +} namespace indexer { -class FeatureTags -{ -public: - bool HasKey(string const & key) const { return m_data.find(key) == end(m_data); } - - string GetValue(string const & key) const { return HasKey(key) ? m_data.find(key)->second : ""; } - - void SetValue(string const key, string const & value) { m_data[key] = value; } - - template - T GetValue(string const & key) const; - - template - void SetValue(string const key, T const & value); - -private: - map m_data; -}; DECLARE_EXCEPTION(XMLFeatureError, RootException); class XMLFeature { public: - XMLFeature() = default; - static XMLFeature FromXMLDocument(pugi::xml_document const & document); + XMLFeature(); + XMLFeature(string const & xml); - bool ToXMLDocument(pugi::xml_document & document) const; + pugi::xml_document const & GetXMLDocument() const; - m2::PointD GetCenter() const { return m_center; } - void SetCenter(m2::PointD const & center) { m_center = center; } + m2::PointD GetCenter() const; - string const & GetInternationalName() const { return m_internationalName; } - void SetInterationalName(string const & name) { m_internationalName = name; } + string const GetName(string const & lang = "") const; + string const GetName(uint8_t const langCode) const; - StringUtf8Multilang const & GetMultilangName() const { return m_name; } - void SetMultilungName(StringUtf8Multilang const & name) { m_name = name; } + string const GetHouse() const; - StringNumericOptimal const & GetHouse() const { return m_house; } - void SetHouse(StringNumericOptimal const & house) { m_house = house; } + time_t GetModificationTime() const; - time_t GetModificationTime() const { return m_timestamp; } - void SetModificationTime(time_t const timestamp) { m_timestamp = timestamp; } + bool HasTag(string const & key) const; - bool HasTag(string const & key) const { return m_tags.find(key) == end(m_tags); } - - string GetTagValue(string const & key) const { - return HasTag(key) ? m_tags.find(key)->second : ""; - } - - void SetTagValue(string const key, string const & value) { m_tags[key] = value; } + string GetTagValue(string const & key) const; private: - m2::PointD m_center; - - string m_internationalName; - StringUtf8Multilang m_name; - StringNumericOptimal m_house; - - // TODO(mgsergio): It could be useful to have separate class - // for this - // uint32_t m_types[m_maxTypesCount]; - - // string version; // Duno, may prove useful - - time_t m_timestamp; - map m_tags; + unique_ptr m_documentPtr; }; } // namespace indexer