dcxmlbase.cpp 8.8 KB
#include "dcxmlbase.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;

void xml_string_writer::write(const void* data, size_t size)
{
    result += std::string(static_cast<const char*>(data), size);
}

DcXmlBase::DcXmlBase(const QString& xmlFile)
    : m_xmldoc()
    , m_xPathHash()
{
    if (!xmlFile.isNull() || !xmlFile.isEmpty()) {
        if (parseFile(xmlFile))
        {
            LogDebug("[DcXmlBase::DcXmlBase]", std::string("File : " + xmlFile + "..............[OK]."));
        }
        else
        {
            LogError("[DcXmlBase::DcXmlBase]", QString("File : %1 ..............[Failed].").arg(xmlFile));
            throw std::runtime_error("[DcXmlBase::DcXmlBase] parseFile failed");
        }
    }
}

DcXmlBase::~DcXmlBase() = default;

bool DcXmlBase::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 DcXmlBase::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 DcXmlBase::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 <node attr=1> 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. <node></nedo>) 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("[DcXmlBase::checkError]", sLogMessage);
    return bResult;
}

void DcXmlBase::addXPath(const QString& qsName, const QString& qsXPath)
{
    if (m_xPathHash.contains(qsName)) {
        LogWarning("[DcXmlBase::addXPath]", "XPath already registered : " + qsName);
    }
    m_xPathHash.insert(qsName, qsXPath);

    LogDebug("[DcXmlBase::addXPath]", QString("XPath" + qsXPath + " registered with key : " + qsName));
}

QString DcXmlBase::getXPath(const QString& qsXPathSelect) const
{
    QString qsXPath = m_xPathHash.value(qsXPathSelect);

    if (qsXPath.isEmpty()) {
        LogWarning("dcxml", "XPath not registered : " + qsXPathSelect);
    }

    return qsXPath;
}

QString DcXmlBase::evaluateXPath(const QString& qsXPathSelect,
    const QList<QVariant>& arguments) const
{
    QString qsResult = getXPath(qsXPathSelect);

    // LogInfo( "DcXmlBase::evaluateXPath", QString( "Found XPathExpression : " + qsResult + " for selection : " + qsXPathSelect ) );

    for (auto& value : arguments) {
        qsResult = qsResult.arg(value.toString());
    }

    // LogInfo( "DcXmlBase::evaluateXPath", QString( "Resulting XPathExpression : " + qsResult ) );
    return qsResult;
}

void DcXmlBase::setSimpleData(const QString& qsXPathSelect,
    const QList<QVariant>& arguments,
    const QVariant& data)
{
    QString qsXPath = evaluateXPath(qsXPathSelect, arguments);
    setNodeData(qsXPath, data);
}

void DcXmlBase::setSimpleData(const QString& qsXPathSelect, const QVariant& data)
{
    QString qsXPath = getXPath(qsXPathSelect);

    setNodeData(qsXPath, data);
}

QVariant DcXmlBase::getSimpleData(const QString& qsXPathSelect,
    const QList<QVariant>& arguments) const
{
    QString qsXPath = evaluateXPath(qsXPathSelect, arguments);

    QVariant qvResult = getNodeData(qsXPath);

    return qvResult;
}

// static
bool DcXmlBase::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 DcXmlBase::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 DcXmlBase::selectNode(const QString& qsXPath) const
{
    return m_xmldoc.select_node(qsXPath.toStdString().c_str());
}

QList<pugi::xpath_node> DcXmlBase::selectNodes(const QString& qsXPath) const
{
    QList<pugi::xpath_node> lstResult;

    pugi::xpath_node_set nodes = m_xmldoc.select_nodes(qsXPath.toStdString().c_str());
    for (auto& node : nodes) {
        lstResult.append(node);
    }

    return lstResult;
}

void DcXmlBase::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("dcxml",
            QString("No node(s) found for XPath expression : '%1'")
                .arg(qsXPath));
    }
}

QVariant DcXmlBase::getNodeData(const QString& qsXPath) const
{
    QVariant qvResult;
    pugi::xml_node selectedNode = selectNode(qsXPath).node();
    if (!selectedNode.empty()) {
        qvResult = QString(selectedNode.value());
    }
    return qvResult;
}

QString DcXmlBase::asString() const
{
    xml_string_writer writer;
    m_xmldoc.save(writer);

    return QString(writer.result.c_str());
}