OpenXLSX 1.9.1
Loading...
Searching...
No Matches
XLXmlParser.hpp
Go to the documentation of this file.
1#ifndef OPENXLSX_XLXMLPARSER_HPP
2#define OPENXLSX_XLXMLPARSER_HPP
3
4#include <memory> // shared_ptr
5
6// ===== pugixml.hpp needed for pugi::impl::xml_memory_page_type_mask, pugi::xml_node_type, pugi::char_t, pugi::node_element,
7// pugi::xml_node, pugi::xml_attribute, pugi::xml_document
8#include "XLException.hpp"
9#include <pugixml.hpp>
10
11namespace
12{ // anonymous namespace to define constants / functions that shall not be exported from this module
13 constexpr const int XLMaxNamespacedNameLen = 100;
14} // anonymous namespace
15
16namespace OpenXLSX
17{
18#define ENABLE_XML_NAMESPACES 1 // disable this line to control behavior via compiler flag
19
20#ifdef ENABLE_XML_NAMESPACES
21 // ===== Macro for NAMESPACED_NAME when node names might need to be prefixed with the current node's namespace
22 // Uses namespaced_name_shared_ptr.get() which returns a pointer owned by a temporary shared_ptr.
23 // The C++ standard guarantees that temporary objects are destroyed as the last step in evaluating the
24 // full-expression that (lexically) contains the point where they were created.
25 // This makes it completely thread-safe and re-entrant without any static buffer side-effects.
26# define NAMESPACED_NAME(name_, force_ns_) namespaced_name_proxy(name_, force_ns_).c_str()
27#else
28 // ===== Optimized version when no namespace support is desired - ignores force_ns_ setting
29# define NAMESPACED_NAME(name_, force_ns_) name_
30#endif
31
32 constexpr const bool XLForceNamespace = true;
40 extern thread_local bool NO_XML_NS; // defined in XLXmlParser.cpp - default: no XML namespaces
53
54 // disable this line to use original (non-augmented) pugixml - as of 2024-07-29 this is no longer a realistic option
55#define PUGI_AUGMENTED
56
57 // ===== Using statements to switch between pugixml and augmented pugixml implementation
58#ifdef PUGI_AUGMENTED
59 // ===== Forward declarations for using statements below
60 class OpenXLSX_xml_node;
61 class OpenXLSX_xml_document;
62
64 using XMLAttribute = pugi::xml_attribute;
66#else
67 using XMLNode = pugi::xml_node;
68 using XMLAttribute = pugi::xml_attribute;
69 using XMLDocument = pugi::xml_document;
70#endif
71
72 // ===== Custom OpenXLSX_xml_node to add functionality to pugi::xml_node
73
74 class NameProxy {
75 std::string m_str;
76 const pugi::char_t* m_ptr;
77 public:
78 NameProxy(const pugi::char_t* ptr) : m_str(), m_ptr(ptr) {}
79 NameProxy(std::string str) : m_str(std::move(str)), m_ptr(m_str.c_str()) {}
80 const pugi::char_t* c_str() const { return m_ptr; }
81 };
82
83 class OpenXLSX_xml_node : public pugi::xml_node
84 {
85 public:
89 OpenXLSX_xml_node() : pugi::xml_node(), name_begin(0) {}
90
94 template<class base>
95 OpenXLSX_xml_node(base b) : pugi::xml_node(b),
96 name_begin(0)
97 {
98 if (NO_XML_NS) return;
99 const char* name = xml_node::name();
100 size_t pos = 0;
101 while (name[pos] and name[pos] != ':') ++pos; // find name delimiter
102 if (name[pos] == ':') name_begin = pos + 1; // if delimiter was found: update name_begin to point behind that position
103 }
104
110 const pugi::char_t* name_without_namespace(const pugi::char_t* name_) const;
111
125 NameProxy namespaced_name_proxy(const pugi::char_t* name_, bool force_ns) const;
126
127 // ===== BEGIN: Wrappers for xml_node member functions to ensure OpenXLSX_xml_node return values
128 // and overrides for xml_node member functions to support ignoring the node namespace
129 // ===== CAUTION: this section might be incomplete, only functions actually used by OpenXLSX to date have been checked
130
135 const pugi::char_t* name() const { return &xml_node::name()[name_begin]; }
136
141 const pugi::char_t* raw_name() const { return xml_node::name(); }
142
146 XMLNode parent() const { return pugi::xml_node::parent(); }
147 template<typename Predicate>
148 XMLNode find_child(Predicate pred) const
149 { return pugi::xml_node::find_child(pred); }
150 XMLNode child(const pugi::char_t* name_) const { return xml_node::child(NAMESPACED_NAME(name_, false)); }
151 XMLNode next_sibling(const pugi::char_t* name_) const { return xml_node::next_sibling(NAMESPACED_NAME(name_, false)); }
152 XMLNode next_sibling() const { return xml_node::next_sibling(); }
153 XMLNode previous_sibling(const pugi::char_t* name_) const { return xml_node::previous_sibling(NAMESPACED_NAME(name_, false)); }
154 XMLNode previous_sibling() const { return xml_node::previous_sibling(); }
155 const pugi::char_t* child_value() const { return xml_node::child_value(); }
156 const pugi::char_t* child_value(const pugi::char_t* name_) const { return xml_node::child_value(NAMESPACED_NAME(name_, false)); }
157 bool set_name(const pugi::char_t* rhs, bool force_ns = false) { return xml_node::set_name(NAMESPACED_NAME(rhs, force_ns)); }
158 bool set_name(const pugi::char_t* rhs, size_t size, bool force_ns = false)
159 { return xml_node::set_name(NAMESPACED_NAME(rhs, force_ns), size + name_begin); }
160 XMLNode append_child(pugi::xml_node_type type_) { return xml_node::append_child(type_); }
161 XMLNode prepend_child(pugi::xml_node_type type_) { return xml_node::prepend_child(type_); }
162 XMLNode append_child(const pugi::char_t* name_, bool force_ns = false)
163 { return xml_node::append_child(NAMESPACED_NAME(name_, force_ns)); }
164 XMLNode prepend_child(const pugi::char_t* name_, bool force_ns = false)
165 { return xml_node::prepend_child(NAMESPACED_NAME(name_, force_ns)); }
166 XMLNode insert_child_after(pugi::xml_node_type type_, const xml_node& node) { return xml_node::insert_child_after(type_, node); }
167 XMLNode insert_child_before(pugi::xml_node_type type_, const xml_node& node) { return xml_node::insert_child_before(type_, node); }
168 XMLNode insert_child_after(const pugi::char_t* name_, const xml_node& node, bool force_ns = false)
169 { return xml_node::insert_child_after(NAMESPACED_NAME(name_, force_ns), node); }
170 XMLNode insert_child_before(const pugi::char_t* name_, const xml_node& node, bool force_ns = false)
171 { return xml_node::insert_child_before(NAMESPACED_NAME(name_, force_ns), node); }
172 bool remove_child(const pugi::char_t* name_) { return xml_node::remove_child(NAMESPACED_NAME(name_, false)); }
173 bool remove_child(const xml_node& n) { return xml_node::remove_child(n); }
174 XMLNode find_child_by_attribute(const pugi::char_t* name_, const pugi::char_t* attr_name, const pugi::char_t* attr_value) const
175 { return xml_node::find_child_by_attribute(NAMESPACED_NAME(name_, false), attr_name, attr_value); }
176 XMLNode find_child_by_attribute(const pugi::char_t* attr_name, const pugi::char_t* attr_value) const
177 { return xml_node::find_child_by_attribute(attr_name, attr_value); }
178 // DISCLAIMER: unused by OpenXLSX, but theoretically impacted by xml namespaces:
179 // PUGI_IMPL_FN xml_node xml_node::first_element_by_path(const pugi::char_t* path_, pugi::char_t delimiter) const
180 // ===== END: Wrappers for xml_node member functions
181
187 XMLNode first_child_of_type(pugi::xml_node_type type_ = pugi::node_element) const;
188
194 XMLNode last_child_of_type(pugi::xml_node_type type_ = pugi::node_element) const;
195
201 size_t child_count_of_type(pugi::xml_node_type type_ = pugi::node_element) const;
202
208 XMLNode next_sibling_of_type(pugi::xml_node_type type_ = pugi::node_element) const;
209
215 XMLNode previous_sibling_of_type(pugi::xml_node_type type_ = pugi::node_element) const;
216
223 XMLNode next_sibling_of_type(const pugi::char_t* name_, pugi::xml_node_type type_ = pugi::node_element) const;
224
231 XMLNode previous_sibling_of_type(const pugi::char_t* name_, pugi::xml_node_type type_ = pugi::node_element) const;
232
233 private:
234 size_t name_begin; // nameBegin holds the position in xml_node::name() where the actual node name begins - 0 for non-namespaced
235 // nodes for nodes with a namespace: the position following namespace + delimiter colon, e.g. "x:c" ->
236 // nameBegin = 2
237 };
238
239 // ===== Custom OpenXLSX_xml_document to override relevant pugi::xml_document member functions with OpenXLSX_xml_node return value
240 class OpenXLSX_xml_document : public pugi::xml_document
241 {
242 public:
246 OpenXLSX_xml_document() : pugi::xml_document() {}
247
251 template<class base>
252 OpenXLSX_xml_document(base b) : pugi::xml_document(b)
253 {}
254
255 // ===== BEGIN: Wrappers for xml_document member functions to ensure OpenXLSX_xml_node return values
256 // ===== CAUTION: this section is incomplete, only implementing those functions actually used by OpenXLSX to date
260 XMLNode document_element() const { return pugi::xml_document::document_element(); }
261 // ===== END: Wrappers for xml_document member functions
262 };
263
264} // namespace OpenXLSX
265#endif // OPENXLSX_XLXMLPARSER_HPP
#define NAMESPACED_NAME(name_, force_ns_)
Definition XLXmlParser.hpp:26
Definition XLXmlParser.hpp:74
NameProxy(const pugi::char_t *ptr)
Definition XLXmlParser.hpp:78
NameProxy(std::string str)
Definition XLXmlParser.hpp:79
const pugi::char_t * c_str() const
Definition XLXmlParser.hpp:80
Definition XLXmlParser.hpp:241
XMLNode document_element() const
for all functions: invoke the base class function, but with a return type of OpenXLSX_xml_node
Definition XLXmlParser.hpp:260
OpenXLSX_xml_document(base b)
Inherit all constructors with parameters from pugi::xml_document.
Definition XLXmlParser.hpp:252
OpenXLSX_xml_document()
Default constructor. Constructs a null object.
Definition XLXmlParser.hpp:246
Definition XLXmlParser.hpp:84
XMLNode append_child(const pugi::char_t *name_, bool force_ns=false)
Definition XLXmlParser.hpp:162
XMLNode last_child_of_type(pugi::xml_node_type type_=pugi::node_element) const
get last node child that matches type
Definition XLXmlParser.cpp:63
const pugi::char_t * child_value() const
Definition XLXmlParser.hpp:155
XMLNode next_sibling(const pugi::char_t *name_) const
Definition XLXmlParser.hpp:151
XMLNode child(const pugi::char_t *name_) const
Definition XLXmlParser.hpp:150
XMLNode prepend_child(pugi::xml_node_type type_)
Definition XLXmlParser.hpp:161
const pugi::char_t * name_without_namespace(const pugi::char_t *name_) const
Strip any namespace from name_.
Definition XLXmlParser.cpp:33
bool set_name(const pugi::char_t *rhs, bool force_ns=false)
Definition XLXmlParser.hpp:157
OpenXLSX_xml_node(base b)
Inherit all constructors with parameters from pugi::xml_node.
Definition XLXmlParser.hpp:95
XMLNode find_child(Predicate pred) const
Definition XLXmlParser.hpp:148
XMLNode previous_sibling() const
Definition XLXmlParser.hpp:154
XMLNode previous_sibling_of_type(pugi::xml_node_type type_=pugi::node_element) const
get previous node sibling that matches type
Definition XLXmlParser.cpp:95
bool remove_child(const xml_node &n)
Definition XLXmlParser.hpp:173
XMLNode append_child(pugi::xml_node_type type_)
Definition XLXmlParser.hpp:160
OpenXLSX_xml_node()
Default constructor. Constructs a null object.
Definition XLXmlParser.hpp:89
const pugi::char_t * child_value(const pugi::char_t *name_) const
Definition XLXmlParser.hpp:156
XMLNode find_child_by_attribute(const pugi::char_t *attr_name, const pugi::char_t *attr_value) const
Definition XLXmlParser.hpp:176
XMLNode next_sibling() const
Definition XLXmlParser.hpp:152
size_t child_count_of_type(pugi::xml_node_type type_=pugi::node_element) const
count node children that match type
Definition XLXmlParser.cpp:74
XMLNode previous_sibling(const pugi::char_t *name_) const
Definition XLXmlParser.hpp:153
XMLNode next_sibling_of_type(pugi::xml_node_type type_=pugi::node_element) const
get next node sibling that matches type
Definition XLXmlParser.cpp:87
bool set_name(const pugi::char_t *rhs, size_t size, bool force_ns=false)
Definition XLXmlParser.hpp:158
XMLNode insert_child_before(pugi::xml_node_type type_, const xml_node &node)
Definition XLXmlParser.hpp:167
XMLNode insert_child_after(pugi::xml_node_type type_, const xml_node &node)
Definition XLXmlParser.hpp:166
const pugi::char_t * raw_name() const
get actual node name from pugi::xml_node::name, including namespace, if any
Definition XLXmlParser.hpp:141
XMLNode insert_child_after(const pugi::char_t *name_, const xml_node &node, bool force_ns=false)
Definition XLXmlParser.hpp:168
XMLNode find_child_by_attribute(const pugi::char_t *name_, const pugi::char_t *attr_name, const pugi::char_t *attr_value) const
Definition XLXmlParser.hpp:174
NameProxy namespaced_name_proxy(const pugi::char_t *name_, bool force_ns) const
add this node's namespace to name_
Definition XLXmlParser.cpp:44
XMLNode prepend_child(const pugi::char_t *name_, bool force_ns=false)
Definition XLXmlParser.hpp:164
const pugi::char_t * name() const
get node name while stripping namespace, if so configured (name_begin > 0)
Definition XLXmlParser.hpp:135
XMLNode parent() const
for all functions returning xml_node: invoke base class function, but with a return type of XMLNode (...
Definition XLXmlParser.hpp:146
XMLNode insert_child_before(const pugi::char_t *name_, const xml_node &node, bool force_ns=false)
Definition XLXmlParser.hpp:170
bool remove_child(const pugi::char_t *name_)
Definition XLXmlParser.hpp:172
XMLNode first_child_of_type(pugi::xml_node_type type_=pugi::node_element) const
get first node child that matches type
Definition XLXmlParser.cpp:52
Definition IZipArchive.hpp:18
constexpr const bool XLForceNamespace
Definition XLXmlParser.hpp:32
bool disable_xml_namespaces()
Set NO_XML_NS to true.
Definition XLXmlParser.cpp:23
bool enable_xml_namespaces()
Set NO_XML_NS to false.
Definition XLXmlParser.cpp:13
pugi::xml_attribute XMLAttribute
Definition XLXmlParser.hpp:64
thread_local bool NO_XML_NS
Definition XLXmlParser.cpp:11
Definition XLCellIterator.hpp:121