RapidXmlReader.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#pragma once
26
27#include <experimental/source_location>
28#include <fstream>
29#include <functional>
30#include <memory>
31#include <streambuf>
32#include <string>
33
41
43{
44 class RapidXmlReaderException : public armarx::LocalException
45 {
46 public:
47 RapidXmlReaderException(const std::string& message) noexcept :
48 armarx::LocalException(message)
49 {
50 }
51
52 ~RapidXmlReaderException() noexcept override
53 {
54 }
55
56 std::string
57 name() const override
58 {
59 return "armarx::exceptions::local::RapidXmlWrapperException";
60 }
61 };
62} // namespace armarx::exceptions::local
63
64namespace armarx
65{
66 class RapidXmlReader;
67 using RapidXmlReaderPtr = std::shared_ptr<RapidXmlReader>;
68
69 class RapidXmlReaderNode
70 {
71 friend class RapidXmlReader;
72
73 private:
75 std::shared_ptr<rapidxml::xml_document<>> doc;
76 std::shared_ptr<char[]> cstr;
77
78 RapidXmlReaderNode(rapidxml::xml_node<>* node,
79 std::shared_ptr<rapidxml::xml_document<>> doc,
80 std::shared_ptr<char[]> cstr) :
81 node(node), doc(doc), cstr(cstr)
82 {
83 }
84
85 void
86 check(const std::string& msg = "",
87 const std::experimental::source_location& location = {}) const
88 {
90 if (!node)
91 {
92 std::stringstream str;
93 str << "RapidXmlWrapper NullPointerException\n"
94 << "from " << location.file_name() << ':' << location.line() << " ("
95 << location.function_name() << ")\nTrace:\n";
96
98 str << "\nmsg: " << msg;
99 throw exceptions::local::RapidXmlReaderException(str.str());
100 }
101 }
102
103 char*
104 get_attrib_value(const char* attrName) const
105 {
107 check();
108 rapidxml::xml_attribute<>* attrib = node->first_attribute(attrName);
109
110 if (!attrib)
111 {
112 throw exceptions::local::RapidXmlReaderException(
113 std::string("Attribute '") + attrName + "' does not exist in node " +
114 getPath());
115 }
116
117 return attrib->value();
118 }
119
120 public:
121 std::string
122 toString() const
123 {
124 check();
125 std::stringstream str;
126 str << *node;
127 return str.str();
128 }
129
130 static RapidXmlReaderNode
132 {
133 return RapidXmlReaderNode(
134 nullptr, std::shared_ptr<rapidxml::xml_document<>>(), std::shared_ptr<char[]>());
135 }
136
137 /**
138 * @brief get_node_ptr only for legacy code.
139 * @return internal pointer
140 */
143 {
144 return node;
145 }
146
149 {
151 check();
152 return RapidXmlReaderNode(node->parent(), doc, cstr);
153 }
154
156 first_node(const char* name = nullptr) const
157 {
159 check();
160 return RapidXmlReaderNode(node->first_node(name), doc, cstr);
161 }
162
164 first_node_with_attr_value(const char* tagName,
165 const char* attrName,
166 const char* attrValue) const
167 {
169 for (RapidXmlReaderNode n = first_node(tagName); n.is_valid();
170 n = n.next_sibling(tagName))
171 {
172 if (n.has_attribute_with_value(attrName, attrValue))
173 {
174 return n;
175 }
176 }
177 return NullNode();
178 }
179
180 std::vector<RapidXmlReaderNode>
181 nodes(const char* name = nullptr) const
182 {
184 std::vector<RapidXmlReaderNode> vec;
185 nodes(name, vec);
186 return vec;
187 }
188
189 void
190 nodes(const char* name, std::vector<RapidXmlReaderNode>& vec) const
191 {
193 for (RapidXmlReaderNode n = first_node(name); n.is_valid(); n = n.next_sibling(name))
194 {
195 vec.push_back(n);
196 }
197 }
198
199 void
200 nodes(const char* name1, const char* name2, std::vector<RapidXmlReaderNode>& vec) const
201 {
202 for (RapidXmlReaderNode n = first_node(name1); n.is_valid(); n = n.next_sibling(name1))
203 {
204 n.nodes(name2, vec);
205 }
206 }
207
208 std::vector<RapidXmlReaderNode>
209 nodes(const char* name1, const char* name2) const
210 {
211 std::vector<RapidXmlReaderNode> vec;
212 nodes(name1, name2, vec);
213 return vec;
214 }
215
216 std::vector<RapidXmlReaderNode>
217 nodes(const char* name1, const char* name2, const char* name3) const
218 {
219 std::vector<RapidXmlReaderNode> vec;
220
221 for (RapidXmlReaderNode n = first_node(name1); n.is_valid(); n = n.next_sibling(name1))
222 {
223 n.nodes(name2, name3, vec);
224 }
225
226 return vec;
227 }
228
229 std::string
230 attribute_value(const char* attrName) const
231 {
232 return std::string(get_attrib_value(attrName));
233 }
234
235 bool
236 has_node(const char* nodeName) const
237 {
238 check();
239 return node->first_node(nodeName) != nullptr;
240 }
241
242 bool
243 has_attribute(const char* attrName) const
244 {
245 check();
246 return node->first_attribute(attrName) != nullptr;
247 }
248
249 bool
250 has_attribute_with_value(const char* attrName, const char* attrValue) const
251 {
252 check();
253 rapidxml::xml_attribute<>* attrib = node->first_attribute(attrName);
254 if (!attrib)
255 {
256 return false;
257 }
258 return std::string(attrib->value()) == std::string(attrValue);
259 }
260
261 std::string
262 attribute_value_or_default(const char* attrName, const std::string& defaultValue) const
263 {
264 check();
265 rapidxml::xml_attribute<>* attrib = node->first_attribute(attrName);
266
267 if (!attrib)
268 {
269 return defaultValue;
270 }
271
272 return std::string(attrib->value());
273 }
274
275 bool
276 attribute_as_bool(const char* attrName,
277 const std::string& trueValue,
278 const std::string& falseValue) const
279 {
280 std::string value = std::string(get_attrib_value(attrName));
281
282 if (value == trueValue)
283 {
284 return true;
285 }
286 else if (value == falseValue)
287 {
288 return false;
289 }
290 else
291 {
293 std::string("Invalid value '") + value + "' for attribute '" + attrName +
294 "'. Expecting '" + trueValue + "' or '" + falseValue + "'.");
295 }
296 }
297
298 bool
300 const std::string& trueValue,
301 const std::string& falseValue,
302 bool defaultValue) const
303 {
304 check();
305 rapidxml::xml_attribute<>* attrib = node->first_attribute(name);
306
307 if (!attrib)
308 {
309 return defaultValue;
310 }
311
312 std::string value = std::string(attrib->value());
313
314 if (value == trueValue)
315 {
316 return true;
317 }
318 else if (value == falseValue)
319 {
320 return false;
321 }
322 else
323 {
325 std::string("Invalid value '") + value + "' for attribute '" + name +
326 "'. Expecting '" + trueValue + "' or '" + falseValue + "'.");
327 }
328 }
329
330 float
331 attribute_as_float(const char* attrName) const
332 {
333 return static_cast<float>(atof(get_attrib_value(attrName)));
334 }
335
336 uint32_t
337 attribute_as_uint(const char* attrName) const
338 {
339 std::stringstream strValue(get_attrib_value(attrName));
340 uint32_t retValue;
341 strValue >> retValue;
342 return retValue;
343 }
344
345 std::vector<std::pair<std::string, std::string>>
347 {
348 check();
349 std::vector<std::pair<std::string, std::string>> attributes;
350
351 rapidxml::xml_attribute<>* attrib = node->first_attribute();
352
353 while (attrib)
354 {
355 std::string name = std::string(attrib->name());
356 std::string value = std::string(attrib->value());
357 attributes.push_back({name, value});
358 attrib = attrib->next_attribute();
359 }
360
361 return attributes;
362 }
363
364 std::string
365 value() const
366 {
367 check();
368 return std::string(node->value());
369 }
370
371 float
373 {
374 check();
375 return static_cast<float>(atof(node->value()));
376 }
377
378 uint32_t
380 {
381 check();
382 return static_cast<uint32_t>(armarx::toUInt(node->value()));
383 }
384
385 uint16_t
387 {
388 check();
389 return static_cast<uint16_t>(armarx::toUInt(node->value()));
390 }
391
392 int32_t
394 {
395 check();
396 return static_cast<int32_t>(std::stoul(node->value(), nullptr, 0));
397 }
398
399 int16_t
401 {
402 check();
403 return static_cast<int16_t>(std::stoul(node->value(), nullptr, 0));
404 }
405
406 std::string
407 name() const
408 {
409 check();
410 return std::string(node->name());
411 }
412
415 {
416 check();
417 return node->type();
418 }
419
420 std::string
421 first_node_value(const char* nodeName = nullptr) const
422 {
423 check();
424 rapidxml::xml_node<>* childNode = node->first_node(nodeName);
425
426 if (!childNode)
427 {
429 std::string("Node '") + nodeName + "' does not exist in node " + getPath());
430 }
431
432 return std::string(childNode->value());
433 }
434
435 std::string
436 first_node_value_or_default(const char* name, const std::string& defaultValue) const
437 {
438 check();
439 rapidxml::xml_node<>* childNode = node->first_node(name);
440
441 if (!childNode)
442 {
443 return defaultValue;
444 }
445
446 return std::string(childNode->value());
447 }
448
450 next_sibling(const char* name = nullptr) const
451 {
452 check();
453 return RapidXmlReaderNode(node->next_sibling(name), doc, cstr);
454 }
455
456 bool
457 is_valid() const
458 {
459 return node != nullptr;
460 }
461
462 std::string
464 {
465 check();
466 std::string result;
467 rapidxml::xml_node<>* p = node->parent();
468
469 while (p)
470 {
471 result = std::string(p->name()) + "/" + result;
472 p = p->parent();
473 }
474
475 return result;
476 }
477
478 std::string
479 getPath() const
480 {
481 check();
482 return getParentPath() + name();
483 }
484
485 private:
486 static void
487 collect_child_names(std::vector<std::string>& out,
488 const RapidXmlReaderNode& n,
489 const std::string& pre)
490 {
492 std::string name = pre + n.name();
493 const auto cs = n.nodes();
494
495 if (cs.empty())
496 {
497 out.emplace_back(name);
498 return;
499 }
500 name += '/';
501 for (const auto& c : cs)
502 {
503 collect_child_names(out, c, name);
504 }
505 }
506
507 public:
508 std::vector<std::string>
510 {
512 std::vector<std::string> result;
513 if (is_valid())
514 {
515 collect_child_names(result, *this, getParentPath());
516 }
517 return result;
518 }
519 };
520
521 class RapidXmlReader
522 {
523 private:
524 std::shared_ptr<rapidxml::xml_document<>> doc;
525 std::shared_ptr<char[]> cstr;
526
527 RapidXmlReader(const std::string& xml) : doc(new rapidxml::xml_document<>())
528 {
529 cstr.reset(new char[xml.size() + 1]); // Create char buffer to store string copy
530 strcpy(cstr.get(), xml.c_str()); // Copy string into char buffer
531 doc->parse<0>(cstr.get()); // Pass the non-const char* to parse()
532 }
533
534 public:
535 static std::string
536 ReadFileContents(const std::string& path)
537 {
538 bool found = false;
539 std::string absolutePath;
540 if (path.front() == '/') // Avoid <filesystem> in header ...
541 {
542 absolutePath = path;
543 found = true;
544 }
545 else
546 {
547 found = ArmarXDataPath::getAbsolutePath(path, absolutePath);
548 }
549
550 std::ifstream t(absolutePath);
551 if (!found || !t.is_open())
552 {
553 throw exceptions::local::RapidXmlReaderException("Could not open file ") << path;
554 }
555
556 std::string str;
557 t.seekg(0, std::ios::end);
558 str.reserve(t.tellg());
559 t.seekg(0, std::ios::beg);
560
561 str.assign((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
562 return str;
563 }
564
565 static RapidXmlReaderPtr
566 FromXmlString(const std::string& xml)
567 {
568 RapidXmlReaderPtr wrapper(new RapidXmlReader(xml));
569 return wrapper;
570 }
571
572 static RapidXmlReaderPtr
573 FromFile(const std::string& path)
574 {
575 return FromXmlString(ReadFileContents(path));
576 }
577
580 {
581 return RapidXmlReaderNode(doc.get(), doc, cstr);
582 }
583
585 getRoot(const char* name = nullptr)
586 {
587 return getDocument().first_node(name);
588 }
589 };
590
591} // namespace armarx
constexpr T c
std::string str(const T &t)
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
RapidXmlReaderNode first_node(const char *name=nullptr) const
bool has_attribute_with_value(const char *attrName, const char *attrValue) const
bool attribute_as_bool(const char *attrName, const std::string &trueValue, const std::string &falseValue) const
std::string name() const
std::string toString() const
rapidxml::node_type type()
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
bool has_attribute(const char *attrName) const
static RapidXmlReaderNode NullNode()
RapidXmlReaderNode first_node_with_attr_value(const char *tagName, const char *attrName, const char *attrValue) const
RapidXmlReaderNode next_sibling(const char *name=nullptr) const
uint32_t value_as_uint32() const
std::vector< std::pair< std::string, std::string > > get_all_attributes() const
rapidxml::xml_node * get_node_ptr() const
get_node_ptr only for legacy code.
std::string first_node_value(const char *nodeName=nullptr) const
std::string getPath() const
bool has_node(const char *nodeName) const
uint32_t attribute_as_uint(const char *attrName) const
float attribute_as_float(const char *attrName) const
std::string getParentPath() const
std::string first_node_value_or_default(const char *name, const std::string &defaultValue) const
std::vector< std::string > getChildPaths() const
std::vector< RapidXmlReaderNode > nodes(const char *name1, const char *name2, const char *name3) const
void nodes(const char *name, std::vector< RapidXmlReaderNode > &vec) const
RapidXmlReaderNode parent_node() const
std::string attribute_value(const char *attrName) const
std::string value() const
uint16_t value_as_uint16() const
std::vector< RapidXmlReaderNode > nodes(const char *name1, const char *name2) const
void nodes(const char *name1, const char *name2, std::vector< RapidXmlReaderNode > &vec) const
bool attribute_as_optional_bool(const char *name, const std::string &trueValue, const std::string &falseValue, bool defaultValue) const
std::string attribute_value_or_default(const char *attrName, const std::string &defaultValue) const
static std::string ReadFileContents(const std::string &path)
RapidXmlReaderNode getDocument()
static RapidXmlReaderPtr FromFile(const std::string &path)
RapidXmlReaderNode getRoot(const char *name=nullptr)
static RapidXmlReaderPtr FromXmlString(const std::string &xml)
RapidXmlReaderException(const std::string &message) noexcept
Class representing attribute node of XML document.
Definition rapidxml.hpp:907
xml_attribute< Ch > * next_attribute(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets next attribute, optionally matching attribute name.
Definition rapidxml.hpp:986
Ch * value() const
Gets value of node.
Definition rapidxml.hpp:795
xml_node< Ch > * parent() const
Gets node parent.
Definition rapidxml.hpp:879
Ch * name() const
Gets name of the node.
Definition rapidxml.hpp:774
This class represents root of the DOM hierarchy.
Class representing a node of XML document.
xml_node< Ch > * first_node(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets first child node, optionally matching node name.
This file offers overloads of toIce() and fromIce() functions for STL container types.
unsigned int toUInt(const std::string &input)
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
node_type
Enumeration listing all node types produced by the parser.
Definition rapidxml.hpp:149
This file contains rapidxml parser and DOM implementation.
This file contains rapidxml printer implementation.
static void PrintExceptionBacktrace(std::ostream &out, const std::string &pre="")
Definition trace.cpp:216
#define ARMARX_TRACE
Definition trace.h:77