FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
libxml_wrapper.h
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright (c) 2024-present FERS Contributors (see AUTHORS.md).
4//
5// See the GNU GPLv2 LICENSE file in the FERS project root for more information.
6
7/**
8 * @file libxml_wrapper.h
9 * @brief Wrapper for managing XML documents and elements using libxml2.
10 *
11 * This header file provides classes and functions to simplify handling XML documents and elements
12 * using the libxml2 library. It includes basic functionality for manipulating XML nodes, attributes,
13 * content, and validation using DTD and XSD schemas.
14 */
15
16#pragma once
17
18#include <iostream>
19#include <libxml/parser.h>
20#include <memory>
21#include <span>
22#include <stdexcept>
23#include <string>
24#include <string_view>
25
26#include "core/logging.h"
27#include "libxml/globals.h"
28#include "libxml/xmlstring.h"
29
30/**
31 * @class XmlException
32 * @brief Exception class for handling XML-related errors.
33 */
34class XmlException final : public std::runtime_error
35{
36public:
37 /**
38 * @brief Constructor for XmlException.
39 *
40 * @param message The error message associated with the exception.
41 */
42 explicit XmlException(const std::string_view message) : std::runtime_error(std::string(message)) {}
43};
44
45/**
46 * @class XmlElement
47 * @brief Class representing a node in an XML document.
48 *
49 * This class encapsulates an XML element, allowing users to access and manipulate
50 * element names, attributes, content, and children. It uses libxml2 for all operations
51 * and provides simplified methods to interact with XML nodes.
52 */
54{
55 xmlNodePtr _node; ///< Pointer to the XML node represented by this object.
56
57public:
58 /**
59 * @brief Constructor for XmlElement.
60 *
61 * @param node The xmlNode pointer representing the XML element.
62 */
63 explicit XmlElement(const xmlNode* node) : _node(const_cast<xmlNode*>(node)) {}
64
65 XmlElement(const XmlElement&) = default;
66
67 XmlElement(XmlElement&&) noexcept = default;
68
69 XmlElement& operator=(const XmlElement&) = default;
70
71 XmlElement& operator=(XmlElement&&) noexcept = default;
72
73 ~XmlElement() = default;
74
75 /**
76 * @brief Get the name of the XML element.
77 *
78 * @return The name of the XML element.
79 */
80 [[nodiscard]] std::string_view name() const noexcept { return reinterpret_cast<const char*>(_node->name); }
81
82 /**
83 * @brief Get the text content of the XML element.
84 *
85 * @return The text content as a string.
86 */
87 [[nodiscard]] std::string getText() const
88 {
89 if (!_node)
90 {
91 return "";
92 }
93 xmlChar* text = xmlNodeGetContent(_node);
94 std::string result = reinterpret_cast<const char*>(text);
95 xmlFree(text);
96 return result;
97 }
98
99 /**
100 * @brief Set the text content of the XML element.
101 *
102 * @param text The text to set as the content of the node.
103 */
104 void setText(const std::string_view text) const
105 {
106 xmlNodeSetContent(_node, reinterpret_cast<const xmlChar*>(text.data()));
107 }
108
109 /**
110 * @brief Get the value of an attribute safely.
111 *
112 * @param element The XmlElement to retrieve the attribute from.
113 * @param name The name of the attribute.
114 * @return The value of the attribute.
115 * @throws XmlException if the attribute is not found.
116 */
117 static std::string getSafeAttribute(const XmlElement& element, const std::string_view name)
118 {
119 std::string value;
120 if (xmlChar* attr = xmlGetProp(element.getNode(), reinterpret_cast<const xmlChar*>(name.data())))
121 {
122 value = reinterpret_cast<const char*>(attr);
123 xmlFree(attr);
124 }
125 else
126 {
127 throw XmlException("Attribute not found: " + std::string(name));
128 }
129 return value;
130 }
131
132 /**
133 * @brief Set an attribute on the XML element.
134 *
135 * @param name The name of the attribute.
136 * @param value The value to set for the attribute.
137 */
138 void setAttribute(const std::string_view name, const std::string_view value) const
139 {
140 xmlSetProp(_node, reinterpret_cast<const xmlChar*>(name.data()),
141 reinterpret_cast<const xmlChar*>(value.data()));
142 }
143
144 /**
145 * @brief Add a child element to the current node.
146 *
147 * @param name The name of the new child element.
148 * @return The newly created XmlElement.
149 */
150 [[nodiscard]] XmlElement addChild(const std::string_view name) const noexcept
151 {
152 const xmlNode* child = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>(name.data()));
153 xmlAddChild(_node, const_cast<xmlNode*>(child));
154 return XmlElement(child);
155 }
156
157 /**
158 * @brief Retrieve a child element by name and index.
159 *
160 * @param name The name of the child element (optional).
161 * @param index The index of the child to retrieve.
162 * @return The child element or an invalid XmlElement if not found.
163 */
164 [[nodiscard]] XmlElement childElement(const std::string_view name = "", const unsigned index = 0) const noexcept
165 {
166 if (!_node)
167 {
168 return XmlElement(nullptr);
169 }
170 unsigned count = 0;
171 for (auto* child = _node->children; child; child = child->next)
172 {
173 if (child->type == XML_ELEMENT_NODE && (name.empty() || name == reinterpret_cast<const char*>(child->name)))
174 {
175 if (count == index)
176 {
177 return XmlElement(child);
178 }
179 ++count;
180 }
181 }
182 return XmlElement(nullptr);
183 }
184
185 /**
186 * @brief Check if the XML element is valid.
187 *
188 * @return True if the node is valid, otherwise false.
189 */
190 [[nodiscard]] bool isValid() const noexcept { return _node != nullptr; }
191
192 /**
193 * @brief Get the underlying XML node pointer.
194 *
195 * @return The underlying XML node pointer.
196 */
197 [[nodiscard]] xmlNodePtr getNode() const noexcept { return _node; }
198};
199
200/**
201 * @class XmlDocument
202 * @brief Class for managing XML documents.
203 */
205{
206 std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> _doc; ///< Pointer to the XML document.
207
208public:
209 /**
210 * @brief Constructor for XmlDocument.
211 *
212 * @throws std::runtime_error if the document creation fails.
213 */
214 XmlDocument() : _doc(xmlNewDoc(reinterpret_cast<const xmlChar*>("1.0")), &xmlFreeDoc)
215 {
216 if (!_doc)
217 {
218 throw XmlException("Failed to create XML document.");
219 }
220 }
221
222 ~XmlDocument() = default;
223
224 XmlDocument(const XmlDocument&) = delete;
225
226 XmlDocument(XmlDocument&&) noexcept = default;
227
228 XmlDocument& operator=(const XmlDocument&) = delete;
229
230 XmlDocument& operator=(XmlDocument&&) noexcept = default;
231
232 /**
233 * @brief Load an XML file into the document.
234 *
235 * @param filename The name of the file to load.
236 * @return True if the file was successfully loaded, otherwise false.
237 */
238 [[nodiscard]] bool loadFile(std::string_view filename);
239
240 /**
241 * @brief Load an XML document from a string in memory.
242 *
243 * @param content The string containing the XML document.
244 * @return True if the string was successfully parsed, otherwise false.
245 */
246 [[nodiscard]] bool loadString(const std::string& content);
247
248 /**
249 * @brief Save the document to a file.
250 *
251 * @param filename The name of the file to save to.
252 * @return True if the file was successfully saved, otherwise false.
253 */
254 [[nodiscard]] bool saveFile(const std::string_view filename) const
255 {
256 if (!_doc)
257 {
258 LOG(logging::Level::ERROR, "Document is null; Cannot save to file");
259 return false;
260 }
261 return xmlSaveFormatFileEnc(filename.data(), _doc.get(), "UTF-8", 1) != -1;
262 }
263
264 /**
265 * @brief Dumps the document to a string.
266 *
267 * @return A string containing the XML document.
268 */
269 [[nodiscard]] std::string dumpToString() const;
270
271 /**
272 * @brief Set the root element of the document.
273 *
274 * @param root The root element to set.
275 * @throws std::runtime_error if the document is not created.
276 */
277 void setRootElement(const XmlElement& root) const
278 {
279 if (!_doc)
280 {
281 throw std::runtime_error("Document not created");
282 }
283 xmlDocSetRootElement(_doc.get(), root.getNode());
284 }
285
286 /**
287 * @brief Get the root element of the document.
288 *
289 * @return The root element.
290 * @throws std::runtime_error if the document is not loaded or the root element is missing.
291 */
292 [[nodiscard]] XmlElement getRootElement() const
293 {
294 if (!_doc)
295 {
296 throw std::runtime_error("Document not loaded");
297 }
298 const xmlNode* root = xmlDocGetRootElement(_doc.get());
299 if (!root)
300 {
301 throw std::runtime_error("Root element not found");
302 }
303 return XmlElement(root);
304 }
305
306 /**
307 * @brief Validate the document using a DTD.
308 *
309 * @param dtdData The DTD data used for validation.
310 * @return True if the document is valid according to the DTD.
311 * @throws XmlException if the DTD is invalid or the validation fails.
312 */
313 [[nodiscard]] bool validateWithDtd(std::span<const unsigned char> dtdData) const;
314
315 /**
316 * @brief Validate the document using an XSD schema.
317 *
318 * @param xsdData The XSD data used for validation.
319 * @return True if the document is valid according to the XSD schema.
320 * @throws XmlException if the XSD is invalid or the validation fails.
321 */
322 [[nodiscard]] bool validateWithXsd(std::span<const unsigned char> xsdData) const;
323};
324
325/**
326 * @brief Merge two XML documents.
327 *
328 * @param mainDoc The main XML document.
329 * @param includedDoc The XML document to include.
330 */
331void mergeXmlDocuments(const XmlDocument& mainDoc, const XmlDocument& includedDoc);
332
333/**
334 * @brief Remove "include" elements from the XML document.
335 *
336 * @param doc The XML document from which to remove the "include" elements.
337 */
338void removeIncludeElements(const XmlDocument& doc);
Class for managing XML documents.
XmlElement getRootElement() const
Get the root element of the document.
XmlDocument(XmlDocument &&) noexcept=default
bool loadFile(std::string_view filename)
Load an XML file into the document.
XmlDocument(const XmlDocument &)=delete
XmlDocument()
Constructor for XmlDocument.
bool validateWithDtd(std::span< const unsigned char > dtdData) const
Validate the document using a DTD.
void setRootElement(const XmlElement &root) const
Set the root element of the document.
bool validateWithXsd(std::span< const unsigned char > xsdData) const
Validate the document using an XSD schema.
bool loadString(const std::string &content)
Load an XML document from a string in memory.
bool saveFile(const std::string_view filename) const
Save the document to a file.
~XmlDocument()=default
std::string dumpToString() const
Dumps the document to a string.
Class representing a node in an XML document.
XmlElement childElement(const std::string_view name="", const unsigned index=0) const noexcept
Retrieve a child element by name and index.
XmlElement addChild(const std::string_view name) const noexcept
Add a child element to the current node.
static std::string getSafeAttribute(const XmlElement &element, const std::string_view name)
Get the value of an attribute safely.
std::string_view name() const noexcept
Get the name of the XML element.
bool isValid() const noexcept
Check if the XML element is valid.
xmlNodePtr getNode() const noexcept
Get the underlying XML node pointer.
void setAttribute(const std::string_view name, const std::string_view value) const
Set an attribute on the XML element.
XmlElement(XmlElement &&) noexcept=default
XmlElement(const xmlNode *node)
Constructor for XmlElement.
std::string getText() const
Get the text content of the XML element.
void setText(const std::string_view text) const
Set the text content of the XML element.
XmlElement(const XmlElement &)=default
Exception class for handling XML-related errors.
XmlException(const std::string_view message)
Constructor for XmlException.
void mergeXmlDocuments(const XmlDocument &mainDoc, const XmlDocument &includedDoc)
Merge two XML documents.
void removeIncludeElements(const XmlDocument &doc)
Remove "include" elements from the XML document.
Header file for the logging system.
#define LOG(level,...)
Definition logging.h:19
@ ERROR
Error level for error events.