LCOV - code coverage report
Current view: top level - source/tools/atlas/AtlasObject - AtlasObjectXML.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 66 68 97.1 %
Date: 2023-01-19 00:18:29 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "AtlasObject.h"
      19             : #include "AtlasObjectImpl.h"
      20             : 
      21             : #include <cassert>
      22             : #include <libxml/parser.h>
      23             : #include <string>
      24             : 
      25             : // TODO: replace most of the asserts below (e.g. for when it fails to load
      26             : // a file) with some proper logging/reporting system
      27             : 
      28             : static AtSmartPtr<AtNode> ConvertNode(xmlNodePtr node);
      29             : 
      30          13 : AtObj AtlasObject::LoadFromXML(const std::string& xml)
      31             : {
      32          13 :     xmlDocPtr doc = xmlReadMemory(xml.c_str(), xml.length(), "noname.xml", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
      33          13 :     if (doc == NULL)
      34           0 :         return AtObj();
      35             :         // TODO: Need to report the error message somehow
      36             : 
      37          13 :     xmlNodePtr root = xmlDocGetRootElement(doc);
      38          26 :     AtObj obj;
      39          13 :     obj.m_Node = ConvertNode(root);
      40             : 
      41          26 :     AtObj rootObj;
      42          13 :     rootObj.set((const char*)root->name, obj);
      43             : 
      44          13 :     xmlFreeDoc(doc);
      45             : 
      46          13 :     return rootObj;
      47             : }
      48             : 
      49             : // Convert from a DOMElement to an AtNode
      50          21 : static AtSmartPtr<AtNode> ConvertNode(xmlNodePtr node)
      51             : {
      52          21 :     AtSmartPtr<AtNode> obj (new AtNode());
      53             : 
      54             :     // Loop through all attributes
      55          28 :     for (xmlAttrPtr cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next)
      56             :     {
      57          14 :         std::string name ("@");
      58           7 :         name += reinterpret_cast<const char*>(cur_attr->name);
      59           7 :         xmlChar* content = xmlNodeGetContent(cur_attr->children);
      60          14 :         std::string value = reinterpret_cast<char*>(content);
      61           7 :         xmlFree(content);
      62             : 
      63           7 :         AtNode* newNode = new AtNode(value.c_str());
      64          14 :         obj->m_Children.insert(AtNode::child_pairtype(
      65          14 :             name.c_str(), AtNode::Ptr(newNode)
      66           7 :         ));
      67             :     }
      68             : 
      69             :     // Loop through all child elements
      70          41 :     for (xmlNodePtr cur_node = node->children; cur_node; cur_node = cur_node->next)
      71             :     {
      72          20 :         if (cur_node->type == XML_ELEMENT_NODE)
      73             :         {
      74          16 :             obj->m_Children.insert(AtNode::child_pairtype(
      75          16 :                 reinterpret_cast<const char*>(cur_node->name), ConvertNode(cur_node)
      76           8 :             ));
      77             :         }
      78          12 :         else if (cur_node->type == XML_TEXT_NODE)
      79             :         {
      80          10 :             xmlChar* content = xmlNodeGetContent(cur_node);
      81          20 :             std::string value = reinterpret_cast<char*>(content);
      82          10 :             xmlFree(content);
      83          10 :             obj->m_Value += value;
      84             :         }
      85             :     }
      86             : 
      87             :     // Trim whitespace surrounding the string value
      88          42 :     const std::string whitespace = " \t\r\n";
      89          21 :     size_t first = obj->m_Value.find_first_not_of(whitespace);
      90          21 :     if (first == std::string::npos)
      91          13 :         obj->m_Value = "";
      92             :     else
      93             :     {
      94           8 :         size_t last = obj->m_Value.find_last_not_of(whitespace);
      95           8 :         obj->m_Value = obj->m_Value.substr(first, 1+last-first);
      96             :     }
      97             : 
      98          42 :     return obj;
      99             : }
     100             : 
     101             : // Build a DOM node from a given AtNode
     102          34 : static void BuildDOMNode(xmlDocPtr doc, xmlNodePtr node, AtNode::Ptr p)
     103             : {
     104          34 :     if (p)
     105             :     {
     106          34 :         if (p->m_Value.length())
     107           8 :             xmlNodeAddContent(node, reinterpret_cast<const xmlChar*>(p->m_Value.c_str()));
     108             : 
     109          62 :         for (const AtNode::child_maptype::value_type& child : p->m_Children)
     110             :         {
     111          28 :             const xmlChar* first_child = reinterpret_cast<const xmlChar*>(child.first.c_str());
     112             : 
     113             :             // Test for attribute nodes (whose names start with @)
     114          28 :             if (child.first.length() && child.first[0] == '@')
     115             :             {
     116           7 :                 assert(child.second);
     117           7 :                 assert(child.second->m_Children.empty());
     118           7 :                 xmlNewProp(node, first_child + 1, reinterpret_cast<const xmlChar*>(child.second->m_Value.c_str()));
     119             :             }
     120             :             else
     121             :             {
     122             :                 // First node in the document - needs to be made the root node
     123          21 :                 if (node == nullptr)
     124             :                 {
     125          13 :                     xmlNodePtr root = xmlNewNode(nullptr, first_child);
     126          13 :                     xmlDocSetRootElement(doc, root);
     127          13 :                     BuildDOMNode(doc, root, child.second);
     128             :                 }
     129             :                 else
     130             :                 {
     131           8 :                     xmlNodePtr newChild = xmlNewChild(node, nullptr, first_child, nullptr);
     132           8 :                     BuildDOMNode(doc, newChild, child.second);
     133             :                 }
     134             :             }
     135             :         }
     136             :     }
     137          34 : }
     138             : 
     139          13 : std::string AtlasObject::SaveToXML(AtObj& obj)
     140             : {
     141          13 :     if (!obj.m_Node || obj.m_Node->m_Children.size() != 1)
     142             :     {
     143           0 :         assert(! "SaveToXML: root must only have one child");
     144             :         return "";
     145             :     }
     146             : 
     147          26 :     AtNode::Ptr firstChild (obj.m_Node->m_Children.begin()->second);
     148             : 
     149          13 :     xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0");
     150          13 :     BuildDOMNode(doc, nullptr, obj.m_Node);
     151             : 
     152             :     xmlChar* buf;
     153             :     int size;
     154          13 :     xmlDocDumpFormatMemoryEnc(doc, &buf, &size, "utf-8", 1);
     155             : 
     156          26 :     std::string ret((const char*)buf, size);
     157             : 
     158          13 :     xmlFree(buf);
     159          13 :     xmlFreeDoc(doc);
     160             : 
     161             :     // TODO: handle errors better
     162             : 
     163          13 :     return ret;
     164             : }

Generated by: LCOV version 1.13