LCOV - code coverage report
Current view: top level - muse/framework/global/serialization - xmldom.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 81.6 % 158 129
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 "xmldom.h"
      23              : 
      24              : #include <algorithm>
      25              : #include <cstring>
      26              : #include <type_traits>
      27              : 
      28              : #include "pugixml.hpp"
      29              : 
      30              : #include "log.h"
      31              : 
      32              : using namespace muse;
      33              : 
      34              : struct muse::XmlDomImplData
      35              : {
      36              :     pugi::xml_document doc;
      37              :     pugi::xml_parse_result result{};
      38              :     bool triedload = false;
      39              : };
      40              : 
      41              : using xml_node_impl = pugi::xml_node;
      42              : using xml_attr_impl = pugi::xml_attribute;
      43              : 
      44              : // ================================================
      45              : // generic pack/unpack
      46              : // ================================================
      47              : 
      48              : namespace {
      49              : template<class T>
      50       136239 : inline xml_handle pack_handle(const T& t) noexcept
      51              : {
      52              :     static_assert(std::is_trivially_copyable_v<T>,
      53              :                   "Backend handle must be trivially copyable");
      54              :     static_assert(sizeof(T) <= sizeof(xml_handle),
      55              :                   "Increase xml_handle slots/size");
      56       136239 :     xml_handle h{};                 // zero both slots
      57       136239 :     const auto* src = reinterpret_cast<const unsigned char*>(&t);
      58       136239 :     auto* dst = reinterpret_cast<unsigned char*>(&h);
      59       136239 :     std::copy_n(src, sizeof(T), dst);
      60       136239 :     return h;
      61              : }
      62              : 
      63              : template<class T>
      64       216296 : inline T unpack_handle(xml_handle h) noexcept
      65              : {
      66              :     static_assert(std::is_trivially_copyable_v<T>,
      67              :                   "Backend handle must be trivially copyable");
      68              :     static_assert(sizeof(T) <= sizeof(xml_handle),
      69              :                   "Increase xml_handle slots/size");
      70       216296 :     T t{};                          // zero-init destination
      71       216296 :     const auto* src = reinterpret_cast<const unsigned char*>(&h);
      72       216296 :     auto* dst = reinterpret_cast<unsigned char*>(&t);
      73       216296 :     std::copy_n(src, sizeof(T), dst);
      74       216296 :     return t;
      75              : }
      76              : } // anonymous namespace
      77              : 
      78              : // ================================================
      79              : // XmlDomNode
      80              : // ================================================
      81              : 
      82       117165 : XmlDomNode::XmlDomNode(const std::shared_ptr<XmlDomImplData>& xml, xml_node_handle node)
      83       117165 :     : m_xml(xml), m_node(node)
      84              : {
      85       117165 : }
      86              : 
      87        64902 : bool XmlDomNode::isNull() const
      88              : {
      89        64902 :     return !static_cast<bool>(m_node);
      90              : }
      91              : 
      92        37058 : String XmlDomNode::nodeName() const
      93              : {
      94        37058 :     if (!m_node) {
      95            0 :         return String();
      96              :     }
      97              : 
      98        37058 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node);
      99        37058 :     switch (n.type()) {
     100        37058 :     case pugi::node_element:
     101              :     case pugi::node_pi:
     102              :     case pugi::node_declaration:
     103              :     case pugi::node_doctype:
     104              :     case pugi::node_document: // usually empty
     105        37058 :         return String::fromUtf8(n.name());
     106            0 :     case pugi::node_pcdata:
     107              :     case pugi::node_cdata:
     108            0 :         return String::fromUtf8(n.value());
     109            0 :     case pugi::node_comment:
     110            0 :         return String::fromUtf8(n.value());
     111            0 :     case pugi::node_null:
     112              :     default:
     113            0 :         return String();
     114              :     }
     115              : }
     116              : 
     117          305 : bool XmlDomNode::hasChildNodes() const
     118              : {
     119          305 :     return m_node ? static_cast<bool>(unpack_handle<xml_node_impl>(m_node).first_child()) : false;
     120              : }
     121              : 
     122        21026 : XmlDomNode XmlDomNode::firstChild() const
     123              : {
     124        21026 :     if (!m_node) {
     125           76 :         return XmlDomNode(m_xml, xml_node_handle());
     126              :     }
     127        20950 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node).first_child();
     128        20950 :     return XmlDomNode(m_xml, pack_handle(n));
     129              : }
     130              : 
     131         7300 : XmlDomElement XmlDomNode::firstChildElement(const char* name) const
     132              : {
     133         7300 :     if (!m_node) {
     134            0 :         return XmlDomElement(m_xml, xml_node_handle());
     135              :     }
     136         7300 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node);
     137         7300 :     xml_node_impl c = name ? n.child(name)
     138            5 :                       : n.find_child([](xml_node_impl x){ return x.type() == pugi::node_element; });
     139         7300 :     if (c && c.type() == pugi::node_element) {
     140         6425 :         return XmlDomElement(m_xml, pack_handle(c));
     141              :     }
     142          875 :     return XmlDomElement(m_xml, xml_node_handle());
     143              : }
     144              : 
     145        49224 : XmlDomNode XmlDomNode::nextSibling() const
     146              : {
     147        49224 :     if (!m_node) {
     148            0 :         return XmlDomNode(m_xml, xml_node_handle());
     149              :     }
     150        49224 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node).next_sibling();
     151        49224 :     return XmlDomNode(m_xml, pack_handle(n));
     152              : }
     153              : 
     154            0 : XmlDomNode XmlDomNode::previousSibling() const
     155              : {
     156            0 :     if (!m_node) {
     157            0 :         return XmlDomNode(m_xml, xml_node_handle());
     158              :     }
     159            0 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node).previous_sibling();
     160            0 :     return XmlDomNode(m_xml, pack_handle(n));
     161              : }
     162              : 
     163            1 : XmlDomNode XmlDomNode::parent() const
     164              : {
     165            1 :     if (!m_node) {
     166            0 :         return XmlDomNode(m_xml, xml_node_handle());
     167              :     }
     168            1 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node).parent();
     169            1 :     return XmlDomNode(m_xml, pack_handle(n));
     170              : }
     171              : 
     172            1 : XmlDomElement XmlDomNode::nextSiblingElement(const char* name) const
     173              : {
     174            1 :     if (!m_node) {
     175            0 :         return XmlDomElement(m_xml, xml_node_handle());
     176              :     }
     177            1 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node);
     178            1 :     xml_node_impl s = name ? n.next_sibling(name) : n.next_sibling();
     179            1 :     if (!name) {
     180            2 :         while (s && s.type() != pugi::node_element) {
     181            1 :             s = s.next_sibling();
     182              :         }
     183              :     }
     184            1 :     if (s && s.type() == pugi::node_element) {
     185            1 :         return XmlDomElement(m_xml, pack_handle(s));
     186              :     }
     187            0 :     return XmlDomElement(m_xml, xml_node_handle());
     188              : }
     189              : 
     190            1 : XmlDomElement XmlDomNode::previousSiblingElement(const char* name) const
     191              : {
     192            1 :     if (!m_node) {
     193            0 :         return XmlDomElement(m_xml, xml_node_handle());
     194              :     }
     195            1 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node);
     196            1 :     xml_node_impl s = name ? n.previous_sibling(name) : n.previous_sibling();
     197            1 :     if (!name) {
     198            2 :         while (s && s.type() != pugi::node_element) {
     199            1 :             s = s.previous_sibling();
     200              :         }
     201              :     }
     202            1 :     if (s && s.type() == pugi::node_element) {
     203            1 :         return XmlDomElement(m_xml, pack_handle(s));
     204              :     }
     205            0 :     return XmlDomElement(m_xml, xml_node_handle());
     206              : }
     207              : 
     208        39386 : XmlDomElement XmlDomNode::toElement() const
     209              : {
     210        39386 :     if (!m_node) {
     211            0 :         return XmlDomElement(m_xml, xml_node_handle());
     212              :     }
     213        39386 :     xml_node_impl n = unpack_handle<xml_node_impl>(m_node);
     214        39386 :     if (n.type() == pugi::node_element) {
     215        39386 :         return XmlDomElement(m_xml, pack_handle(n));
     216              :     }
     217            0 :     return XmlDomElement(m_xml, xml_node_handle());
     218              : }
     219              : 
     220              : // ================================================
     221              : // XmlDomAttribute
     222              : // ================================================
     223              : 
     224        20025 : XmlDomAttribute::XmlDomAttribute(const std::shared_ptr<XmlDomImplData>& data, xml_attr_handle attribute)
     225        20025 :     : m_xml(data), m_attribute(attribute)
     226              : {
     227        20025 : }
     228              : 
     229            7 : bool XmlDomAttribute::isNull() const
     230              : {
     231            7 :     return !static_cast<bool>(m_attribute);
     232              : }
     233              : 
     234            1 : String XmlDomAttribute::attributeName() const
     235              : {
     236            1 :     if (!m_attribute) {
     237            0 :         return String();
     238              :     }
     239            1 :     xml_attr_impl a = unpack_handle<xml_attr_impl>(m_attribute);
     240            1 :     return String::fromUtf8(a.name());
     241              : }
     242              : 
     243        20021 : String XmlDomAttribute::value() const
     244              : {
     245        20021 :     if (!m_attribute) {
     246          172 :         return String();
     247              :     }
     248        19849 :     xml_attr_impl a = unpack_handle<xml_attr_impl>(m_attribute);
     249        19849 :     return String::fromUtf8(a.value());
     250              : }
     251              : 
     252            3 : XmlDomAttribute XmlDomAttribute::nextAttribute() const
     253              : {
     254            3 :     if (!m_attribute) {
     255            0 :         return XmlDomAttribute(m_xml, xml_attr_handle());
     256              :     }
     257            3 :     xml_attr_impl a = unpack_handle<xml_attr_impl>(m_attribute).next_attribute();
     258            3 :     return XmlDomAttribute(m_xml, pack_handle(a));
     259              : }
     260              : 
     261              : // ================================================
     262              : // XmlDomElement
     263              : // ================================================
     264              : 
     265        46914 : XmlDomElement::XmlDomElement(const std::shared_ptr<XmlDomImplData>& data, xml_node_handle node)
     266        46914 :     : XmlDomNode(data, node)
     267              : {
     268        46914 : }
     269              : 
     270        22390 : String XmlDomElement::text() const
     271              : {
     272        22390 :     xml_node_impl e = unpack_handle<xml_node_impl>(m_node);
     273        22390 :     if (!e || e.type() != pugi::node_element) {
     274            0 :         return String();
     275              :     }
     276        22390 :     String result;
     277        65296 :     for (xml_node_impl c = e.first_child(); c; c = c.next_sibling()) {
     278        21453 :         const pugi::xml_node_type t = c.type();
     279        21453 :         if (t == pugi::node_pcdata || t == pugi::node_cdata) {
     280        21452 :             result += String::fromUtf8(c.value());
     281              :         }
     282              :     }
     283        22390 :     return result;
     284        22390 : }
     285              : 
     286            1 : XmlDomAttribute XmlDomElement::firstAttribute() const
     287              : {
     288            1 :     if (!m_node) {
     289            0 :         return XmlDomAttribute(m_xml, xml_attr_handle());
     290              :     }
     291            1 :     xml_node_impl e = unpack_handle<xml_node_impl>(m_node);
     292            1 :     if (e && e.type() == pugi::node_element) {
     293            1 :         return XmlDomAttribute(m_xml, pack_handle(e.first_attribute()));
     294              :     }
     295            0 :     return XmlDomAttribute(m_xml, xml_attr_handle());
     296              : }
     297              : 
     298        20021 : XmlDomAttribute XmlDomElement::attribute(const char* name) const
     299              : {
     300        20021 :     if (!m_node) {
     301            0 :         return XmlDomAttribute(m_xml, xml_attr_handle());
     302              :     }
     303        20021 :     xml_node_impl e = unpack_handle<pugi::xml_node>(m_node);
     304        20021 :     if (e && e.type() == pugi::node_element) {
     305        20021 :         return XmlDomAttribute(m_xml, pack_handle(e.attribute(name)));
     306              :     }
     307            0 :     return XmlDomAttribute(m_xml, xml_attr_handle());
     308              : }
     309              : 
     310              : // ================================================
     311              : // XmlDomDocument
     312              : // ================================================
     313              : 
     314          228 : XmlDomDocument::XmlDomDocument()
     315              : {
     316          228 :     m_xml = std::make_shared<XmlDomImplData>();
     317          228 : }
     318              : 
     319          228 : void XmlDomDocument::setContent(const ByteArray& data)
     320              : {
     321          230 :     TRACEFUNC;
     322              : 
     323          228 :     m_xml->doc.reset();
     324          228 :     m_xml->result = m_xml->doc.load_buffer(data.constData(), data.size());
     325          228 :     m_xml->triedload = true;
     326              : 
     327          228 :     if (m_xml->result.status != pugi::status_ok) {
     328            2 :         LOGE() << errorString();
     329              :     }
     330          228 : }
     331              : 
     332          226 : XmlDomElement XmlDomDocument::rootElement() const
     333              : {
     334          226 :     xml_node_impl e = m_xml->doc.document_element();
     335          452 :     return XmlDomElement(m_xml, pack_handle(e));
     336              : }
     337              : 
     338            3 : bool XmlDomDocument::hasError() const
     339              : {
     340            3 :     return m_xml->triedload && m_xml->result.status != pugi::status_ok;
     341              : }
     342              : 
     343            3 : String XmlDomDocument::errorString() const
     344              : {
     345            3 :     if (m_xml->triedload) {
     346            3 :         return String::fromUtf8(m_xml->result.description());
     347              :     }
     348            0 :     return String();
     349              : }
        

Generated by: LCOV version 2.0-1