Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "xmlstreamwriter.h"
23 :
24 : #include <vector>
25 :
26 : #include <utf8.h>
27 :
28 : #include "global/containers.h"
29 : #include "textstream.h"
30 :
31 : #include "global/log.h"
32 :
33 : using namespace std::literals;
34 : using namespace muse;
35 :
36 : struct XmlStreamWriter::Impl {
37 : std::vector<std::string> stack;
38 : TextStream stream;
39 :
40 712376 : void putLevel()
41 : {
42 712376 : size_t level = stack.size();
43 8052400 : for (size_t i = 0; i < level * 2; ++i) {
44 7340024 : stream << ' ';
45 : }
46 712376 : }
47 : };
48 :
49 293 : XmlStreamWriter::XmlStreamWriter()
50 : {
51 293 : m_impl = new Impl();
52 293 : }
53 :
54 1948 : XmlStreamWriter::XmlStreamWriter(io::IODevice* dev)
55 : {
56 1948 : m_impl = new Impl();
57 1948 : m_impl->stream.setDevice(dev);
58 1948 : }
59 :
60 2241 : XmlStreamWriter::~XmlStreamWriter()
61 : {
62 2241 : flush();
63 2241 : delete m_impl;
64 2241 : }
65 :
66 293 : void XmlStreamWriter::setDevice(io::IODevice* dev)
67 : {
68 293 : m_impl->stream.setDevice(dev);
69 293 : }
70 :
71 4479 : void XmlStreamWriter::flush()
72 : {
73 4479 : m_impl->stream.flush();
74 4479 : }
75 :
76 2225 : void XmlStreamWriter::startDocument()
77 : {
78 2225 : m_impl->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
79 2225 : }
80 :
81 293 : void XmlStreamWriter::writeDoctype(const String& type)
82 : {
83 293 : m_impl->stream << "<!DOCTYPE " << type << '>' << '\n';
84 293 : }
85 :
86 1374033 : std::string XmlStreamWriter::escapeCodePoint(const char32_t c)
87 : {
88 1374033 : switch (c) {
89 1148 : case U'<':
90 1148 : return "<"s;
91 1148 : case U'>':
92 1148 : return ">"s;
93 6 : case U'&':
94 6 : return "&"s;
95 287 : case U'\"':
96 287 : return """s;
97 1371444 : default:
98 : // ignore invalid characters in xml 1.0
99 1371444 : if ((c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D)) {
100 0 : return std::string();
101 : }
102 1371444 : return utf8::utf32to8(std::u32string_view(&c, 1));
103 : }
104 : }
105 :
106 246807 : std::string XmlStreamWriter::escapeString(const std::string_view str)
107 : {
108 246807 : std::string escaped{};
109 246807 : auto it = str.begin();
110 1620840 : while (it != str.end()) {
111 1374033 : const char32_t c = utf8::next(it, str.end());
112 1374033 : escaped += escapeCodePoint(c);
113 : }
114 :
115 246807 : return escaped;
116 0 : }
117 :
118 417361 : void XmlStreamWriter::writeValue(const Value& v)
119 : {
120 : // std::monostate, int, unsigned int, signed long int, unsigned long int, signed long long, unsigned long long,
121 : // double, const char*, std::string_view, String
122 417361 : switch (v.index()) {
123 0 : case 0:
124 0 : break;
125 154669 : case 1: m_impl->stream << int32_t{ std::get<int>(v) };
126 154669 : break;
127 0 : case 2: m_impl->stream << uint32_t{ std::get<unsigned int>(v) };
128 0 : break;
129 0 : case 3: m_impl->stream << int64_t{ std::get<signed long int>(v) };
130 0 : break;
131 2800 : case 4: m_impl->stream << uint64_t{ std::get<unsigned long int>(v) };
132 2800 : break;
133 0 : case 5: m_impl->stream << int64_t{ std::get<signed long long>(v) };
134 0 : break;
135 0 : case 6: m_impl->stream << uint64_t{ std::get<unsigned long long>(v) };
136 0 : break;
137 13085 : case 7: m_impl->stream << std::get<double>(v);
138 13085 : break;
139 12412 : case 8: m_impl->stream << escapeString(AsciiStringView(std::get<const char*>(v)));
140 12412 : break;
141 156447 : case 9: m_impl->stream << escapeString(std::get<std::string_view>(v));
142 156447 : break;
143 77948 : case 10: m_impl->stream << escapeString(std::get<String>(v).toStdString());
144 77948 : break;
145 0 : default:
146 0 : LOGI() << "index: " << v.index();
147 0 : UNREACHABLE;
148 : break;
149 : }
150 417361 : }
151 :
152 150726 : void XmlStreamWriter::startElement(const std::string_view& name, const Attributes& attrs)
153 : {
154 150726 : IF_ASSERT_FAILED(name.find(' ') == std::string_view::npos) {
155 : }
156 :
157 150726 : m_impl->putLevel();
158 150726 : m_impl->stream << '<' << name;
159 171992 : for (const Attribute& a : attrs) {
160 21266 : m_impl->stream << ' ' << a.first << '=' << '\"';
161 21266 : writeValue(a.second);
162 21266 : m_impl->stream << '\"';
163 : }
164 150726 : m_impl->stream << '>' << '\n';
165 150726 : m_impl->stack.emplace_back(name);
166 150726 : }
167 :
168 0 : void XmlStreamWriter::startElement(const String& name, const Attributes& attrs)
169 : {
170 0 : ByteArray ba = name.toAscii();
171 0 : startElement(AsciiStringView(ba.constChar(), ba.size()), attrs);
172 0 : }
173 :
174 12536 : void XmlStreamWriter::startElementRaw(const String& name)
175 : {
176 12536 : m_impl->putLevel();
177 12536 : m_impl->stream << '<' << name << '>' << '\n';
178 12536 : m_impl->stack.push_back(name.split(' ')[0].toStdString());
179 12536 : }
180 :
181 163262 : void XmlStreamWriter::endElement()
182 : {
183 163262 : m_impl->putLevel();
184 163262 : m_impl->stream << "</" << muse::takeLast(m_impl->stack) << '>' << '\n';
185 163262 : }
186 :
187 : // <element attr="value" />
188 8259 : void XmlStreamWriter::element(const std::string_view& name, const Attributes& attrs)
189 : {
190 8259 : IF_ASSERT_FAILED(name.find(' ') == std::string_view::npos) {
191 : }
192 :
193 8259 : m_impl->putLevel();
194 8259 : m_impl->stream << '<' << name;
195 21163 : for (const Attribute& a : attrs) {
196 12904 : m_impl->stream << ' ' << a.first << '=' << '\"';
197 12904 : writeValue(a.second);
198 12904 : m_impl->stream << '\"';
199 : }
200 8259 : m_impl->stream << "/>\n";
201 8259 : }
202 :
203 345984 : void XmlStreamWriter::element(const std::string_view& name, const Value& body)
204 : {
205 345984 : IF_ASSERT_FAILED(name.find(' ') == std::string_view::npos) {
206 : }
207 :
208 345984 : m_impl->putLevel();
209 345984 : m_impl->stream << '<' << name << '>';
210 345984 : writeValue(body);
211 345984 : m_impl->stream << "</" << name << '>' << '\n';
212 345984 : }
213 :
214 17670 : void XmlStreamWriter::element(const std::string_view& name, const Attributes& attrs, const Value& body)
215 : {
216 17670 : IF_ASSERT_FAILED(name.find(' ') == std::string_view::npos) {
217 : }
218 :
219 17670 : m_impl->putLevel();
220 17670 : m_impl->stream << '<' << name;
221 32046 : for (const Attribute& a : attrs) {
222 14376 : m_impl->stream << ' ' << a.first << '=' << '\"';
223 14376 : writeValue(a.second);
224 14376 : m_impl->stream << '\"';
225 : }
226 17670 : m_impl->stream << ">";
227 17670 : writeValue(body);
228 17670 : m_impl->stream << "</" << name << '>' << '\n';
229 17670 : }
230 :
231 8295 : void XmlStreamWriter::elementRaw(const String& nameWithAttributes, const Value& body)
232 : {
233 8295 : m_impl->putLevel();
234 8295 : if (body.index() == 0) {
235 3134 : m_impl->stream << '<' << nameWithAttributes << "/>\n";
236 : } else {
237 5161 : String ename(String(nameWithAttributes).split(' ')[0]);
238 5161 : m_impl->stream << '<' << nameWithAttributes << '>';
239 5161 : writeValue(body);
240 5161 : m_impl->stream << "</" << ename << '>' << '\n';
241 5161 : }
242 8295 : }
243 :
244 5643 : void XmlStreamWriter::elementStringRaw(const String& nameWithAttributes, const String& body)
245 : {
246 5643 : m_impl->putLevel();
247 5643 : if (body.isEmpty()) {
248 41 : m_impl->stream << '<' << nameWithAttributes << "/>\n";
249 : } else {
250 5602 : String ename(String(nameWithAttributes).split(' ')[0]);
251 5602 : m_impl->stream << '<' << nameWithAttributes << '>';
252 5602 : m_impl->stream << body;
253 5602 : m_impl->stream << "</" << ename << '>' << '\n';
254 5602 : }
255 5643 : }
256 :
257 1 : void XmlStreamWriter::comment(const String& text)
258 : {
259 1 : m_impl->putLevel();
260 1 : m_impl->stream << "<!-- " << text << " -->\n";
261 1 : }
|