ddl/mapping/configuration/map_target.cpp
2019-12-12 14:41:47 +01:00

617 lines
18 KiB
C++

/**
* @file
*
* @copyright
* @verbatim
Copyright @ 2017 Audi Electronics Venture GmbH. All rights reserved.
This Source Code Form is subject to the terms of the Mozilla
Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
@endverbatim
*/
#include "map_target.h"
#include <algorithm>
#include "a_util/result/error_def.h"
#include "legacy_error_macros.h"
#include "map_configuration.h"
namespace mapping
{
namespace oo
{
//define all needed error types and values locally
_MAKE_RESULT(-5, ERR_INVALID_ARG)
_MAKE_RESULT(-19, ERR_NOT_SUPPORTED)
_MAKE_RESULT(-20, ERR_NOT_FOUND)
_MAKE_RESULT(-40, ERR_INVALID_STATE)
}
}
using namespace mapping::oo;
MapTarget::MapTarget(MapConfiguration* pConfig) : _config(pConfig), _is_valid(true)
{
}
MapTarget::MapTarget(MapConfiguration* pConfig, std::string name, std::string strType) :
_config(pConfig), _is_valid(true)
{
_name = name;
_type = strType;
}
MapTarget::MapTarget(const MapTarget& oOther)
{
_name = oOther._name;
_type = oOther._type;
_assignments = oOther._assignments;
_sources = oOther._sources;
_is_valid = oOther._is_valid;
_config = oOther._config;
for (MapTriggerList::const_iterator it = oOther._triggers.begin();
it != oOther._triggers.end(); ++it)
{
_triggers.push_back((*it)->clone());
}
}
MapTarget& MapTarget::operator=(const MapTarget& oOther)
{
MapTarget oCopy(oOther);
oCopy.swap(*this);
return *this;
}
MapTarget::~MapTarget()
{
for (MapTriggerList::iterator it = _triggers.begin();
it != _triggers.end(); ++it)
{
delete *it;
}
_triggers.clear();
}
const std::string& MapTarget::getName() const
{
return _name;
}
const std::string& MapTarget::getType() const
{
return _type;
}
bool MapTarget::isValid() const
{
return _is_valid;
}
const MapAssignmentList& MapTarget::getAssignmentList() const
{
return _assignments;
}
a_util::result::Result MapTarget::addAssignment(const MapAssignment& oAssignment)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(_config->resetErrors());
if(!_is_valid)
{
_config->appendError(a_util::strings::format("Target Signal %s is not valid, cannot add any assignment", _name.c_str()));
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(_config->checkAssignmentReferences(oAssignment));
RETURN_IF_FAILED(addAssignmentNoTypeCheck(oAssignment));
// Check new assignment with ddl
const ddl::DDLComplex* pTargetStruct = _config->_ddl_ref->getStructByName(_type);
// Target is valid, so the type is defined and can be dereferenced
a_util::result::Result nRes = _config->checkAssignmentType(_name, *pTargetStruct, oAssignment);
if(isOk(nRes))
{
nRes = checkDoubleAssignments();
}
// If assignment not consistent with DDL, remove assignment
if(isFailed(nRes))
{
removeAssignmentWithoutClear(oAssignment.getTo());
}
return nRes;
}
a_util::result::Result MapTarget::removeAssignment(const std::string& strElementName)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
_config->_errors.clear();
return removeAssignmentWithoutClear(strElementName);
}
a_util::result::Result MapTarget::addTrigger(MapTriggerBase* pTrigger)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(_config->resetErrors());
if(!_is_valid)
{
_config->appendError(a_util::strings::format("Target Signal %s is not valid, cannot add any assignment", _name.c_str()));
return ERR_INVALID_STATE;
}
if(pTrigger->_config != _config)
{
_config->appendError(a_util::strings::format("Trigger refers to another configuration"));
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(pTrigger->checkTriggerReferences());
RETURN_IF_FAILED(_config->checkTriggerType(pTrigger));
return addTriggerNoTypeCheck(pTrigger);
}
a_util::result::Result MapTarget::removeTrigger(MapTriggerBase* pTrigger)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
_config->_errors.clear();
bool bIsRemoved = false;
std::string strSource;
for(MapTriggerList::iterator it = _triggers.begin(); it != _triggers.end(); it++)
{
if((*it)->isEqual(*pTrigger))
{
strSource = (*it)->getSourceDependency();
_triggers.erase(it);
bIsRemoved = true;
break;
}
}
if(!strSource.empty())
{
bool bIsSourceUsed = false;
for(MapAssignmentList::const_iterator it = _assignments.begin(); it != _assignments.end(); it++)
{
if(it->getSource() == strSource)
{
bIsSourceUsed = true;
}
}
for(MapTriggerList::const_iterator it = _triggers.begin(); it != _triggers.end(); it++)
{
if((*it)->getSourceDependency() == strSource)
{
bIsSourceUsed = true;
}
}
if(!bIsSourceUsed)
{
for (MapSourceNameList::iterator itSource = _sources.begin();
itSource != _sources.end(); ++itSource)
{
if (*itSource == strSource)
{
_sources.erase(itSource);
break;
}
}
}
}
if(bIsRemoved)
{
return a_util::result::SUCCESS;
}
return ERR_NOT_FOUND;
}
a_util::result::Result MapTarget::removeAssignmentWithoutClear(const std::string& strElementName)
{
bool bIsRemoved = false;
std::string strSource;
for(MapAssignmentList::iterator it = _assignments.begin(); it != _assignments.end(); it++)
{
if(it->getTo() == strElementName)
{
strSource = it->getSource();
_assignments.erase(it);
bIsRemoved = true;
break;
}
}
if(!strSource.empty())
{
bool isSourceUsed = false;
for(MapAssignmentList::const_iterator it = _assignments.begin(); it != _assignments.end(); it++)
{
if(it->getSource() == strSource)
{
isSourceUsed = true;
}
}
for(MapTriggerList::const_iterator it = _triggers.begin(); it != _triggers.end(); it++)
{
if((*it)->getSourceDependency() == strSource)
{
isSourceUsed = true;
}
}
if(!isSourceUsed)
{
for (MapSourceNameList::iterator itSource = _sources.begin();
itSource != _sources.end(); ++itSource)
{
if (*itSource == strSource)
{
_sources.erase(itSource);
break;
}
}
}
}
if(bIsRemoved)
{
return a_util::result::SUCCESS;
}
return ERR_NOT_FOUND;
}
a_util::result::Result MapTarget::addAssignmentNoTypeCheck(const MapAssignment& oAssignment)
{
// Assignment is attributed to an element
const std::string& strTo = oAssignment.getTo();
if(strTo.empty())
{
_config->appendError(a_util::strings::format("Target element name is empty in <target> '%s'",
_name.c_str()));
return ERR_INVALID_ARG;
}
for(MapAssignmentList::iterator itAssign = _assignments.begin(); itAssign != _assignments.end(); itAssign++)
{
// Is To already assigned
if (itAssign->getTo() == strTo)
{
_config->appendError(
a_util::strings::format("Target element '%s' is already assigned in <target> '%s'",
strTo.c_str(), getName().c_str()));
return ERR_INVALID_ARG;
}
}
// Assignment is a constant, a connection or a function
if (oAssignment.getSource().empty() && oAssignment.getConstant().empty() && oAssignment.getFunction().empty())
{
_config->appendError(a_util::strings::format("Missing <from>, <constant> or <function> attribute for <assignment> to '%s' in <target> '%s'",
strTo.c_str(), _name.c_str()));
return ERR_INVALID_ARG;
}
// The constant is numeric
if (!oAssignment.getConstant().empty() &&
!a_util::strings::isDouble(oAssignment.getConstant()))
{
_config->appendError(
a_util::strings::format("Empty or non-numeric constant for <assignment> to '%s' in <target> '%s'",
strTo.c_str(), _name.c_str()));
return ERR_INVALID_ARG;
}
// The trigger_counter function modulo is numeric
if (!oAssignment.getModulo().empty())
{
if (!a_util::strings::isInt64(oAssignment.getModulo()) ||
oAssignment.getModulo().find('-') != std::string::npos ||
oAssignment.getModulo().find('+') != std::string::npos ||
a_util::strings::toUInt64(oAssignment.getModulo()) == 0)
{
_config->appendError(
a_util::strings::format("Non-integer or zero modulo in transmission_counter for <assignment> to '%s' in <target> '%s'",
strTo.c_str(), _name.c_str()));
return ERR_INVALID_ARG;
}
}
_assignments.push_back(oAssignment);
if (!oAssignment.getSource().empty())
{
for (MapSourceList::const_iterator itSource = _config->getSourceList().begin();
itSource != _config->getSourceList().end(); ++itSource)
{
if (itSource->getName() == oAssignment.getSource())
{
_sources.insert(itSource->getName());
break;
}
}
}
return a_util::result::SUCCESS;
}
a_util::result::Result MapTarget::addTriggerNoTypeCheck(MapTriggerBase* pTrigger)
{
_triggers.push_back(pTrigger);
// append dependency to references sources
std::string strSourceDep = pTrigger->getSourceDependency();
if (!strSourceDep.empty())
{
for (MapSourceList::const_iterator itSource = _config->getSourceList().begin();
itSource != _config->getSourceList().end(); ++itSource)
{
if (itSource->getName() == strSourceDep)
{
_sources.insert(itSource->getName());
break;
}
}
}
return a_util::result::SUCCESS;
}
a_util::result::Result MapTarget::modifySourceName(const std::string& name, const std::string& strNewName)
{
_sources.erase(name);
_sources.insert(strNewName);
for(MapAssignmentList::iterator itAssign = _assignments.begin();
itAssign != _assignments.end(); itAssign++)
{
if(itAssign->_source == name)
{
itAssign->_source = strNewName;
}
}
for(MapTriggerList::iterator itTrigger = _triggers.begin();
itTrigger != _triggers.end(); itTrigger++)
{
if((*itTrigger)->getSourceDependency() == name)
{
(*itTrigger)->setSourceDependency(strNewName);
}
}
return a_util::result::SUCCESS;
}
const MapTriggerList& MapTarget::getTriggerList() const
{
return _triggers;
}
a_util::result::Result MapTarget::loadFromDOM(const a_util::xml::DOMElement& oTarget)
{
_sources.clear();
_assignments.clear();
_triggers.clear();
// parse attributes
const a_util::xml::DOMAttributes mapAttrs = oTarget.getAttributes();
a_util::xml::DOMAttributes::const_iterator itName = mapAttrs.find("name");
if (itName == mapAttrs.end() || itName->second.empty())
{
_config->appendError("Missing name attribute for a <target>");
return ERR_INVALID_ARG;
}
_name = itName->second;
a_util::strings::trim(_name);
a_util::xml::DOMAttributes::const_iterator itType = mapAttrs.find("type");
if (itType == mapAttrs.end() || itType->second.empty())
{
_config->appendError(a_util::strings::format("Missing type attribute for <target> '%s'",
itName->second.c_str()));
return ERR_INVALID_ARG;
}
_type = itType->second;
a_util::strings::trim(_type);
// parse assignments and triggers
const a_util::xml::DOMElementList lstElements = oTarget.getChildren();
a_util::result::Result nResult = a_util::result::SUCCESS;
for (a_util::xml::DOMElementList::const_iterator it = lstElements.begin();
it != lstElements.end(); ++it)
{
const a_util::xml::DOMElement& oElem = *it;
const std::string& name = oElem.getName();
if (name == "assignment")
{
MapAssignment oAssign;
a_util::result::Result nRes = oAssign.loadFromDOM(oElem, _config->_errors);
if(isOk(nRes))
{
nRes = addAssignmentNoTypeCheck(oAssign);
}
if (isFailed(nRes))
{
nResult = nRes;
}
}
else if (name == "trigger")
{
MapTriggerBase* pTrigger = NULL;
a_util::result::Result nRes = MapTriggerBase::createFromDOM(_config,
oElem, pTrigger);
if(isOk(nRes))
{
nRes = addTriggerNoTypeCheck(pTrigger);
}
if (isFailed(nRes))
{
nResult = nRes;
}
}
else
{
_config->appendError(a_util::strings::format("Invalid element type in <target> '%s': '%s'",
itName->second.c_str(), name.c_str()));
nResult = ERR_INVALID_ARG;
}
}
return nResult;
}
a_util::result::Result MapTarget::writeToDOM(a_util::xml::DOMElement& oDOMElement) const
{
oDOMElement.setName("target");
oDOMElement.setAttribute("name", _name);
oDOMElement.setAttribute("type", _type);
for(MapAssignmentList::const_iterator itAssign = _assignments.begin();
itAssign != _assignments.end(); itAssign++)
{
a_util::xml::DOMElement oDOMAssign = oDOMElement.createChild("assignment");
itAssign->writeToDOM(oDOMAssign);
}
for(MapTriggerList::const_iterator itTrigger = _triggers.begin();
itTrigger != _triggers.end(); itTrigger++)
{
a_util::xml::DOMElement oDOMTrigger = oDOMElement.createChild("trigger");
(*itTrigger)->writeToDOM(oDOMTrigger);
}
return a_util::result::SUCCESS;
}
a_util::result::Result MapTarget::checkDoubleAssignments()
{
a_util::result::Result nRes = a_util::result::SUCCESS;
std::set<std::string> oAssignedElList;
for(MapAssignmentList::iterator itAssign = _assignments.begin(); itAssign != _assignments.end(); itAssign++)
{
oAssignedElList.insert(itAssign->getTo());
}
for(MapAssignmentList::iterator itAssign = _assignments.begin(); itAssign != _assignments.end(); itAssign++)
{
for(std::set<std::string>::iterator itParent = oAssignedElList.begin(); itParent != oAssignedElList.end(); itParent++)
{
std::string strEl = itAssign->getTo();
if(strEl.find(*itParent) == 0)
{
size_t pos = 0;
while ((pos = strEl.find(*itParent, pos)) != std::string::npos)
{
strEl.replace(pos, itParent->length(), std::string());
pos += 1;
}
if(strEl.find('.') == 0 ||strEl.find('[') == 0)
{
_config->appendError(a_util::strings::format(
"<assignment> to '%s' not possible because of <assignment> to %s in <target> '%s'",
strEl.c_str(), itParent->c_str(), getName().c_str()));
itAssign->_is_valid = false;
nRes = ERR_NOT_SUPPORTED;
}
}
}
}
return nRes;
}
void MapTarget::swap(MapTarget& oOther)
{
using std::swap;
swap(_config, oOther._config);
swap(_name, oOther._name);
swap(_type, oOther._type);
swap(_assignments, oOther._assignments);
swap(_sources, oOther._sources);
swap(_triggers, oOther._triggers);
}
const MapSourceNameList& MapTarget::getReferencedSources() const
{
return _sources;
}
bool mapping::oo::operator==(const MapTarget& a, const MapTarget& b)
{
if (a.getName() != b.getName() ||
a.getType() != b.getType())
{
return false;
}
const MapAssignmentList& lstAssignA = a.getAssignmentList();
const MapAssignmentList& lstAssignB = b.getAssignmentList();
if (lstAssignA.size() != lstAssignB.size() ||
!std::equal(lstAssignA.begin(), lstAssignA.end(), lstAssignB.begin()))
{
return false;
}
const MapTriggerList& lstTriggerA = a.getTriggerList();
const MapTriggerList& lstTriggerB = b.getTriggerList();
if (lstTriggerA.size() != lstTriggerB.size())
{
return false;
}
MapTriggerList::const_iterator itA = lstTriggerA.begin();
MapTriggerList::const_iterator itB = lstTriggerB.begin();
for (; itA != lstTriggerA.end() && itB != lstTriggerB.end();
++itA, ++itB)
{
if (!(*itA)->isEqual(*(*itB)))
{
return false;
}
}
return true;
}
a_util::result::Result MapTarget::setName(const std::string& strNewName)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(_config->resetErrors());
RETURN_IF_FAILED(_config->checkSignalName(strNewName));
_name = strNewName;
return _config->checkMappingConsistency();
}
a_util::result::Result MapTarget::setType(const std::string& strType)
{
if(!_config)
{
return ERR_INVALID_STATE;
}
RETURN_IF_FAILED(_config->resetErrors());
RETURN_IF_FAILED(_config->checkSignalType(strType));
_type = strType;
return _config->checkDDLConsistency();
}