Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
xmlParser.h 6.19 KiB
/*******************************************************************************
* Copyright (c) 2017, 2018, 2019, 2020 in-tech GmbH
*               2016, 2017, 2018 ITK Engineering GmbH
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

//-----------------------------------------------------------------------------
//! @file  XmlParser.h
//! @brief This file contains helper functions to parse the configuration.
//-----------------------------------------------------------------------------

#pragma once

#include <string>
#include <unordered_map>

#include <QDomDocument>
#include <QFile>

#include "common/log.h"

namespace SimulationCommon {

extern bool GetFirstChildElement(QDomElement rootElement, const std::string &tag, QDomElement &result);

extern bool GetFirstChild(QDomElement rootElement, QDomElement &result);

extern bool GetLastChildElement(QDomElement rootElement, const std::string &tag, QDomElement &result);

extern bool ParseCurrentInt(QDomElement currentElement, int &result);

/// Tempalted wrapper for Parse* functions
template <typename T>
bool Parse(QDomElement rootElement, const std::string &tag, T &result);

extern bool ParseString(QDomElement rootElement, const std::string &tag, std::string &result);

extern bool ParseDouble(QDomElement rootElement, const std::string &tag, double &result);

extern bool ParseDoubleVector(QDomElement rootElement, const std::string &tag, std::vector<double> &result);

extern bool ParseInt(QDomElement rootElement, const std::string &tag, int &result);

extern bool ParseULong(QDomElement rootElement, const std::string &tag, unsigned long &result);

extern bool ParseBool(QDomElement rootElement, const std::string &tag, bool &result);

extern bool ParseAttributeString(QDomElement element, const std::string &attributeName, std::string &result, std::optional<std::string> defaultValue = std::nullopt);

extern bool ParseAttributeDouble(QDomElement element, const std::string &attributeName, double &result, std::optional<double> defaultValue = std::nullopt);

extern bool ParseAttributeInt(QDomElement element, const std::string &attributeName, int &result);

extern bool ParseAttributeBool(QDomElement element, const std::string &attributeName, bool &result);

extern bool ParseAttributeStringVector(QDomElement element, const std::string &attributeName, std::vector<std::string> *result);

extern bool ParseAttributeDoubleVector(QDomElement element, const std::string &attributeName, std::vector<double> *result);

extern bool ParseAttributeIntVector(QDomElement element, const std::string &attributeName, std::vector<int> *result);

extern bool ParseAttributeBoolVector(QDomElement element, const std::string &attributeName, std::vector<bool> *result);

/*!
 *  \brief Parses the value of an XML attribute into the provided container.
 *
 *  Depending on the datatype of the result parameter, different parsing techniques are applied.
 *
 *  \param[in]  element         XML element holding the attribute
 *  \param[in]  attributeName   Name of the attribute to parse the value from
 *  \param[out] result          Container holding the result
 *
 *  \return     True on successful parsing, false otherwise.
 */
template <typename T>
bool ParseAttribute(QDomElement element, const std::string &attributeName, T &result);

//! Returns true if the given element has an attribute of the given name
bool HasAttribute(QDomElement element, const std::string &attributeName);

/*!
* \brief Imports a probability map
*
* \details This method imports a probability map for some value of type T. T can either be int, double or string
*
*
* @param[in]     parentElement          Element containing the information
* @param[in]     key                    Name how the value is specified in the xml file
* @param[in]     tag                    Name of the tags that should be imported
* @param[out]    probabilities          Map where the probabilities get saved
* @param[in]     mustAddUpToOne         flag which specifies wether the sum of all probalities must be 1
* @return	     true, if successful
*/
template <typename T>
bool ImportProbabilityMap(QDomElement parentElement,
                          const std::string key,
                          const QString tag,
                          std::vector<std::pair<T, double>> &probabilities,
                          bool mustAddUpToOne = true)
{
    double probabilitySum = 0.0;

    QDomElement childElement;
    if (!GetFirstChildElement(parentElement, tag.toStdString(), childElement))
    {
        LOG_INTERN(LogLevel::Error) << "At least one element is required.";
        return false;
    }

    while (!childElement.isNull())
    {
        T keyValue;
        double probability;

        if (!ParseAttribute<T>(childElement, key, keyValue))
        {
            LOG_INTERN(LogLevel::Error) << "Key is invalid.";
            return false;
        }

        if (!ParseAttributeDouble(childElement, "Probability", probability))
        {
            LOG_INTERN(LogLevel::Error) << "Probability is invalid.";
            return false;
        }

        probabilities.push_back({keyValue, probability});

        probabilitySum += probability;

        childElement = childElement.nextSiblingElement(tag);
    }

    //Checks probabilities
    if (mustAddUpToOne && std::abs(probabilitySum - 1.0) > 1e-6)
    {
        LOG_INTERN(LogLevel::Error) << "Probabilities do not add up to 1.0.";
        return false;
    }

    if (probabilitySum > 1.0 + 1e-6)
    {
        LOG_INTERN(LogLevel::Error) << "Probabilities add up to more than 1.0.";
        return false;
    }

    return true;
}

} // namespace SimulationCommon

[[maybe_unused]] static void ThrowIfFalse(bool success, const QDomElement element, const std::string &message)
{
    if (!success)
    {
        LogErrorAndThrow("Could not import element " + element.tagName().toStdString() +
                         " (line " + std::to_string(element.lineNumber()) +
                         ", column " + std::to_string(element.columnNumber()) + "): " +
                         message);
    }
}