#include #include #include #include #include #include std::string xml_escape(const std::string& data) { std::string res; res.reserve(data.size()); for (size_t pos = 0; pos != data.size(); ++pos) { switch(data[pos]) { case '&': res += "&"; break; case '<': res += "<"; break; case '>': res += ">"; break; case '\"': res += """; break; case '\'': res += "'"; break; default: res += data[pos]; break; } } return res; } std::string xml_unescape(const std::string& data) { std::string res; res.reserve(data.size()); const char* str = data.c_str(); while (str && *str && static_cast(str - data.c_str()) < data.size()) { if (*str == '&') { if (strncmp(str+1, "amp;", 4) == 0) { res += "&"; str += 4; } else if (strncmp(str+1, "lt;", 3) == 0) { res += "<"; str += 3; } else if (strncmp(str+1, "gt;", 3) == 0) { res += ">"; str += 3; } else if (strncmp(str+1, "quot;", 5) == 0) { res += "\""; str += 5; } else if (strncmp(str+1, "apos;", 5) == 0) { res += "'"; str += 5; } else res += "&"; } else res += *str; str++; } return res; } XmlNode::XmlNode(const std::string& name, XmlNode* parent): name(name), parent(parent), closed(false) { } XmlNode::XmlNode(const std::string& name): XmlNode(name, nullptr) { } XmlNode::~XmlNode() { this->delete_all_children(); } void XmlNode::delete_all_children() { for (auto& child: this->children) { delete child; } this->children.clear(); } void XmlNode::set_attribute(const std::string& name, const std::string& value) { this->attributes[name] = value; } void XmlNode::set_tail(const std::string& data) { this->tail = xml_escape(data); } void XmlNode::add_to_tail(const std::string& data) { this->tail += xml_escape(data); } void XmlNode::set_inner(const std::string& data) { this->inner = xml_escape(data); } void XmlNode::add_to_inner(const std::string& data) { this->inner += xml_escape(data); } std::string XmlNode::get_inner() const { return xml_unescape(this->inner); } std::string XmlNode::get_tail() const { return xml_unescape(this->tail); } XmlNode* XmlNode::get_child(const std::string& name) const { for (auto& child: this->children) { if (child->name == name) return child; } return nullptr; } XmlNode* XmlNode::add_child(XmlNode* child) { child->parent = this; this->children.push_back(child); return child; } XmlNode* XmlNode::add_child(XmlNode&& child) { XmlNode* new_node = new XmlNode(std::move(child)); return this->add_child(new_node); } XmlNode* XmlNode::get_last_child() const { return this->children.back(); } void XmlNode::close() { if (this->closed) throw std::runtime_error("Closing an already closed XmlNode"); this->closed = true; } XmlNode* XmlNode::get_parent() const { return this->parent; } const std::string XmlNode::get_name() const { const std::vector splited = utils::split(this->name, ':', false); if (splited.empty()) return ""; const std::string res = splited.back(); return res; } std::string XmlNode::to_string() const { std::string res("<"); res += this->name; for (const auto& it: this->attributes) res += " " + utils::remove_invalid_xml_chars(it.first) + "='" + utils::remove_invalid_xml_chars(it.second) + "'"; if (this->closed && !this->has_children() && this->inner.empty()) res += "/>"; else { res += ">" + utils::remove_invalid_xml_chars(this->inner); for (const auto& child: this->children) res += child->to_string(); if (this->closed) { res += "name + ">"; } } res += utils::remove_invalid_xml_chars(this->tail); return res; } bool XmlNode::has_children() const { return !this->children.empty(); } const std::string XmlNode::get_tag(const std::string& name) const { try { const auto& value = this->attributes.at(name); return value; } catch (const std::out_of_range& e) { return ""; } } std::string& XmlNode::operator[](const std::string& name) { return this->attributes[name]; }