/* **************************************************************************** * Copyright 2019 Open Systems Development BV * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * * DEALINGS IN THE SOFTWARE. * * ***************************************************************************/ #include "xmlbase.h" #include "log.h" /* * ________________________________________ * / Distance doesn't make you any smaller, \ * | but it does make you part of a larger | * \ picture. / * ---------------------------------------- * \ * \ * .--. * |o_o | * |:_/ | * // \ \ * (| | ) * /'\_ _/`\ * \___)=(___/ * **********************************************************/ using namespace osdev::components::xml; using namespace osdev::components::log; void xml_string_writer::write(const void* data, size_t size) { result += std::string(static_cast(data), size); } XmlBase::XmlBase(const QString& xmlFile) : m_xmldoc() , m_xPathHash() { if (!xmlFile.isNull() || !xmlFile.isEmpty()) { if (parseFile(xmlFile)) { LogDebug("[XmlBase::XmlBase]", QString("File : %1 ..............[OK].").arg(xmlFile).toStdString()); } else { LogError("[XmlBase::XmlBase]", QString("File : %1 ..............[Failed].").arg(xmlFile).toStdString()); throw std::runtime_error("[XmlBase::XmlBase] parseFile failed"); } } } XmlBase::~XmlBase() = default; bool XmlBase::parseString(const QString& qsXml) { bool bResult = false; if (!qsXml.isEmpty()) { pugi::xml_parse_status parseStatus = m_xmldoc.load_buffer(qsXml.toStdString().c_str(), qsXml.toStdString().size()) .status; bResult = checkError(parseStatus); } return bResult; } bool XmlBase::parseFile(const QString& qsXml) { bool bResult = false; if (!qsXml.isEmpty()) { pugi::xml_parse_status parseStatus = m_xmldoc.load_file(qsXml.toStdString().c_str()).status; bResult = checkError(parseStatus); } return bResult; } bool XmlBase::checkError(pugi::xml_parse_status _parseStatus) { bool bResult = false; QString sLogMessage = "An unknown error occured."; /* * GCC 5.3.1 insits on all enumeration cases for this switch to be specified. * The enumeration cases that throws -Wswitch warning are not available in pugixml for FC21. * For backward compatibility with older pugixml version "-Wswitch" warnings are temporarily supressed. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch" switch (_parseStatus) { case pugi::status_ok: sLogMessage = "File parsed successfully."; bResult = true; break; case pugi::status_file_not_found: sLogMessage = "File not found."; break; case pugi::status_io_error: sLogMessage = "Some I/O Error occured during reading the file / stream. Check the hardware for errors."; break; case pugi::status_out_of_memory: sLogMessage = "Out of Memory while parsing the file."; break; case pugi::status_internal_error: sLogMessage = "Oh dear... That's different. Something went horribly wrong. It is unrecoverable. We're exiting the whole shabang.."; break; case pugi::status_unrecognized_tag: sLogMessage = "Parsing stopping due to a tag with either an empty name or a name which starts with an incorrect character (Left a '#' somewhere?)"; break; case pugi::status_bad_pi: sLogMessage = "Parsing stopped due to incorrect document declaration/processing instruction. "; break; case pugi::status_bad_comment: sLogMessage = "Parsing stopped due to the invalid construct of a Comment."; break; case pugi::status_bad_cdata: sLogMessage = "Parsing stopped due to the invalid construct of a CDATA section."; break; case pugi::status_bad_doctype: sLogMessage = "Parsing stopped due to the invalid construct of a DocType."; break; case pugi::status_bad_pcdata: sLogMessage = "Parsing stopped due to the invalid construct of a PCDATA section."; break; case pugi::status_bad_attribute: sLogMessage = "Parsing stopped because there was an incorrect attribute, such as an attribute without value or with value that is not quoted (note that is incorrect in XML)."; break; case pugi::status_bad_start_element: sLogMessage = "Parsing stopped because a starting tag either had no closing > symbol or contained some incorrect symbol."; break; case pugi::status_bad_end_element: sLogMessage = "Parsing stopped because ending tag had incorrect syntax (i.e. extra non-whitespace symbols between tag name and >)."; break; case pugi::status_end_element_mismatch: sLogMessage = "parsing stopped because the closing tag did not match the opening one (i.e. ) or because some tag was not closed at all."; break; // DISABLED ON FEDORA 21 and CentOS 7. Not present in pugixml 1.0-8.fc21 // case pugi::status_append_invalid_root: // case pugi::status_no_document_element: } #pragma GCC diagnostic pop LogDebug("[XmlBase::checkError]", sLogMessage.toStdString()); return bResult; } void XmlBase::addXPath(const QString& qsName, const QString& qsXPath) { if (m_xPathHash.contains(qsName)) { LogWarning("[XmlBase::addXPath]", QString( "XPath already registered : " + qsName).toStdString()); } m_xPathHash.insert(qsName, qsXPath); LogDebug("[XmlBase::addXPath]", QString("XPath" + qsXPath + " registered with key : " + qsName).toStdString()); } QString XmlBase::getXPath(const QString& qsXPathSelect) const { QString qsXPath = m_xPathHash.value(qsXPathSelect); if (qsXPath.isEmpty()) { LogWarning("[XmlBase::getXPath]", QString("XPath not registered : " + qsXPathSelect).toStdString()); } return qsXPath; } QString XmlBase::evaluateXPath(const QString& qsXPathSelect, const QList& arguments) const { QString qsResult = getXPath(qsXPathSelect); LogDebug( "[XmlBase::evaluateXPath]", QString( "Found XPathExpression : " + qsResult + " for selection : " + qsXPathSelect ).toStdString() ); for (auto& value : arguments) { qsResult = qsResult.arg(value.toString()); } LogInfo("[XmlBase::evaluateXPath]", QString("Resulting XPathExpression : " + qsResult).toStdString()); return qsResult; } void XmlBase::setSimpleData(const QString& qsXPathSelect, const QList& arguments, const QVariant& data) { QString qsXPath = evaluateXPath(qsXPathSelect, arguments); setNodeData(qsXPath, data); } void XmlBase::setSimpleData(const QString& qsXPathSelect, const QVariant& data) { QString qsXPath = getXPath(qsXPathSelect); setNodeData(qsXPath, data); } QVariant XmlBase::getSimpleData(const QString& qsXPathSelect, const QList& arguments) const { QString qsXPath = evaluateXPath(qsXPathSelect, arguments); QVariant qvResult = getNodeData(qsXPath); return qvResult; } // static bool XmlBase::getBoolean(const QVariant& value) { bool b_result = false; QString l_result = value.toString().toUpper(); if ( // ------------------------ ("Y" == l_result) || ("YES" == l_result) || ("TRUE" == l_result) || ("ON" == l_result) || ("1" == l_result) // ------------------------ ) { b_result = true; } return b_result; } // static QString XmlBase::getAttributeValue(const pugi::xml_node& xmlNode, const char* attributeName) { const auto attr = xmlNode.attribute(attributeName); if (attr.empty()) { return {}; } return attr.value(); } pugi::xpath_node XmlBase::selectNode(const QString& qsXPath) const { return m_xmldoc.select_node(qsXPath.toStdString().c_str()); } QList XmlBase::selectNodes(const QString& qsXPath) const { QList lstResult; pugi::xpath_node_set nodes = m_xmldoc.select_nodes(qsXPath.toStdString().c_str()); for (auto& node : nodes) { lstResult.append(node); } return lstResult; } void XmlBase::setNodeData(const QString& qsXPath, const QVariant& qsData) { pugi::xml_node selectedNode; selectedNode = selectNode(qsXPath).node(); if (!selectedNode.empty()) { selectedNode.set_value(qsData.toString().toStdString().c_str()); } else { LogError("[XmlBase::setNodeData]", QString("No node(s) found for XPath expression : '%1'").arg(qsXPath).toStdString()); } } QVariant XmlBase::getNodeData(const QString& qsXPath) const { QVariant qvResult; pugi::xml_node selectedNode = selectNode(qsXPath).node(); if (!selectedNode.empty()) { qvResult = QString(selectedNode.value()); } return qvResult; } QString XmlBase::asString() const { xml_string_writer writer; m_xmldoc.save(writer); return QString(writer.result.c_str()); }