/*
* 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 "ConfigurableElement.h"
#include "MappingData.h"
#include "SyncerSet.h"
#include "ConfigurableDomain.h"
#include "ConfigurationAccessContext.h"
#include "ConfigurableElementAggregator.h"
#include "AreaConfiguration.h"
#include "Iterator.hpp"
#include "Utility.h"
#include "XmlParameterSerializingContext.h"
#include <assert.h>
#define base CElement
CConfigurableElement::CConfigurableElement(const std::string &strName) : base(strName)
{
}
bool CConfigurableElement::fromXml(const CXmlElement &xmlElement,
CXmlSerializingContext &serializingContext)
{
auto &context = static_cast<CXmlParameterSerializingContext &>(serializingContext);
auto &accessContext = context.getAccessContext();
if (accessContext.serializeSettings()) {
// As serialization and deserialisation are handled through the *same* function
// the (de)serialize object can not be const in `serializeXmlSettings` signature.
// As a result a const_cast is unavoidable :(.
// Fixme: split serializeXmlSettings in two functions (in and out) to avoid the `const_cast`
return serializeXmlSettings(const_cast<CXmlElement &>(xmlElement),
static_cast<CConfigurationAccessContext &>(accessContext));
}
return structureFromXml(xmlElement, serializingContext);
}
void CConfigurableElement::toXml(CXmlElement &xmlElement,
CXmlSerializingContext &serializingContext) const
{
auto &context = static_cast<CXmlParameterSerializingContext &>(serializingContext);
auto &accessContext = context.getAccessContext();
if (accessContext.serializeSettings()) {
serializeXmlSettings(xmlElement, static_cast<CConfigurationAccessContext &>(accessContext));
} else {
structureToXml(xmlElement, serializingContext);
}
}
// XML configuration settings parsing
bool CConfigurableElement::serializeXmlSettings(
CXmlElement &xmlConfigurationSettingsElementContent,
CConfigurationAccessContext &configurationAccessContext) const
{
size_t uiNbChildren = getNbChildren();
if (!configurationAccessContext.serializeOut()) {
// Just do basic checks and propagate to children
CXmlElement::CChildIterator it(xmlConfigurationSettingsElementContent);
CXmlElement xmlChildConfigurableElementSettingsElement;
// Propagate to children
for (size_t index = 0; index < uiNbChildren; index++) {
// Get child
const CConfigurableElement *pChildConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
if (!it.next(xmlChildConfigurableElementSettingsElement)) {
// Structure error
configurationAccessContext.setError(
"Configuration settings parsing: missing child node " +
pChildConfigurableElement->getXmlElementName() + " (name:" +
pChildConfigurableElement->getName() + ") in " + getName());
return false;
}
// Check element type matches in type
if (xmlChildConfigurableElementSettingsElement.getType() !=
pChildConfigurableElement->getXmlElementName()) {
// "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 xml setting name is "Component".
bool compatibilityCase =
(pChildConfigurableElement->getXmlElementName() == "ParameterBlock") &&
(xmlChildConfigurableElementSettingsElement.getType() == "Component");
// Error if the compatibility case does not occur.
if (!compatibilityCase) {
// Type error
configurationAccessContext.setError(
"Configuration settings parsing: Settings "
"for configurable element " +
pChildConfigurableElement->getQualifiedPath() +
" does not match expected type: " +
xmlChildConfigurableElementSettingsElement.getType() + " instead of " +
pChildConfigurableElement->getKind());
return false;
}
}
// Check element type matches in name
if (xmlChildConfigurableElementSettingsElement.getNameAttribute() !=
pChildConfigurableElement->getName()) {
// Name error
configurationAccessContext.setError(
"Configuration settings parsing: Under configurable element " +
getQualifiedPath() + ", expected element name " +
pChildConfigurableElement->getName() + " but found " +
xmlChildConfigurableElementSettingsElement.getNameAttribute() + " instead");
return false;
}
// Parse child configurable element's settings
if (!pChildConfigurableElement->serializeXmlSettings(
xmlChildConfigurableElementSettingsElement, configurationAccessContext)) {
return false;
}
}
// There should remain no configurable element to parse
if (it.next(xmlChildConfigurableElementSettingsElement)) {
// Structure error
configurationAccessContext.setError(
"Configuration settings parsing: Unexpected xml element node " +
xmlChildConfigurableElementSettingsElement.getType() + " in " + getQualifiedPath());
return false;
}
} else {
// Handle element name attribute
xmlConfigurationSettingsElementContent.setNameAttribute(getName());
// Propagate to children
for (size_t index = 0; index < uiNbChildren; index++) {
const CConfigurableElement *pChildConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
// Create corresponding child element
CXmlElement xmlChildConfigurableElementSettingsElement;
xmlConfigurationSettingsElementContent.createChild(
xmlChildConfigurableElementSettingsElement,
pChildConfigurableElement->getXmlElementName());
// Propagate
pChildConfigurableElement->serializeXmlSettings(
xmlChildConfigurableElementSettingsElement, configurationAccessContext);
}
}
// Done
return true;
}
// AreaConfiguration creation
CAreaConfiguration *CConfigurableElement::createAreaConfiguration(
const CSyncerSet *pSyncerSet) const
{
return new CAreaConfiguration(this, pSyncerSet);
}
// Parameter access
bool CConfigurableElement::accessValue(CPathNavigator &pathNavigator, std::string &strValue,
bool bSet,
CParameterAccessContext ¶meterAccessContext) const
{
std::string *pStrChildName = pathNavigator.next();
if (!pStrChildName) {
parameterAccessContext.setError((bSet ? "Can't set " : "Can't get ") +
pathNavigator.getCurrentPath() +
" because it is not a parameter");
return false;
}
const CConfigurableElement *pChild =
static_cast<const CConfigurableElement *>(findChild(*pStrChildName));
if (!pChild) {
parameterAccessContext.setError("Path not found: " + pathNavigator.getCurrentPath());
return false;
}
return pChild->accessValue(pathNavigator, strValue, bSet, parameterAccessContext);
}
// Whole element access
void CConfigurableElement::getSettingsAsBytes(std::vector<uint8_t> &bytes,
CParameterAccessContext ¶meterAccessContext) const
{
bytes.resize(getFootPrint());
parameterAccessContext.getParameterBlackboard()->readBytes(
bytes, getOffset() - parameterAccessContext.getBaseOffset());
}
bool CConfigurableElement::setSettingsAsBytes(const std::vector<uint8_t> &bytes,
CParameterAccessContext ¶meterAccessContext) const
{
CParameterBlackboard *pParameterBlackboard = parameterAccessContext.getParameterBlackboard();
// Size
size_t size = getFootPrint();
// Check sizes match
if (size != bytes.size()) {
parameterAccessContext.setError(std::string("Wrong size: Expected: ") +
std::to_string(size) + " Provided: " +
std::to_string(bytes.size()));
return false;
}
// Write bytes
pParameterBlackboard->writeBytes(bytes, getOffset() - parameterAccessContext.getBaseOffset());
if (not parameterAccessContext.getAutoSync()) {
// Auto sync is not activated, sync will be defered until an explicit request
return true;
}
CSyncerSet syncerSet;
fillSyncerSet(syncerSet);
core::Results res;
if (not syncerSet.sync(*parameterAccessContext.getParameterBlackboard(), false, &res)) {
parameterAccessContext.setError(utility::asString(res));
return false;
}
return true;
}
std::list<const CConfigurableElement *> CConfigurableElement::getConfigurableElementContext() const
{
std::list<const CConfigurableElement *> configurableElementPath;
const CElement *element = this;
while (element != nullptr and isOfConfigurableElementType(element)) {
auto configurableElement = static_cast<const CConfigurableElement *>(element);
configurableElementPath.push_back(configurableElement);
element = element->getParent();
}
return configurableElementPath;
}
// Used for simulation and virtual subsystems
void CConfigurableElement::setDefaultValues(CParameterAccessContext ¶meterAccessContext) const
{
// Propagate to children
size_t uiNbChildren = getNbChildren();
for (size_t index = 0; index < uiNbChildren; index++) {
const CConfigurableElement *pConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
pConfigurableElement->setDefaultValues(parameterAccessContext);
}
}
// Element properties
void CConfigurableElement::showProperties(std::string &strResult) const
{
base::showProperties(strResult);
strResult += "Total size: " + getFootprintAsString() + "\n";
}
std::string CConfigurableElement::logValue(utility::ErrorContext &context) const
{
return logValue(static_cast<CParameterAccessContext &>(context));
}
std::string CConfigurableElement::logValue(CParameterAccessContext & /*ctx*/) const
{
// By default, an element does not have a value. Only leaf elements have
// one. This method could be pure virtual but then, several derived classes
// would need to implement it in order to simply return an empty string.
return "";
}
// Offset
void CConfigurableElement::setOffset(size_t offset)
{
// Assign offset locally
_offset = offset;
// Propagate to children
size_t uiNbChildren = getNbChildren();
for (size_t index = 0; index < uiNbChildren; index++) {
CConfigurableElement *pConfigurableElement =
static_cast<CConfigurableElement *>(getChild(index));
pConfigurableElement->setOffset(offset);
offset += pConfigurableElement->getFootPrint();
}
}
size_t CConfigurableElement::getOffset() const
{
return _offset;
}
// Memory
size_t CConfigurableElement::getFootPrint() const
{
size_t uiSize = 0;
size_t uiNbChildren = getNbChildren();
for (size_t index = 0; index < uiNbChildren; index++) {
const CConfigurableElement *pConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
uiSize += pConfigurableElement->getFootPrint();
}
return uiSize;
}
// Browse parent path to find syncer
ISyncer *CConfigurableElement::getSyncer() const
{
// Check parent
const CElement *pParent = getParent();
if (isOfConfigurableElementType(pParent)) {
return static_cast<const CConfigurableElement *>(pParent)->getSyncer();
}
return NULL;
}
// Syncer set (me, ascendant or descendant ones)
void CConfigurableElement::fillSyncerSet(CSyncerSet &syncerSet) const
{
// Try me or ascendants
ISyncer *pMineOrAscendantSyncer = getSyncer();
if (pMineOrAscendantSyncer) {
// Provide found syncer object
syncerSet += pMineOrAscendantSyncer;
// Done
return;
}
// Fetch descendant ones
fillSyncerSetFromDescendant(syncerSet);
}
// Syncer set (descendant)
void CConfigurableElement::fillSyncerSetFromDescendant(CSyncerSet &syncerSet) const
{
// Dig
size_t uiNbChildren = getNbChildren();
for (size_t index = 0; index < uiNbChildren; index++) {
const CConfigurableElement *pConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
pConfigurableElement->fillSyncerSetFromDescendant(syncerSet);
}
}
// Configurable domain association
void CConfigurableElement::addAttachedConfigurableDomain(
const CConfigurableDomain *pConfigurableDomain)
{
_configurableDomainList.push_back(pConfigurableDomain);
}
void CConfigurableElement::removeAttachedConfigurableDomain(
const CConfigurableDomain *pConfigurableDomain)
{
_configurableDomainList.remove(pConfigurableDomain);
}
// Belonging domain
bool CConfigurableElement::belongsTo(const CConfigurableDomain *pConfigurableDomain) const
{
if (containsConfigurableDomain(pConfigurableDomain)) {
return true;
}
return belongsToDomainAscending(pConfigurableDomain);
}
// Belonging domains
void CConfigurableElement::getBelongingDomains(
std::list<const CConfigurableDomain *> &configurableDomainList) const
{
configurableDomainList.insert(configurableDomainList.end(), _configurableDomainList.begin(),
_configurableDomainList.end());
// Check parent
const CElement *pParent = getParent();
if (isOfConfigurableElementType(pParent)) {
static_cast<const CConfigurableElement *>(pParent)->getBelongingDomains(
configurableDomainList);
}
}
void CConfigurableElement::listBelongingDomains(std::string &strResult, bool bVertical) const
{
// Get belonging domain list
std::list<const CConfigurableDomain *> configurableDomainList;
getBelongingDomains(configurableDomainList);
// Fill list
listDomains(configurableDomainList, strResult, bVertical);
}
// Elements with no domains
void CConfigurableElement::listRogueElements(std::string &strResult) const
{
// Get rogue element aggregate list (no associated domain)
std::list<const CConfigurableElement *> rogueElementList;
CConfigurableElementAggregator configurableElementAggregator(
rogueElementList, &CConfigurableElement::hasNoDomainAssociated);
configurableElementAggregator.aggegate(this);
// Build list as std::string
std::list<const CConfigurableElement *>::const_iterator it;
for (it = rogueElementList.begin(); it != rogueElementList.end(); ++it) {
const CConfigurableElement *pConfigurableElement = *it;
strResult += pConfigurableElement->getPath() + "\n";
}
}
bool CConfigurableElement::isRogue() const
{
// Check not belonging to any domin from current level and towards ascendents
if (getBelongingDomainCount() != 0) {
return false;
}
// Get a list of elements (current + descendants) with no domains associated
std::list<const CConfigurableElement *> rogueElementList;
CConfigurableElementAggregator agregator(rogueElementList,
&CConfigurableElement::hasNoDomainAssociated);
agregator.aggegate(this);
// Check only one element found which ought to be current one
return (rogueElementList.size() == 1) && (rogueElementList.front() == this);
}
// Footprint as string
std::string CConfigurableElement::getFootprintAsString() const
{
// Get size as string
return std::to_string(getFootPrint()) + " byte(s)";
}
// Matching check for no domain association
bool CConfigurableElement::hasNoDomainAssociated() const
{
return _configurableDomainList.empty();
}
// Matching check for no valid associated domains
bool CConfigurableElement::hasNoValidDomainAssociated() const
{
if (_configurableDomainList.empty()) {
// No domains associated
return true;
}
ConfigurableDomainListConstIterator it;
// Browse all configurable domains for validity checking
for (it = _configurableDomainList.begin(); it != _configurableDomainList.end(); ++it) {
const CConfigurableDomain *pConfigurableDomain = *it;
if (pConfigurableDomain->isApplicableConfigurationValid(this)) {
return false;
}
}
return true;
}
// Owning domains
void CConfigurableElement::listAssociatedDomains(std::string &strResult, bool bVertical) const
{
// Fill list
listDomains(_configurableDomainList, strResult, bVertical);
}
size_t CConfigurableElement::getBelongingDomainCount() const
{
// Get belonging domain list
std::list<const CConfigurableDomain *> configurableDomainList;
getBelongingDomains(configurableDomainList);
return configurableDomainList.size();
}
void CConfigurableElement::listDomains(
const std::list<const CConfigurableDomain *> &configurableDomainList, std::string &strResult,
bool bVertical) const
{
// Fill list
ConfigurableDomainListConstIterator it;
bool bFirst = true;
// Browse all configurable domains for comparison
for (it = configurableDomainList.begin(); it != configurableDomainList.end(); ++it) {
const CConfigurableDomain *pConfigurableDomain = *it;
if (!bVertical && !bFirst) {
strResult += ", ";
}
strResult += pConfigurableDomain->getName();
if (bVertical) {
strResult += "\n";
} else {
bFirst = false;
}
}
}
bool CConfigurableElement::containsConfigurableDomain(
const CConfigurableDomain *pConfigurableDomain) const
{
ConfigurableDomainListConstIterator it;
// Browse all configurable domains for comparison
for (it = _configurableDomainList.begin(); it != _configurableDomainList.end(); ++it) {
if (pConfigurableDomain == *it) {
return true;
}
}
return false;
}
// Belonging domain ascending search
bool CConfigurableElement::belongsToDomainAscending(
const CConfigurableDomain *pConfigurableDomain) const
{
// Check parent
const CElement *pParent = getParent();
if (isOfConfigurableElementType(pParent)) {
return static_cast<const CConfigurableElement *>(pParent)->belongsTo(pConfigurableDomain);
}
return false;
}
// Belonging subsystem
const CSubsystem *CConfigurableElement::getBelongingSubsystem() const
{
const CElement *pParent = getParent();
// Stop at system class
if (!pParent->getParent()) {
return NULL;
}
return static_cast<const CConfigurableElement *>(pParent)->getBelongingSubsystem();
}
// Check element is a parameter
bool CConfigurableElement::isParameter() const
{
return false;
}
// Check parent is still of current type (by structure knowledge)
bool CConfigurableElement::isOfConfigurableElementType(const CElement *pParent) const
{
assert(pParent);
// Up to system class
return !!pParent->getParent();
}