Medusa  1.1
Coordinate Free Mehless Method implementation
XML.cpp
Go to the documentation of this file.
1 #include <medusa/bits/io/XML.hpp>
2 #include <rapidxml/rapidxml.hpp>
3 #include <rapidxml/rapidxml_print.hpp>
5 #include <fstream>
6 #include <iostream>
7 #include <cstring>
8 
14 namespace mm {
15 
16 void XML::loadFileHelper(const std::string& file) {
17  std::ifstream ifs(file);
18  assert_msg(ifs.good(), "Failed opening file '%s' with error: %s.", file, strerror(errno));
19  ifs.seekg(0, std::ios::end);
20  std::size_t length = ifs.tellg();
21  ifs.seekg(0, std::ios::beg);
22  file_contents.resize(length+1, '\0');
23  ifs.read(file_contents.data(), length);
24  ifs.close();
26 }
27 
29  try {
30  doc.parse<0>(file_contents.data());
31  } catch (const rapidxml::parse_error& e) {
32  std::cerr << "Error parsing file contents '"
33  << static_cast<const char*>(file_contents.data()) << "'." << std::endl;
34  throw e;
35  }
36 }
37 
38 XML::XML(const std::string& filename) { loadFileHelper(filename); }
39 XML::XML(const XML& xml) {
40  rapidxml::print(std::back_inserter(file_contents), xml.doc);
41  file_contents.push_back('\0');
43 }
44 void XML::load(const std::string& filename) { loadFileHelper(filename); }
45 rapidxml::xml_document<char>& XML::documentRoot() { return doc; }
46 const rapidxml::xml_document<char>& XML::documentRoot() const { return doc; }
47 
49 std::ostream& operator<<(std::ostream& os, const XML& xml) {
50  os << "XML document: '\n";
51  rapidxml::print(os, xml.doc, 0);
52  return os << '\'';
53 }
54 
55 std::pair<std::vector<std::string>, std::string> XML::splitPath(std::string path) {
56  assert_msg(!path.empty(), "Path name should not be empty.");
57  std::size_t last_dot_idx = path.find_last_of('.');
58  if (last_dot_idx == std::string::npos) {
59  return {{}, path};
60  }
61  std::string attribute_name = path.substr(last_dot_idx+1);
62  assert_msg(!attribute_name.empty(), "Path '%s' contains an empty attribute name.", path);
63 
64  path = path.substr(0, last_dot_idx);
65  std::vector<std::string> split_path;
66  size_t idx = 0;
67  do {
68  std::size_t new_idx = path.find('.', idx);
69  if (new_idx == std::string::npos) { new_idx = path.size(); }
70  assert_msg(new_idx - idx > 0, "Path '%s' contains an empty element name.", path);
71  split_path.push_back(path.substr(idx, new_idx-idx));
72  idx = new_idx + 1;
73  } while (idx <= path.size());
74  return {split_path, attribute_name};
75 }
76 
77 std::string XML::join(const std::vector<std::string>& path) {
78  if (path.empty()) { return ""; }
79  std::string r = path[0];
80  for (std::string::size_type i = 1; i < path.size(); ++i) {
81  r += '.';
82  r += path[i];
83  }
84  return r;
85 }
86 
87 char* XML::getString(const std::vector<std::string>& path,
88  const std::string& attribute_name) const {
89  rapidxml::xml_node<char>* root = doc.first_node();
90  for (const auto& name : path) {
91  root = root->first_node(name.c_str(), name.size());
92  assert_msg(root != nullptr, "Element '%s' from path '%s' not found.",
93  name, join(path));
94  }
95  auto* attr = root->first_attribute(attribute_name.c_str(), attribute_name.size());
96  assert_msg(attr != nullptr, "Attribute '%s', specified by path '%s' not found.",
97  attribute_name, join(path));
98  return attr->value();
99 }
100 
101 void XML::setString(const std::vector<std::string>& path, const std::string& attribute_name,
102  const std::string& content) {
103  rapidxml::xml_node<char>* root = doc.first_node();
104  for (const auto& name : path) {
105  auto* child = root->first_node(name.c_str(), name.size());
106  if (child == nullptr) {
107  char* node_name = doc.allocate_string(name.c_str());
108  child = doc.allocate_node(rapidxml::node_element, node_name);
109  root->append_node(child);
110  }
111  root = child;
112  }
113  auto* attr = root->first_attribute(attribute_name.c_str(), attribute_name.size());
114  if (attr == nullptr) {
115  char* attr_name = doc.allocate_string(attribute_name.c_str());
116  attr = doc.allocate_attribute(attr_name);
117  root->append_attribute(attr);
118  }
119  assert_msg(attr != nullptr, "Attribute '%s' not found on path '%s'.",
120  attribute_name, join(path));
121  char* attr_content = doc.allocate_string(content.c_str());
122  attr->value(attr_content);
123 }
124 
126 template <>
127 std::string XML::get<std::string>(const std::string& path) const {
128  std::vector<std::string> path_elements;
129  std::string attribute_name;
130  std::tie(path_elements, attribute_name) = splitPath(path);
131  return getString(path_elements, attribute_name);
132 }
133 
134 template <>
135 void XML::set<std::string>(const std::string& path, const std::string& value, bool overwrite) {
136  std::vector<std::string> path_elements;
137  std::string attribute_name;
138  std::tie(path_elements, attribute_name) = splitPath(path);
139  if (!overwrite) {
140  assert_msg(!exists(path), "Attribute on path '%s' already exists with value '%s'. "
141  "Set overwrite=true to overwrite its value.",
142  path, getString(path_elements, attribute_name));
143  }
144  setString(path_elements, attribute_name, value);
145 }
147 
148 std::vector<std::pair<std::string, std::string>> XML::getAll() const {
149  std::vector<std::pair<std::string, std::string>> all_attributes;
150  getAllRecursive(doc.first_node(), "", all_attributes);
151  return all_attributes;
152 }
153 
154 void XML::getAllRecursive(const rapidxml::xml_node<>* node, std::string path,
155  std::vector<std::pair<std::string, std::string>>& all_attr) const {
156  for (const auto* a = node->first_attribute(); a; a = a->next_attribute()) {
157  all_attr.emplace_back(path+a->name(), a->value());
158  }
159  for (const rapidxml::xml_node<>* n = node->first_node(); n; n = n->next_sibling()) {
160  getAllRecursive(n, path + n->name() + ".", all_attr);
161  }
162 }
163 
164 bool XML::exists(const std::string& path) const {
165  rapidxml::xml_node<char>* root = doc.first_node();
166  std::vector<std::string> path_elements;
167  std::string attribute_name;
168  std::tie(path_elements, attribute_name) = splitPath(path);
169  for (const auto& element_name : path_elements) {
170  root = root->first_node(element_name.c_str(), element_name.size());
171  if (!root) return false;
172  }
173  auto* attr = root->first_attribute(attribute_name.c_str(), attribute_name.size());
174  return attr != nullptr;
175 }
176 
177 } // namespace mm
XML.hpp
mm
Root namespace for the whole library.
Definition: Gaussian.hpp:14
mm::XML::loadFromStoredContents
void loadFromStoredContents()
Loads the XML document from a stored null-terminated file_contents.
Definition: XML.cpp:28
mm::XML::getAll
std::vector< std::pair< std::string, std::string > > getAll() const
Returns all pairs (path, attribute_value).
Definition: XML.cpp:148
mm::XML::XML
XML()=default
Creates an XML reader linked to no document.
mm::operator<<
std::ostream & operator<<(std::ostream &os, const Gaussian< S > &b)
Output basic information about given Gaussian RBF.
Definition: Gaussian.hpp:37
assert_msg
#define assert_msg(cond,...)
Assert with better error reporting.
Definition: assert.hpp:75
mm::XML::loadFileHelper
void loadFileHelper(const std::string &file)
Function for opening files, called by XML::XML(const std::string&) and XML::load().
Definition: XML.cpp:16
mm::XML::getString
char * getString(const std::vector< std::string > &path, const std::string &attribute_name) const
Reads the contents of the attribute specified by path as a string.
Definition: XML.cpp:87
mm::XML::getAllRecursive
void getAllRecursive(const rapidxml::xml_node<> *node, std::string path, std::vector< std::pair< std::string, std::string >> &all_attr) const
Fills the all_attr array with all pairs (path, value) pairs that are descendants of node node at path...
Definition: XML.cpp:154
assert.hpp
mm::XML::documentRoot
rapidxml::xml_document< char > & documentRoot()
Access to underlying XML root element from RapidXml library.
Definition: XML.cpp:45
mm::XML::splitPath
static std::pair< std::vector< std::string >, std::string > splitPath(std::string path)
Splits dot separated path into elements path and attribute name.
Definition: XML.cpp:55
mm::XML
Class for reading and storing values to XML files.
Definition: XML_fwd.hpp:40
mm::XML::setString
void setString(const std::vector< std::string > &path, const std::string &attribute_name, const std::string &content)
Writes the contents of string content to the attribute specified by path.
Definition: XML.cpp:101
mm::XML::file_contents
std::vector< char > file_contents
Whole XML file contents.
Definition: XML_fwd.hpp:41
mm::XML::exists
bool exists(const std::string &path) const
Returns true if the an attribute specified by path exists and false otherwise.
Definition: XML.cpp:164
mm::XML::doc
rapidxml::xml_document< char > doc
XML document root.
Definition: XML_fwd.hpp:42
mm::XML::join
static std::string join(const std::vector< std::string > &path)
Join a path into dot separated string.
Definition: XML.cpp:77
mm::XML::load
void load(const std::string &filename)
Loads XML document from a file given by filename.
Definition: XML.cpp:44