From 8c08017809990f335e404394537c79cbc830bf02 Mon Sep 17 00:00:00 2001 From: Alex Zolotarev Date: Mon, 21 Dec 2015 20:03:04 +0300 Subject: [PATCH] Base editor serialization. --- editor/xml_feature.cpp | 58 ++++++++++++++++++++++++++++++++++-------- editor/xml_feature.hpp | 20 +++++++++++---- indexer/osm_editor.cpp | 50 +++++++++++++++++++++++++++++++++--- qt/qt.pro | 2 +- 4 files changed, 110 insertions(+), 20 deletions(-) diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp index eb980ac1fe..e0beeef5ce 100644 --- a/editor/xml_feature.cpp +++ b/editor/xml_feature.cpp @@ -16,7 +16,12 @@ namespace { -auto constexpr kLatLonTolerance = 7; +constexpr int const kLatLonTolerance = 7; +constexpr char const * kTimestamp = "timestamp"; +constexpr char const * kUploadTimestamp = "upload_timestamp"; +constexpr char const * kUploadStatus = "upload_status"; +constexpr char const * kUploadError = "upload_error"; +constexpr char const * kHouseNumber = "addr:housenumber"; pugi::xml_node FindTag(pugi::xml_document const & document, string const & key) { @@ -47,20 +52,16 @@ void ValidateNode(pugi::xml_node const & node) // Check if point can be parsed. Throws if it's can't. UNUSED_VALUE(PointFromLatLon(node)); - if (!node.attribute("timestamp")) + if (!node.attribute(kTimestamp)) MYTHROW(editor::XMLFeatureNoTimestampError, ("Node has no timestamp attribute")); } } // namespace namespace editor { -char const * const XMLFeature::kLastModified = "timestamp"; -char const * const XMLFeature::kHouseNumber = "addr:housenumber"; -char const * const XMLFeature::kDefaultName = "name"; -char const * const XMLFeature::kLocalName = "name:"; + char const * const XMLFeature::kDefaultLang = StringUtf8Multilang::GetLangByCode(StringUtf8Multilang::DEFAULT_CODE); -; XMLFeature::XMLFeature() { @@ -107,7 +108,7 @@ void XMLFeature::SetCenter(m2::PointD const & mercatorCenter) string XMLFeature::GetName(string const & lang) const { auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang; - return GetTagValue("name" + suffix); + return GetTagValue(kDefaultName + suffix); } string XMLFeature::GetName(uint8_t const langCode) const @@ -123,7 +124,7 @@ void XMLFeature::SetName(string const & name) void XMLFeature::SetName(string const & lang, string const & name) { auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang; - SetTagValue("name" + suffix, name); + SetTagValue(kDefaultName + suffix, name); } void XMLFeature::SetName(uint8_t const langCode, string const & name) @@ -143,12 +144,42 @@ void XMLFeature::SetHouse(string const & house) time_t XMLFeature::GetModificationTime() const { - return my::StringToTimestamp(GetRootNode().attribute("timestamp").value()); + return my::StringToTimestamp(GetRootNode().attribute(kTimestamp).value()); } void XMLFeature::SetModificationTime(time_t const time) { - SetAttribute(kLastModified, my::TimestampToString(time)); + SetAttribute(kTimestamp, my::TimestampToString(time)); +} + +time_t XMLFeature::GetUploadTime() const +{ + return my::StringToTimestamp(GetRootNode().attribute(kUploadTimestamp).value()); +} + +void XMLFeature::SetUploadTime(time_t const time) +{ + SetAttribute(kUploadTimestamp, my::TimestampToString(time)); +} + +string XMLFeature::GetUploadStatus() const +{ + return GetRootNode().attribute(kUploadStatus).value(); +} + +void XMLFeature::SetUploadStatus(string const & status) +{ + SetAttribute(kUploadStatus, status); +} + +string XMLFeature::GetUploadError() const +{ + return GetRootNode().attribute(kUploadError).value(); +} + +void XMLFeature::SetUploadError(string const & error) +{ + SetAttribute(kUploadError, error); } bool XMLFeature::HasTag(string const & key) const @@ -210,4 +241,9 @@ pugi::xml_node XMLFeature::GetRootNode() { return m_document.child("node"); } + +bool XMLFeature::AttachToParentNode(pugi::xml_node parent) const +{ + return !parent.append_copy(GetRootNode()).empty(); +} } // namespace editor diff --git a/editor/xml_feature.hpp b/editor/xml_feature.hpp index da7f12a011..e42da72232 100644 --- a/editor/xml_feature.hpp +++ b/editor/xml_feature.hpp @@ -21,10 +21,8 @@ DECLARE_EXCEPTION(XMLFeatureNoHeaderError, XMLFeatureError); class XMLFeature { - static char const * const kLastModified; - static char const * const kHouseNumber; - static char const * const kDefaultName; - static char const * const kLocalName; + static constexpr char const * kDefaultName = "name"; + static constexpr char const * kLocalName = "name:"; static char const * const kDefaultLang; public: @@ -67,8 +65,18 @@ public: string GetHouse() const; void SetHouse(string const & house); + /// Our and OSM modification time are equal. time_t GetModificationTime() const; - void SetModificationTime(time_t const time = ::time(nullptr)); + void SetModificationTime(time_t const time); + + time_t GetUploadTime() const; + void SetUploadTime(time_t const time); + + string GetUploadStatus() const; + void SetUploadStatus(string const & status); + + string GetUploadError() const; + void SetUploadError(string const & error); bool HasTag(string const & key) const; bool HasAttribute(string const & key) const; @@ -86,6 +94,8 @@ public: string GetAttribute(string const & key) const; void SetAttribute(string const & key, string const & value); + bool AttachToParentNode(pugi::xml_node parent) const; + private: pugi::xml_node const GetRootNode() const; pugi::xml_node GetRootNode(); diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp index 38ffff9545..483949cd19 100644 --- a/indexer/osm_editor.cpp +++ b/indexer/osm_editor.cpp @@ -5,6 +5,8 @@ #include "platform/platform.hpp" +#include "editor/xml_feature.hpp" + #include "base/logging.hpp" #include "std/map.hpp" @@ -16,7 +18,11 @@ using namespace pugi; using feature::EGeomType; using feature::Metadata; -static char constexpr const * kEditorXMLFileName = "edits.xml"; +constexpr char const * kEditorXMLFileName = "edits.xml"; +constexpr char const * kXmlRootNode = "mapsme"; +constexpr char const * kDeleteSection = "delete"; +constexpr char const * kModifySection = "modify"; +constexpr char const * kCreateSection = "create"; namespace osm { @@ -53,9 +59,47 @@ void Editor::Load(string const & fullFilePath) // TODO(mgsergio): Implement XML deserialization into m_features. } -void Editor::Save(string const & /*fullFilePath*/) const +void Editor::Save(string const & fullFilePath) const { - // TODO(mgsergio): Implement XML serialization from m_features. + // Should we delete edits file if user has canceled all changes? + if (m_features.empty()) + return; + + xml_document doc; + xml_node root = doc.append_child(kXmlRootNode); + // Use format_version for possible future format changes. + root.append_attribute("format_version") = 1; + for (auto const & mwm : m_features) + { + xml_node mwmNode = root.append_child("mwm"); + mwmNode.append_attribute("name") = mwm.first.GetInfo()->GetCountryName().c_str(); + mwmNode.append_attribute("version") = mwm.first.GetInfo()->GetVersion(); + xml_node deleted = mwmNode.append_child(kDeleteSection); + xml_node modified = mwmNode.append_child(kModifySection); + xml_node created = mwmNode.append_child(kCreateSection); + for (auto const & offset : mwm.second) + { + FeatureTypeInfo const & fti = offset.second; + editor::XMLFeature xf = fti.m_feature.ToXML(); + xf.SetModificationTime(fti.m_modificationTimestamp); + if (fti.m_uploadAttemptTimestamp) + { + xf.SetUploadTime(fti.m_uploadAttemptTimestamp); + xf.SetUploadStatus(fti.m_uploadStatus); + xf.SetUploadError(fti.m_uploadError); + } + switch (fti.m_status) + { + case EDeleted: VERIFY(xf.AttachToParentNode(deleted), ()); break; + case EModified: VERIFY(xf.AttachToParentNode(modified), ()); break; + case ECreated: VERIFY(xf.AttachToParentNode(created), ()); break; + case EUntouched: CHECK(false, ("Not edited features shouldn't be here.")); + } + } + } + + if (doc && !doc.save_file(fullFilePath.c_str(), " ")) + LOG(LERROR, ("Can't save map edits into", fullFilePath)); } Editor::FeatureStatus Editor::GetFeatureStatus(MwmSet::MwmId const & mwmId, uint32_t offset) const diff --git a/qt/qt.pro b/qt/qt.pro index 319bd5aae2..608f439f96 100644 --- a/qt/qt.pro +++ b/qt/qt.pro @@ -1,6 +1,6 @@ # Main application in qt. ROOT_DIR = .. -DEPENDENCIES = map drape_frontend routing search storage indexer drape platform geometry coding base \ +DEPENDENCIES = map drape_frontend routing search storage indexer drape platform editor geometry coding base \ freetype expat fribidi tomcrypt jansson protobuf osrm stats_client minizip succinct pugixml