forked from organicmaps/organicmaps
Try another way of implementing XMLFeature.
This commit is contained in:
parent
486aeb869c
commit
0e38939461
3 changed files with 158 additions and 154 deletions
|
@ -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"(<?xml version="1.0"?>
|
||||
<node
|
||||
center="64.234234000000000719, 53.312420000000003029"
|
||||
timestamp="2015-11-27T21:13:32Z">
|
||||
<tag
|
||||
k="name"
|
||||
v="Gorki Park" />
|
||||
<tag
|
||||
k="name::en"
|
||||
v="Gorki Park" />
|
||||
<tag
|
||||
k="name::ru"
|
||||
v="Парк Горького" />
|
||||
<tag
|
||||
k="addr:housenumber"
|
||||
v="10" />
|
||||
<tag
|
||||
k="opening_hours"
|
||||
v="Mo-Fr 08:15-17:30" />
|
||||
</node>
|
||||
)";
|
||||
// auto const expectedString = R"(<?xml version="1.0"?>
|
||||
// <node
|
||||
// center="64.234234000000000719, 53.312420000000003029"
|
||||
// timestamp="2015-11-27T21:13:32Z">
|
||||
// <tag
|
||||
// k="name"
|
||||
// v="Gorki Park" />
|
||||
// <tag
|
||||
// k="name::en"
|
||||
// v="Gorki Park" />
|
||||
// <tag
|
||||
// k="name::ru"
|
||||
// v="Парк Горького" />
|
||||
// <tag
|
||||
// k="addr:housenumber"
|
||||
// v="10" />
|
||||
// <tag
|
||||
// k="opening_hours"
|
||||
// v="Mo-Fr 08:15-17:30" />
|
||||
// </node>
|
||||
// )";
|
||||
|
||||
TEST_EQUAL(sstr.str(), expectedString, ());
|
||||
}
|
||||
// TEST_EQUAL(sstr.str(), expectedString, ());
|
||||
// }
|
||||
|
||||
|
||||
UNIT_TEST(FeatureFromXml)
|
||||
{
|
||||
auto const srcString = R"(<?xml version="1.0"?>
|
||||
<node
|
||||
center="64.234234000000000719, 53.312420000000003029"
|
||||
center="64.2342340, 53.3124200"
|
||||
timestamp="2015-11-27T21:13:32Z">
|
||||
<tag
|
||||
k="name"
|
||||
|
@ -101,14 +101,24 @@ UNIT_TEST(FeatureFromXml)
|
|||
</node>
|
||||
)";
|
||||
|
||||
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", ());
|
||||
}
|
||||
|
|
|
@ -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 <iostream>
|
||||
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
|
||||
|
|
|
@ -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 <typename T>
|
||||
T GetValue(string const & key) const;
|
||||
|
||||
template <typename T>
|
||||
void SetValue(string const key, T const & value);
|
||||
|
||||
private:
|
||||
map<string, string> 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<string, string> m_tags;
|
||||
unique_ptr<pugi::xml_document> m_documentPtr;
|
||||
};
|
||||
} // namespace indexer
|
||||
|
|
Loading…
Add table
Reference in a new issue