/*
* Copyright (c) 2011-2015, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "DomainConfiguration.h"
#include "ConfigurableElement.h"
#include "CompoundRule.h"
#include "Subsystem.h"
#include "XmlDomainSerializingContext.h"
#include "XmlDomainImportContext.h"
#include "XmlDomainExportContext.h"
#include "ConfigurationAccessContext.h"
#include "AlwaysAssert.hpp"
#include <assert.h>
#include <cstdlib>
#include <algorithm>
#include <numeric>
#include "RuleParser.h"
#define base CElement
using std::string;
CDomainConfiguration::CDomainConfiguration(const string &strName) : base(strName)
{
}
// Class kind
string CDomainConfiguration::getKind() const
{
return "Configuration";
}
// Child dynamic creation
bool CDomainConfiguration::childrenAreDynamic() const
{
return true;
}
// XML configuration settings parsing
bool CDomainConfiguration::parseSettings(CXmlElement &xmlConfigurationSettingsElement,
CXmlDomainImportContext &context)
{
// Parse configurable element's configuration settings
CXmlElement::CChildIterator it(xmlConfigurationSettingsElement);
CXmlElement xmlConfigurableElementSettingsElement;
auto insertLocation = begin(mAreaConfigurationList);
while (it.next(xmlConfigurableElementSettingsElement)) {
// Retrieve area configuration
string configurableElementPath;
xmlConfigurableElementSettingsElement.getAttribute("Path", configurableElementPath);
auto areaConfiguration = findAreaConfigurationByPath(configurableElementPath);
if (areaConfiguration == end(mAreaConfigurationList)) {
context.setError("Configurable Element " + configurableElementPath +
" referred to by Configuration " + getPath() +
" not associated to Domain");
return false;
}
// Parse
if (!importOneConfigurableElementSettings(areaConfiguration->get(),
xmlConfigurableElementSettingsElement, context)) {
return false;
}
// Take into account the new configuration order by moving the configuration associated to
// the element to the n-th position of the configuration list.
// It will result in prepending to the configuration list wit the configuration of all
// elements found in XML, keeping the order of the processing of the XML file.
mAreaConfigurationList.splice(insertLocation, mAreaConfigurationList, areaConfiguration);
// areaConfiguration is still valid, but now refer to the reorderer list
insertLocation = std::next(areaConfiguration);
}
return true;
}
// XML configuration settings composing
void CDomainConfiguration::composeSettings(CXmlElement &xmlConfigurationSettingsElement,
CXmlDomainExportContext &context) const
{
// Go through all are configurations
for (auto &areaConfiguration : mAreaConfigurationList) {
// Retrieve configurable element
const CConfigurableElement *pConfigurableElement =
areaConfiguration->getConfigurableElement();
// Create configurable element child element
CXmlElement xmlConfigurableElementSettingsElement;
xmlConfigurationSettingsElement.createChild(xmlConfigurableElementSettingsElement,
"ConfigurableElement");
// Set Path attribute
xmlConfigurableElementSettingsElement.setAttribute("Path", pConfigurableElement->getPath());
// Delegate composing to area configuration
exportOneConfigurableElementSettings(areaConfiguration.get(),
xmlConfigurableElementSettingsElement, context);
}
}
// Serialize one configuration for one configurable element
bool CDomainConfiguration::importOneConfigurableElementSettings(
CAreaConfiguration *areaConfiguration, CXmlElement &xmlConfigurableElementSettingsElement,
CXmlDomainImportContext &context)
{
const CConfigurableElement *destination = areaConfiguration->getConfigurableElement();
// Check structure
if (xmlConfigurableElementSettingsElement.getNbChildElements() != 1) {
// Structure error
context.setError("Struture error encountered while parsing settings of " +
destination->getKind() + " " + destination->getName() +
" in Configuration " + getPath());
return false;
}
// Element content
CXmlElement xmlConfigurableElementSettingsElementContent;
// Check name and kind
if (!xmlConfigurableElementSettingsElement.getChildElement(
destination->getXmlElementName(), destination->getName(),
xmlConfigurableElementSettingsElementContent)) {
// "Component" tag has been renamed to "ParameterBlock", but retro-compatibility shall
// be ensured.
//
// So checking if this case occurs, i.e. element name is "ParameterBlock"
// but found xml setting name is "Component".
bool compatibilityCase =
(destination->getXmlElementName() == "ParameterBlock") &&
xmlConfigurableElementSettingsElement.getChildElement(
"Component", destination->getName(), xmlConfigurableElementSettingsElementContent);
// Error if the compatibility case does not occur.
if (!compatibilityCase) {
context.setError("Couldn't find settings for " + destination->getXmlElementName() +
" " + destination->getName() + " for Configuration " + getPath());
return false;
}
}
// Create configuration access context
string error;
CConfigurationAccessContext configurationAccessContext(error, false);
// Have domain configuration parse settings for configurable element
bool success = areaConfiguration->serializeXmlSettings(
xmlConfigurableElementSettingsElementContent, configurationAccessContext);
context.appendToError(error);
return success;
}
bool CDomainConfiguration::exportOneConfigurableElementSettings(
CAreaConfiguration *areaConfiguration, CXmlElement &xmlConfigurableElementSettingsElement,
CXmlDomainExportContext &context) const
{
const CConfigurableElement *source = areaConfiguration->getConfigurableElement();
// Create child XML element
CXmlElement xmlConfigurableElementSettingsElementContent;
xmlConfigurableElementSettingsElement.createChild(xmlConfigurableElementSettingsElementContent,
source->getXmlElementName());
// Create configuration access context
string error;
CConfigurationAccessContext configurationAccessContext(error, true);
configurationAccessContext.setValueSpaceRaw(context.valueSpaceIsRaw());
configurationAccessContext.setOutputRawFormat(context.outputRawFormatIsHex());
// Have domain configuration parse settings for configurable element
bool success = areaConfiguration->serializeXmlSettings(
xmlConfigurableElementSettingsElementContent, configurationAccessContext);
context.appendToError(error);
return success;
}
void CDomainConfiguration::addConfigurableElement(const CConfigurableElement *configurableElement,
const CSyncerSet *syncerSet)
{
mAreaConfigurationList.emplace_back(configurableElement->createAreaConfiguration(syncerSet));
}
void CDomainConfiguration::removeConfigurableElement(
const CConfigurableElement *pConfigurableElement)
{
auto &areaConfigurationToRemove = getAreaConfiguration(pConfigurableElement);
mAreaConfigurationList.remove(areaConfigurationToRemove);
}
bool CDomainConfiguration::setElementSequence(const std::vector<string> &newElementSequence,
string &error)
{
std::vector<string> elementSequenceSet;
auto insertLocation = begin(mAreaConfigurationList);
for (const std::string &elementPath : newElementSequence) {
auto areaConfiguration = findAreaConfigurationByPath(elementPath);
if (areaConfiguration == end(mAreaConfigurationList)) {
error = "Element " + elementPath + " not found in domain";
return false;
}
auto it = find(begin(elementSequenceSet), end(elementSequenceSet), elementPath);
if (it != end(elementSequenceSet)) {
error = "Element " + elementPath + " provided more than once";
return false;
}
elementSequenceSet.push_back(elementPath);
// Take into account the new configuration order by moving the configuration associated to
// the element to the n-th position of the configuration list.
// It will result in prepending to the configuration list wit the configuration of all
// elements found in XML, keeping the order of the processing of the XML file.
mAreaConfigurationList.splice(insertLocation, mAreaConfigurationList, areaConfiguration);
// areaConfiguration is still valid, but now refer to the reorderer list
insertLocation = std::next(areaConfiguration);
}
return true;
}
void CDomainConfiguration::getElementSequence(string &strResult) const
{
// List configurable element paths out of ordered area configuration list
strResult = accumulate(begin(mAreaConfigurationList), end(mAreaConfigurationList), string("\n"),
[](const string &a, const AreaConfiguration &conf) {
return a + conf->getConfigurableElement()->getPath() + "\n";
});
}
// Application rule
bool CDomainConfiguration::setApplicationRule(
const string &strApplicationRule,
const CSelectionCriteriaDefinition *pSelectionCriteriaDefinition, string &strError)
{
// Parser
CRuleParser ruleParser(strApplicationRule, pSelectionCriteriaDefinition);
// Attempt to parse it
if (!ruleParser.parse(NULL, strError)) {
return false;
}
// Replace compound rule
setRule(ruleParser.grabRootRule());
return true;
}
void CDomainConfiguration::clearApplicationRule()
{
// Replace compound rule
setRule(NULL);
}
string CDomainConfiguration::getApplicationRule() const
{
const CCompoundRule *pRule = getRule();
return pRule ? pRule->dump() : "<none>";
}
/**
* Get the Configuration Blackboard.
*
* Fetch the Configuration Blackboard related to the ConfigurableElement given in parameter. This
* Element is used to retrieve the correct AreaConfiguration where the Blackboard is stored.
*
* @param[in] pConfigurableElement A pointer to a ConfigurableElement that is part of the
* Domain. This must have been checked previously, as an
* assertion is performed.
*
* return Pointer to the Blackboard of the Configuration.
*/
CParameterBlackboard *CDomainConfiguration::getBlackboard(
const CConfigurableElement *pConfigurableElement) const
{
const auto &it = find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
[&](const AreaConfiguration &conf) {
return conf != nullptr &&
conf->getConfigurableElement() == pConfigurableElement;
});
ALWAYS_ASSERT(it != end(mAreaConfigurationList), "Configurable Element "
<< pConfigurableElement->getName()
<< " not found in any area Configuration");
return &(*it)->getBlackboard();
}
// Save data from current
void CDomainConfiguration::save(const CParameterBlackboard *pMainBlackboard)
{
// Just propagate to areas
for (auto &areaConfiguration : mAreaConfigurationList) {
areaConfiguration->save(pMainBlackboard);
}
}
// Apply data to current
bool CDomainConfiguration::restore(CParameterBlackboard *pMainBlackboard, bool bSync,
core::Results *errors) const
{
return std::accumulate(begin(mAreaConfigurationList), end(mAreaConfigurationList), true,
[&](bool accumulator, const AreaConfiguration &conf) {
return conf->restore(pMainBlackboard, bSync, errors) && accumulator;
});
}
// Ensure validity for configurable element area configuration
void CDomainConfiguration::validate(const CConfigurableElement *pConfigurableElement,
const CParameterBlackboard *pMainBlackboard)
{
auto &areaConfigurationToValidate = getAreaConfiguration(pConfigurableElement);
// Delegate
areaConfigurationToValidate->validate(pMainBlackboard);
}
// Ensure validity of all area configurations
void CDomainConfiguration::validate(const CParameterBlackboard *pMainBlackboard)
{
for (auto &areaConfiguration : mAreaConfigurationList) {
areaConfiguration->validate(pMainBlackboard);
}
}
// Return configuration validity for given configurable element
bool CDomainConfiguration::isValid(const CConfigurableElement *pConfigurableElement) const
{
// Get child configurable elemnt's area configuration
auto &areaConfiguration = getAreaConfiguration(pConfigurableElement);
ALWAYS_ASSERT(areaConfiguration != nullptr, "Configurable Element "
<< pConfigurableElement->getName()
<< " not found in any area Configuration");
return areaConfiguration->isValid();
}
// Ensure validity of configurable element's area configuration by copying in from a valid one
void CDomainConfiguration::validateAgainst(const CDomainConfiguration *pValidDomainConfiguration,
const CConfigurableElement *pConfigurableElement)
{
// Retrieve related area configurations
auto &areaConfigurationToValidate = getAreaConfiguration(pConfigurableElement);
const auto &areaConfigurationToValidateAgainst =
pValidDomainConfiguration->getAreaConfiguration(pConfigurableElement);
// Delegate to area
areaConfigurationToValidate->validateAgainst(areaConfigurationToValidateAgainst.get());
}
void CDomainConfiguration::validateAgainst(const CDomainConfiguration *validDomainConfiguration)
{
ALWAYS_ASSERT(mAreaConfigurationList.size() ==
validDomainConfiguration->mAreaConfigurationList.size(),
"Cannot validate domain configuration "
<< getPath() << " since area configuration list does not have the same size"
"than the configuration list to check against");
for (const auto &configurationToValidateAgainst :
validDomainConfiguration->mAreaConfigurationList) {
// Get the area configuration associated to the configurable element of the
// valid area configuration, it will assert if none found.
auto configurableElement = configurationToValidateAgainst->getConfigurableElement();
auto &configurationToValidate = getAreaConfiguration(configurableElement);
// Delegate to area
configurationToValidate->validateAgainst(configurationToValidateAgainst.get());
}
}
// Dynamic data application
bool CDomainConfiguration::isApplicable() const
{
const CCompoundRule *pRule = getRule();
return pRule && pRule->matches();
}
// Merge existing configurations to given configurable element ones
void CDomainConfiguration::merge(CConfigurableElement *pToConfigurableElement,
CConfigurableElement *pFromConfigurableElement)
{
// Retrieve related area configurations
auto &areaConfigurationToMergeTo = getAreaConfiguration(pToConfigurableElement);
const auto &areaConfigurationToMergeFrom = getAreaConfiguration(pFromConfigurableElement);
// Do the merge
areaConfigurationToMergeFrom->copyToOuter(areaConfigurationToMergeTo.get());
}
// Domain splitting
void CDomainConfiguration::split(CConfigurableElement *pFromConfigurableElement)
{
// Retrieve related area configuration
const auto &areaConfigurationToSplitFrom = getAreaConfiguration(pFromConfigurableElement);
// Go through children areas to copy configuration data to them
size_t uiNbConfigurableElementChildren = pFromConfigurableElement->getNbChildren();
size_t uiChild;
for (uiChild = 0; uiChild < uiNbConfigurableElementChildren; uiChild++) {
CConfigurableElement *pToChildConfigurableElement =
static_cast<CConfigurableElement *>(pFromConfigurableElement->getChild(uiChild));
// Get child configurable elemnt's area configuration
auto &childAreaConfiguration = getAreaConfiguration(pToChildConfigurableElement);
// Do the copy
childAreaConfiguration->copyFromOuter(areaConfigurationToSplitFrom.get());
}
}
const CDomainConfiguration::AreaConfiguration &CDomainConfiguration::getAreaConfiguration(
const CConfigurableElement *pConfigurableElement) const
{
const auto &it = find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
[&](const AreaConfiguration &conf) {
return conf->getConfigurableElement() == pConfigurableElement;
});
ALWAYS_ASSERT(it != end(mAreaConfigurationList),
"Configurable Element " << pConfigurableElement->getName()
<< " not found in Domain Configuration list");
return *it;
}
CDomainConfiguration::AreaConfigurations::iterator CDomainConfiguration::
findAreaConfigurationByPath(const std::string &configurableElementPath)
{
auto areaConfiguration =
find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
[&](const AreaConfiguration &conf) {
return conf->getConfigurableElement()->getPath() == configurableElementPath;
});
return areaConfiguration;
}
// Rule
const CCompoundRule *CDomainConfiguration::getRule() const
{
if (getNbChildren()) {
// Rule created
return static_cast<const CCompoundRule *>(getChild(ECompoundRule));
}
return NULL;
}
CCompoundRule *CDomainConfiguration::getRule()
{
if (getNbChildren()) {
// Rule created
return static_cast<CCompoundRule *>(getChild(ECompoundRule));
}
return NULL;
}
void CDomainConfiguration::setRule(CCompoundRule *pRule)
{
CCompoundRule *pOldRule = getRule();
if (pOldRule) {
// Remove previous rule
removeChild(pOldRule);
delete pOldRule;
}
// Set new one
if (pRule) {
// Chain
addChild(pRule);
}
}