MUSX Document Model
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
QtXmlImpl.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#ifdef MUSX_USE_QTXML
25
26#include <QString>
27#include <QDomDocument>
28#include <QDomElement>
29#include <QDomAttr>
30#include <QByteArray>
31#include <memory>
32#include "XmlInterface.h"
33
34namespace musx {
35namespace xml {
36
41namespace qt {
42
46class Attribute : public musx::xml::IXmlAttribute {
47 QDomAttr m_attr;
48 int m_index;
49
50public:
51 Attribute(const QDomAttr& attr, int index)
52 : m_attr(attr), m_index(index) {}
53
54 std::string getName() const override {
55 return m_attr.name().toStdString();
56 }
57
58 std::string getValue() const override {
59 return m_attr.value().toStdString();
60 }
61
62 std::shared_ptr<IXmlAttribute> nextAttribute() const override {
63 QDomNamedNodeMap attributes = m_attr.parentNode().attributes();
64 if (m_index + 1 >= attributes.count()) {
65 return nullptr;
66 }
67 QDomAttr nextAttr = attributes.item(m_index + 1).toAttr();
68 if (nextAttr.isNull()) { // [[unlikely]]
69 return nullptr;
70 }
71 return std::make_shared<Attribute>(nextAttr, m_index + 1);
72 }
73};
74
78class Element : public musx::xml::IXmlElement {
79 const QDomElement m_element;
80
81public:
82 explicit Element(const QDomElement& elem) : m_element(elem) {}
83
84 std::string getTagName() const override { return m_element.tagName().toStdString(); }
85
86 std::string getText() const override { return m_element.text().toStdString(); }
87
88 std::shared_ptr<IXmlAttribute> getFirstAttribute() const override {
89 QDomNamedNodeMap attributes = m_element.attributes();
90 if (attributes.isEmpty()) {
91 return nullptr;
92 }
93 QDomAttr attr = attributes.item(0).toAttr();
94 if (attr.isNull()) { // [[unlikely]]
95 return nullptr;
96 }
97 return std::make_shared<Attribute>(attr, 0);
98 }
99
100 std::shared_ptr<IXmlAttribute> findAttribute(const std::string& name) const override {
101 // work around Qt bug that attributeNode is not marked const
102 QDomElement& nonConstElement = const_cast<QDomElement&>(m_element);
103 QDomAttr attr = nonConstElement.attributeNode(QString::fromStdString(name));
104 return attr.isNull() ? nullptr : std::make_shared<Attribute>(attr, 0);
105 }
106
107 std::shared_ptr<IXmlElement> getFirstChildElement(const std::string& tagName = {}) const override {
108 QDomNode child = tagName.empty()
109 ? m_element.firstChildElement()
110 : m_element.firstChildElement(QString::fromStdString(tagName));
111 return child.isNull() ? nullptr : std::make_shared<Element>(child.toElement());
112 }
113
114 std::shared_ptr<IXmlElement> getNextSibling(const std::string& tagName = {}) const override {
115 QDomNode sibling = m_element.nextSiblingElement(QString::fromStdString(tagName));
116 if (tagName.empty() && sibling.isNull()) {
117 sibling = m_element.nextSibling();
118 while (!sibling.isNull() && !sibling.isElement()) {
119 sibling = sibling.nextSibling();
120 }
121 }
122 return sibling.isNull() ? nullptr : std::make_shared<Element>(sibling.toElement());
123 }
124
125 std::shared_ptr<IXmlElement> getPreviousSibling(const std::string& tagName = {}) const override {
126 QDomNode sibling = m_element.previousSiblingElement(QString::fromStdString(tagName));
127 if (tagName.empty() && sibling.isNull()) {
128 sibling = m_element.previousSibling();
129 while (!sibling.isNull() && !sibling.isElement()) {
130 sibling = sibling.previousSibling();
131 }
132 }
133 return sibling.isNull() ? nullptr : std::make_shared<Element>(sibling.toElement());
134 }
135
136 std::shared_ptr<IXmlElement> getParent() const override {
137 QDomNode parent = m_element.parentNode();
138 return parent.isNull() || !parent.isElement() ? nullptr : std::make_shared<Element>(parent.toElement());
139 }
140};
141
145class Document : public musx::xml::IXmlDocument {
146 QDomDocument m_document;
147
148public:
149 void loadFromString(const std::string& xmlContent) override {
150 QString errorMsg;
151 int errorLine = 0, errorColumn = 0;
152 if (!m_document.setContent(QString::fromUtf8(xmlContent.c_str()), &errorMsg, &errorLine, &errorColumn)) {
153 throw musx::xml::load_error("Error parsing XML at line " + std::to_string(errorLine) +
154 ", column " + std::to_string(errorColumn) + ": " + errorMsg.toStdString());
155 }
156 }
157
158 void loadFromBuffer(const char * data, size_t size) override {
159 QString errorMsg;
160 int errorLine = 0, errorColumn = 0;
161 if (!m_document.setContent(QByteArray(data, size), &errorMsg, &errorLine, &errorColumn)) {
162 throw musx::xml::load_error("Error parsing XML at line " + std::to_string(errorLine) +
163 ", column " + std::to_string(errorColumn) + ": " + errorMsg.toStdString());
164 }
165 }
166
167 std::shared_ptr<IXmlElement> getRootElement() const override {
168 QDomElement root = m_document.documentElement();
169 return root.isNull() ? nullptr : std::make_shared<Element>(root);
170 }
171};
172
173} // namespace qt
174} // namespace xml
175} // namespace musx
176
177#endif // MUSX_USE_QTXML
Interface for an XML attribute.
Definition XmlInterface.h:65
Interface for an XML document.
Definition XmlInterface.h:230
Interface for an XML element.
Definition XmlInterface.h:133
Exception for load xml error.
Definition XmlInterface.h:46
object model for musx file (enigmaxml)
Definition BaseClasses.h:32