LCOV - code coverage report
Current view: top level - muse/framework/global/serialization - xmlstreamwriter.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 87.7 % 155 136
Test Date: 2026-11-05 04:07:02

            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 "&lt;"s;
      91         1148 :     case U'>':
      92         1148 :         return "&gt;"s;
      93            6 :     case U'&':
      94            6 :         return "&amp;"s;
      95          287 :     case U'\"':
      96          287 :         return "&quot;"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 : }
        

Generated by: LCOV version 2.0-1