Try another way of implementing XMLFeature.

This commit is contained in:
Sergey Magidovich 2015-11-30 13:50:21 +03:00 committed by Sergey Yershov
parent 486aeb869c
commit 0e38939461
3 changed files with 158 additions and 154 deletions

View file

@ -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", ());
}

View file

@ -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

View file

@ -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