Major Changes:
- Merged latest Qt5 devel content from dlbo (292 files changed, 7 new dirs)
- Consolidated forward-declarations and include folders
- Removed unneeded modules per Niklas' repo check list
Build System Fixes:
- Fixed CMake VERSION_GREATER syntax in ParticipantManagement/CMakeLists.txt
- Added VLC packages (libvlc-dev, vlc-plugin-base) to docker_build.sh
- Added PostgreSQL dev packages (libpq-dev, postgresql-server-dev-all) to Dockerfile
- Fixed locale warnings (ANSI_X3.4-1968 → C.UTF-8)
- Fixed ArcGIS SDK path warnings when BUILD_ARCGIS=OFF
Code Generation Fixes:
- Fixed IDL enum collisions (PortType→CorePortType/DdsutPortType, PdmEventType)
- Fixed Grantlee template svc_translation.cpp.grantlee:
- Changed {{ idl_enum.name }}_def:: to {{ idl_enum.name }}::
- Fixed .underlying() calls for C++11 scoped enums (enum class)
- Removed generated files from repo (moc_*, *_dtc.h, *_ftc.h)
Validation:
- BUILD_RTI=ON: SUCCESS
- BUILD_RTI=OFF: SUCCESS
- C++17 standard maintained for RTI 7.7 compatibility
- Ubuntu 26.04 Docker environment verified
Remaining Warnings (expected):
- BgViewer, KDChart, KDReports, Poppler-Qt6 not found (optional features)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
942 lines
35 KiB
C++
942 lines
35 KiB
C++
/**
|
|
* @RDE_HEADER@
|
|
* Read Xml service files
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
#include "XmlReader.h"
|
|
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
|
|
#include <QDirIterator>
|
|
#include <QFile>
|
|
#include <QDomDocument>
|
|
#include <QXmlStreamReader>
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
|
|
using namespace CodeGen;
|
|
|
|
|
|
class XmlReaderException : public std::exception
|
|
{
|
|
public:
|
|
explicit XmlReaderException(const QString &msg) noexcept
|
|
: m_msg(msg.toUtf8())
|
|
{}
|
|
|
|
~XmlReaderException() = default;
|
|
|
|
virtual const char *what() const noexcept
|
|
{
|
|
return m_msg.data();
|
|
}
|
|
private:
|
|
const QByteArray m_msg;
|
|
};
|
|
|
|
XmlReader::XmlReader()
|
|
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.XmlReader"))
|
|
{}
|
|
|
|
bool XmlReader::readServiceFile(const QString &svcFile,
|
|
const QString &idlServiceInfoFile,
|
|
const XmlReader *parent)
|
|
{
|
|
m_externalXmlInformations.clear();
|
|
m_idlFiles.clear();
|
|
m_xmlFiles.clear();
|
|
m_usedServices.clear();
|
|
m_fullServiceName2Abbreviation.clear();
|
|
// clean up
|
|
m_svcNamespace.clear();
|
|
m_svcAbbr.clear();
|
|
m_svcProcNames.clear();
|
|
m_customRecordReplay = false;
|
|
m_exerciseDependency = ExerciseDependency::Exercise;
|
|
m_dbClassTables.clear();
|
|
m_struct2BaseClass.clear();
|
|
m_structsToGenerate.clear();
|
|
m_idlStructsToGenerate.clear();
|
|
m_tables.clear();
|
|
m_views.clear();
|
|
m_enumerations.clear();
|
|
m_functionTopics.clear();
|
|
m_externalTopicTypes.clear();
|
|
m_customFunctions.clear();
|
|
m_prio = 0;
|
|
m_svcDevMode = false;
|
|
|
|
// avoid parsing unneeded files
|
|
if (parent)
|
|
{
|
|
m_idlFiles = parent->m_idlFiles;
|
|
m_xmlFiles = parent->m_xmlFiles;
|
|
m_fullServiceName2Abbreviation = parent->m_fullServiceName2Abbreviation;
|
|
m_externalXmlInformations = parent->m_externalXmlInformations;
|
|
}
|
|
|
|
|
|
// load informations about the location of all idl files
|
|
loadIdlInfofile(idlServiceInfoFile);
|
|
|
|
// try to open the service file
|
|
QFile f(svcFile);
|
|
if (!f.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "Service file '" << svcFile << "' not found!";
|
|
return false;
|
|
}
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
{
|
|
m_errorString = QLatin1String("Can not open service file '") + svcFile +
|
|
QLatin1String("' for reading: ") + f.errorString();
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
INFO_LOG(m_logger) << "SvcFile: " << svcFile;
|
|
|
|
// read content
|
|
QString errMsg;
|
|
int errLine = 0;
|
|
int errColumn = 0;
|
|
QDomDocument doc;
|
|
if (!doc.setContent(&f, &errMsg, &errLine, &errColumn))
|
|
{
|
|
m_errorString = QLatin1String("Error parsing service file '") + svcFile + QLatin1String("': ") +
|
|
errMsg + QLatin1String(" at ") + QString::number(errLine) +
|
|
QLatin1Char(':') + QString::number(errColumn);
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
// now finally read the data
|
|
// what do we expect?
|
|
// - a service name ( plus service abbreviation i.e. AES)
|
|
// - service classes to generate (dbServiceStructs)
|
|
// - database tables to generate (tables)
|
|
// - static enumerations to generate (enumerations)
|
|
// - idl structures for topicTypes (idlStructs)
|
|
// - a devMode attribute which is optional
|
|
QDomElement domRoot = doc.documentElement();
|
|
if (!domRoot.hasAttribute(QStringLiteral("namespace")))
|
|
{
|
|
m_errorString = QLatin1String("Xml file '") + svcFile +
|
|
QLatin1String("' is not a valid service file: service name/namespace is missing");
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
m_svcNamespace = getAttribute(domRoot, QStringLiteral("namespace"));
|
|
m_svcAbbr = getAttribute(domRoot, QStringLiteral("abbreviation")).toLower();
|
|
m_svcProcNames = getAttribute(domRoot, QStringLiteral("procNames")).split(QLatin1Char(','));
|
|
m_customRecordReplay = getAttributeBool(domRoot, QStringLiteral("customRecordReplay"), true, false);
|
|
|
|
QString devModeStr = getAttribute(domRoot, QStringLiteral("devMode"), true);
|
|
m_svcDevMode = devModeStr == QStringLiteral("1") ||
|
|
devModeStr == QStringLiteral("true");
|
|
INFO_LOG(m_logger) << "devMode: " << m_svcDevMode;
|
|
|
|
const QString groupNamesString = getAttribute(domRoot, QStringLiteral("groups"), true);
|
|
if (!groupNamesString.isEmpty())
|
|
{
|
|
m_groupNames = groupNamesString.split(QLatin1Char(','), QString::SkipEmptyParts);
|
|
INFO_LOG(m_logger) << "groups: " << groupNamesString;
|
|
}
|
|
else
|
|
m_groupNames.clear();
|
|
|
|
m_exerciseDependency = string2ExerciseDependency(getAttribute(domRoot, QStringLiteral("exerciseDependency"), true));
|
|
// exerciseIndepenent only if in core/master group AND no other groups
|
|
// core + other groups => one instance of service is svc_0
|
|
// other instances are svc_<exerciseId>
|
|
if (m_groupNames.contains(QStringLiteral("Core")) ||
|
|
m_groupNames.contains(QStringLiteral("Master")))
|
|
{
|
|
if (m_exerciseDependency != ExerciseDependency::Global &&
|
|
m_exerciseDependency != ExerciseDependency::GlobalAndExercise)
|
|
{
|
|
WARNING_LOG(m_logger) << "Service " << getServiceNamespace()
|
|
<< " is not marked as exercise independent but should be started in group Core/Master";
|
|
}
|
|
}
|
|
|
|
INFO_LOG(m_logger) << "exerciseDependency: " << (int)m_exerciseDependency;
|
|
if ( domRoot.hasAttribute(QStringLiteral("prio")))
|
|
{
|
|
m_prio = getAttribute(domRoot, QStringLiteral("prio"), 0).toInt();
|
|
}
|
|
try
|
|
{
|
|
parseDBSserviceStructs(domRoot.firstChildElement(QStringLiteral("dbServiceStructs")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing dbServiceStructs: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseDBTables(domRoot.firstChildElement(QStringLiteral("tables")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing dbtables: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseDBViews(domRoot.firstChildElement(QStringLiteral("views")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing dbviews: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseEnumerations(domRoot.firstChildElement(QStringLiteral("enumerations")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing enumerations: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseCustomDtcFunctions(domRoot.firstChildElement(QStringLiteral("customDtcFunctions")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing custom dtc functions: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseTopicTypes(domRoot.firstChildElement(QStringLiteral("topicTypes")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing idl strucs: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
parseFunctionTopics(domRoot.firstChildElement(QStringLiteral("functionTopics")));
|
|
}
|
|
catch (const XmlReaderException &ex)
|
|
{
|
|
m_errorString = QLatin1String("Error parsing function topics: ") + QLatin1String(ex.what());
|
|
ERROR_LOG(m_logger) << m_errorString;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
QString XmlReader::getAttribute(const QDomElement &element,
|
|
const QString &attributeName,
|
|
bool bOptional)
|
|
{
|
|
if (!element.hasAttribute(attributeName))
|
|
{
|
|
if (bOptional)
|
|
return QString();
|
|
throw XmlReaderException(QLatin1String("Element ") + element.tagName() +
|
|
QLatin1String(" has no attribute ") + attributeName);
|
|
}
|
|
return element.attribute(attributeName);
|
|
}
|
|
|
|
bool XmlReader::getAttributeBool(const QDomElement &element,
|
|
const QString &attributeName,
|
|
bool bOptional, bool defaultValue)
|
|
{
|
|
QString str = getAttribute(element, attributeName, bOptional);
|
|
if (str.isEmpty())
|
|
return defaultValue;
|
|
bool bOk;
|
|
bool ret = (str.toInt(&bOk) != 0);
|
|
if (!bOk)
|
|
throw XmlReaderException(QLatin1String("Can not parse boolean value: ") + str);
|
|
return ret;
|
|
}
|
|
|
|
int XmlReader::getAttributeInt(const QDomElement &element,
|
|
const QString &attributeName,
|
|
bool bOptional, int defaultValue)
|
|
{
|
|
QString str = getAttribute(element, attributeName, bOptional);
|
|
if (str.isEmpty())
|
|
return defaultValue;
|
|
bool bOk;
|
|
int ret = str.toInt(&bOk);
|
|
if (!bOk)
|
|
{
|
|
//try from base16
|
|
ret = str.toUInt(&bOk,16);
|
|
if(!bOk)
|
|
throw XmlReaderException(QLatin1String("Can not parse int value: ") + str);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void XmlReader::parseDBSserviceStructs(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
// service struct -> table relations
|
|
QDomElement domDbStruct = rootElement.firstChildElement(QStringLiteral("serviceStruct"));
|
|
while (!domDbStruct.isNull())
|
|
{
|
|
ServiceStructInformation ssi;
|
|
ssi.name = getAttribute(domDbStruct, QStringLiteral("name"));
|
|
ssi.primaryTable = getAttribute(domDbStruct, QStringLiteral("table"));
|
|
ssi.hasStaticContent = getAttributeBool(domDbStruct, QStringLiteral("static"), true, false);
|
|
|
|
QDomElement domFunc = domDbStruct.firstChildElement(QStringLiteral("function"));
|
|
while (!domFunc.isNull())
|
|
{
|
|
m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Db);
|
|
domFunc = domFunc.nextSiblingElement(domFunc.tagName());
|
|
}
|
|
|
|
QDomElement domNotify = domDbStruct.firstChildElement(QStringLiteral("notification"));
|
|
while (!domNotify.isNull())
|
|
{
|
|
ssi.dbNotifications.append(parseDbNotification(domNotify));
|
|
domNotify = domNotify.nextSiblingElement(domNotify.tagName());
|
|
}
|
|
|
|
m_structsToGenerate.push_back(ssi);
|
|
|
|
domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName());
|
|
}
|
|
std::sort(m_structsToGenerate.begin(), m_structsToGenerate.end());
|
|
|
|
// table -> parent table relations
|
|
domDbStruct = rootElement.firstChildElement(QStringLiteral("table"));
|
|
while (!domDbStruct.isNull())
|
|
{
|
|
const QString table = getAttribute(domDbStruct, QStringLiteral("name"));
|
|
const QString parentTable = getAttribute(domDbStruct, QStringLiteral("base"), true);
|
|
m_struct2BaseClass[table] = parentTable;
|
|
|
|
domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName());
|
|
}
|
|
INFO_LOG(m_logger) << "Read " << m_structsToGenerate.size() << " database service struct(s)";
|
|
}
|
|
|
|
void XmlReader::parseDBTables(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
QDomElement domDbTable = rootElement.firstChildElement(QStringLiteral("table"));
|
|
while (!domDbTable.isNull())
|
|
{
|
|
QSharedPointer<TableInformation> ti(new TableInformation);
|
|
ti->name = getAttribute(domDbTable, QStringLiteral("name"));
|
|
ti->structName = QLatin1String("t_") + ti->name;
|
|
ti->comment = getAttribute(domDbTable, QStringLiteral("comment"), true);
|
|
ti->exerciseDependency = string2ExerciseDependency(getAttribute(domDbTable, QStringLiteral("exerciseDependency"), true));
|
|
|
|
QDomElement domColumn = domDbTable.firstChildElement(QStringLiteral("column"));
|
|
while (!domColumn.isNull())
|
|
{
|
|
ti->plainColumns.append(parseTableColumn(domColumn, ti->name));
|
|
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
|
|
}
|
|
|
|
QDomElement domForeignKey = domDbTable.firstChildElement(QStringLiteral("foreignKey"));
|
|
while (!domForeignKey.isNull())
|
|
{
|
|
ti->foreignKeys.append(parseForeignKey(domForeignKey));
|
|
domForeignKey = domForeignKey.nextSiblingElement(domForeignKey.tagName());
|
|
}
|
|
QDomElement domConstraint = domDbTable.firstChildElement(QStringLiteral("constraint"));
|
|
while (!domConstraint.isNull())
|
|
{
|
|
ti->constraints.append(parseConstraint(domConstraint));
|
|
domConstraint = domConstraint.nextSiblingElement(domConstraint.tagName());
|
|
}
|
|
|
|
m_tables.insert(ti->name, ti);
|
|
|
|
domDbTable = domDbTable.nextSiblingElement(domDbTable.tagName());
|
|
}
|
|
|
|
INFO_LOG(m_logger) << "Read " << m_tables.size() << " database table(s)";
|
|
}
|
|
|
|
void XmlReader::parseDBViews(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
QDomElement domDbView = rootElement.firstChildElement(QStringLiteral("view"));
|
|
while (!domDbView.isNull())
|
|
{
|
|
QSharedPointer<ViewInformation> vi(new ViewInformation);
|
|
vi->name = getAttribute(domDbView, QStringLiteral("name"));
|
|
vi->structName = QLatin1String("v_") + vi->name;
|
|
vi->baseSchema = m_svcAbbr;
|
|
vi->baseTable = getAttribute(domDbView, QStringLiteral("base"));
|
|
vi->comment = getAttribute(domDbView, QStringLiteral("comment"), true);
|
|
vi->filter = getAttribute(domDbView, QStringLiteral("filter"));
|
|
vi->exerciseDependency = string2ExerciseDependency(getAttribute(domDbView, QStringLiteral("exerciseDependency"), true));
|
|
|
|
DEBUG_LOG(m_logger) << "Base table = " << vi->baseTable;
|
|
/*
|
|
QDomElement domConstraint = domDbView.firstChildElement(QStringLiteral("constraint"));
|
|
while (!domConstraint.isNull())
|
|
{
|
|
vi->constraints.append(parseConstraint(domConstraint));
|
|
domConstraint = domConstraint.nextSiblingElement(domConstraint.tagName());
|
|
}
|
|
*/
|
|
m_views.insert(vi->name, vi);
|
|
|
|
domDbView = domDbView.nextSiblingElement(domDbView.tagName());
|
|
}
|
|
|
|
INFO_LOG(m_logger) << "Read " << m_views.size() << " database view(s)";
|
|
}
|
|
|
|
void XmlReader::parseEnumerations(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
QDomElement domEnum = rootElement.firstChildElement(QStringLiteral("enumeration"));
|
|
while (!domEnum.isNull())
|
|
{
|
|
EnumInformation enumeration;
|
|
enumeration.serviceName = getServiceNamespace();
|
|
enumeration.enumName = getAttribute(domEnum, QStringLiteral("name"));
|
|
enumeration.tableName = getAttribute(domEnum, QStringLiteral("table"), true);
|
|
|
|
QDomElement enumVal = domEnum.firstChildElement(QStringLiteral("attribute"));
|
|
while (!enumVal.isNull())
|
|
{
|
|
EnumKeyValuePair evp;
|
|
evp.key = getAttribute(enumVal, QStringLiteral("key"));
|
|
evp.value = getAttributeInt(enumVal, QStringLiteral("value"));
|
|
enumeration.values.append(evp);
|
|
|
|
enumVal = enumVal.nextSiblingElement(enumVal.tagName());
|
|
}
|
|
|
|
m_enumerations.push_back(enumeration);
|
|
|
|
domEnum = domEnum.nextSiblingElement(domEnum.tagName());
|
|
}
|
|
INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " enumeration(s)";
|
|
}
|
|
|
|
void XmlReader::parseCustomDtcFunctions(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
QDomElement domElement = rootElement.firstChildElement(QStringLiteral("customDtcFunction"));
|
|
while (!domElement.isNull())
|
|
{
|
|
|
|
QDomElement domFunc = domElement.firstChildElement(QStringLiteral("function"));
|
|
while (!domFunc.isNull())
|
|
{
|
|
m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Dtc);
|
|
domFunc = domFunc.nextSiblingElement(domFunc.tagName());
|
|
}
|
|
domElement = domElement.nextSiblingElement(domElement.tagName());
|
|
}
|
|
INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " customDtcFunction(s)";
|
|
}
|
|
|
|
/**
|
|
* @brief Parse idl structure (TopicType Information)
|
|
* (IDL = Interface Definition Language)
|
|
* @param rootElement
|
|
*/
|
|
void XmlReader::parseTopicTypes(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
// idl struct
|
|
QDomElement domIdlStruct = rootElement.firstChildElement(QStringLiteral("topicType"));
|
|
while (!domIdlStruct.isNull())
|
|
{
|
|
TopicTypeInformation isi;
|
|
isi.name = getAttribute(domIdlStruct, QStringLiteral("name"));
|
|
isi.baseValueType = getAttribute(domIdlStruct, QStringLiteral("baseValueType"), true);
|
|
|
|
// preload some external topics to be able to know which external services we're using
|
|
const QVector<QStringRef> sl = isi.baseValueType.splitRef(QStringLiteral("::"));
|
|
if (sl.size() == 2)
|
|
loadExternalTopicType(sl.at(0).toString(), sl.at(1).toString());
|
|
|
|
const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true);
|
|
|
|
if (tmp.compare(QLatin1String("FINAL"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::FINAL;
|
|
if (tmp.compare(QLatin1String("MUTABLE"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::MUTABLE;
|
|
if (tmp.compare(QLatin1String("EXTENSIBLE"), Qt::CaseInsensitive) == 0) isi.extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE;
|
|
|
|
isi.comment = getAttribute(domIdlStruct, QStringLiteral("comment"), true);
|
|
isi.isDataObject = getAttributeBool(domIdlStruct, QStringLiteral("isDataObject"), true, false);
|
|
isi.dtsAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTS"), true, true);
|
|
isi.dtcAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTC"), true, true);
|
|
isi.recordTopic = getAttributeBool(domIdlStruct, QStringLiteral("recordTopic"), true, false);
|
|
isi.routeToClient = getAttributeBool(domIdlStruct, QStringLiteral("routeToClient"), true, true);
|
|
isi.routeToServer = getAttributeBool(domIdlStruct, QStringLiteral("routeToServer"), true, false);
|
|
isi.routeInReplay = getAttributeBool(domIdlStruct, QStringLiteral("routeInReplay"), true, false);
|
|
isi.autosubscribe = getAttributeBool(domIdlStruct, QStringLiteral("autosubscribe"), true, true);
|
|
|
|
QDomElement domColumn = domIdlStruct.firstChildElement(QStringLiteral("column"));
|
|
while (!domColumn.isNull())
|
|
{
|
|
isi.columns.append(parseIdlStructColumn(domColumn));
|
|
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
|
|
}
|
|
|
|
//INFO_LOG(m_logger) << "# TopicType: " << isi.name;
|
|
m_idlStructsToGenerate.insert(isi.name, isi);
|
|
|
|
domIdlStruct = domIdlStruct.nextSiblingElement(domIdlStruct.tagName());
|
|
}
|
|
|
|
|
|
INFO_LOG(m_logger) << "Read " << m_idlStructsToGenerate.size() << " idl struct(s)";
|
|
}
|
|
|
|
void XmlReader::parseFunctionTopics(const QDomElement &rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
// idl struct
|
|
QDomElement domFunctionTopic = rootElement.firstChildElement(QStringLiteral("functionTopic"));
|
|
while (!domFunctionTopic.isNull())
|
|
{
|
|
FunctionTopic ft;
|
|
ft.name = getAttribute(domFunctionTopic, QStringLiteral("name"));
|
|
ft.requestTopicType = getAttribute(domFunctionTopic, QStringLiteral("requestTopicType"));
|
|
ft.replyTopicType = getAttribute(domFunctionTopic, QStringLiteral("replyTopicType"));
|
|
ft.requestTopicName = getAttribute(domFunctionTopic, QStringLiteral("requestTopicName"));
|
|
ft.replyTopicName = getAttribute(domFunctionTopic, QStringLiteral("replyTopicName"));
|
|
ft.routeInReplay = getAttributeBool(domFunctionTopic, QStringLiteral("routeInReplay"), true, false);
|
|
|
|
m_functionTopics.append(ft);
|
|
|
|
domFunctionTopic = domFunctionTopic.nextSiblingElement(domFunctionTopic.tagName());
|
|
}
|
|
|
|
for (const FunctionTopic &ft : qAsConst(m_functionTopics))
|
|
{
|
|
const QVector<QStringRef> sl1 = ft.requestTopicType.splitRef(QStringLiteral("::"));
|
|
if (sl1.size() == 2)
|
|
loadExternalTopicType(sl1.at(0).toString(), sl1.at(1).toString());
|
|
const QVector<QStringRef> sl2 = ft.replyTopicType.splitRef(QStringLiteral("::"));
|
|
if (sl2.size() == 2)
|
|
loadExternalTopicType(sl2.at(0).toString(), sl2.at(1).toString());
|
|
}
|
|
}
|
|
|
|
ColumnInformation XmlReader::parseTableColumn(const QDomElement &column, const QString &tableName) const
|
|
{
|
|
ColumnInformation ci;
|
|
|
|
ci.columnName = getAttribute(column, QStringLiteral("name"));
|
|
ci.datatype = getAttribute(column, QStringLiteral("datatype"));
|
|
ci.defaultValue = getAttribute(column, QStringLiteral("default"), true);
|
|
ci.comment = getAttribute(column, QStringLiteral("comment"), true);
|
|
ci.isPrimaryKey = getAttributeBool(column, QStringLiteral("isPrimaryKey"), true, false);
|
|
ci.isForeignKey = getAttributeBool(column, QStringLiteral("isForeignKey"), true, false);
|
|
ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false);
|
|
const QDomElement seq = column.firstChildElement(QStringLiteral("sequence"));
|
|
if (!seq.isNull())
|
|
{
|
|
ci.sequenceStart = getAttributeInt(seq, QStringLiteral("start"), true, -1);
|
|
ci.sequenceIncrement = getAttributeInt(seq, QStringLiteral("increment"), true, -1);
|
|
ci.hasSequence = ci.sequenceStart >= 0 && ci.sequenceIncrement > 0;
|
|
}
|
|
|
|
if (ci.columnName.toLower() != ci.columnName)
|
|
WARNING_LOG(m_logger) << "Column " << ci.columnName << " in Table "
|
|
<< tableName << " has has upper-case chars which is not allowed!";
|
|
|
|
return ci;
|
|
}
|
|
|
|
IdlColumnInformation XmlReader::parseIdlStructColumn(const QDomElement &column) const
|
|
{
|
|
IdlColumnInformation ci;
|
|
|
|
ci.name = getAttribute(column, QStringLiteral("name"));
|
|
ci.type = getAttribute(column, QStringLiteral("idlType"));
|
|
ci.comment = getAttribute(column, QStringLiteral("comment"), true);
|
|
ci.isKey = getAttributeBool(column, QStringLiteral("isKey"), true, false);
|
|
ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false);
|
|
ci.isTranslatable = getAttributeBool(column, QStringLiteral("isTranslatable"), true, false);
|
|
|
|
return ci;
|
|
}
|
|
|
|
QSharedPointer<ForeignKeyInformation> XmlReader::parseForeignKey(const QDomElement &foreignKey) const
|
|
{
|
|
QSharedPointer<ForeignKeyInformation> fk(new ForeignKeyInformation);
|
|
|
|
fk->name = getAttribute(foreignKey, QStringLiteral("name"));
|
|
// Default-Value of isOnDeleteCascade => true
|
|
const QString deleteAction = getAttribute(foreignKey, QStringLiteral("referential_integrity"), true);
|
|
if (deleteAction == QLatin1String("DeleteNoAction"))
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::NoAction;
|
|
else if (deleteAction == QLatin1String("DeleteCascade"))
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade;
|
|
else if (deleteAction == QLatin1String("DeleteRestrict"))
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Restrict;
|
|
else if (deleteAction == QLatin1String("DeleteSetNull"))
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetNull;
|
|
else if (deleteAction == QLatin1String("DeleteSetDefault"))
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetDefault;
|
|
else
|
|
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade; // default
|
|
|
|
fk->source = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("source")));
|
|
fk->destination = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("destination")));
|
|
|
|
return fk;
|
|
}
|
|
|
|
ForeignKeyInformation::Endpoint XmlReader::parseForeignKeyEndpoint(const QString &foreignKeyName,
|
|
const QDomElement &endpoint) const
|
|
{
|
|
if (endpoint.isNull())
|
|
throw XmlReaderException(QLatin1String("No endpoint for foreign key ") + foreignKeyName);
|
|
|
|
ForeignKeyInformation::Endpoint fkEndpoint;
|
|
|
|
fkEndpoint.service = getAttribute(endpoint, QStringLiteral("service"), true);
|
|
fkEndpoint.serviceAbbr= getAttribute(endpoint, QStringLiteral("serviceAbbr"), true);
|
|
fkEndpoint.schema = getAttribute(endpoint, QStringLiteral("schema"), true);
|
|
fkEndpoint.tableName = getAttribute(endpoint, QStringLiteral("table"));
|
|
fkEndpoint.columnName = getAttribute(endpoint, QStringLiteral("column"));
|
|
|
|
return fkEndpoint;
|
|
}
|
|
|
|
ConstraintInformation XmlReader::parseConstraint(const QDomElement &constraint) const
|
|
{
|
|
ConstraintInformation ret;
|
|
|
|
ret.name = getAttribute(constraint, QStringLiteral("name"), true);
|
|
const QString type = getAttribute(constraint, QStringLiteral("type")).toLower();
|
|
if (type == QLatin1String("unique"))
|
|
ret.type = ConstraintInformation::Type::unique;
|
|
else if (type == QLatin1String("index"))
|
|
ret.type = ConstraintInformation::Type::index;
|
|
|
|
QDomElement domColumn = constraint.firstChildElement(QStringLiteral("column"));
|
|
if (domColumn.isNull())
|
|
throw XmlReaderException(QLatin1String("No column for constraint") + ret.name);
|
|
while (!domColumn.isNull())
|
|
{
|
|
ret.affectedColumns += getAttribute(domColumn, QStringLiteral("name"));
|
|
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
CustomServiceFunctionInformation XmlReader::parseCustomFunction(const QDomElement &customFunc,
|
|
CustomServiceFunctionInformation::Type funcType) const
|
|
{
|
|
CustomServiceFunctionInformation ret;
|
|
ret.methodName = getAttribute(customFunc, QStringLiteral("name"));
|
|
ret.returnValue = getAttribute(customFunc, QStringLiteral("returns"), true);
|
|
ret.isConst = getAttributeBool(customFunc, QStringLiteral("isConst"), true, false);
|
|
if (ret.returnValue.isEmpty())
|
|
ret.returnValue = QStringLiteral("bool");
|
|
QMap<int, QPair<QString, QString>> paramHelper;
|
|
QDomElement domParam = customFunc.firstChildElement(QStringLiteral("parameter"));
|
|
while (!domParam.isNull())
|
|
{
|
|
int pos = getAttributeInt(domParam, QStringLiteral("pos"));
|
|
if (paramHelper.contains(pos))
|
|
throw XmlReaderException(QLatin1String("Doubled position entry for custom function ") + ret.methodName);
|
|
QString paramName = getAttribute(domParam, QStringLiteral("name"));
|
|
const QString paramType = getAttribute(domParam, QStringLiteral("type"));
|
|
const QString paramDefault = getAttribute(domParam, QStringLiteral("default"), true);
|
|
if (!paramDefault.isEmpty())
|
|
paramName.append(QLatin1String(" = ") + paramDefault);
|
|
paramHelper[pos] = qMakePair(paramName, paramType);
|
|
domParam = domParam.nextSiblingElement(domParam.tagName());
|
|
|
|
}
|
|
ret.paramNames.reserve(paramHelper.size());
|
|
ret.paramTypes.reserve(paramHelper.size());
|
|
ret.type = funcType;
|
|
QStringList signature;
|
|
for (auto it = paramHelper.constBegin(); it != paramHelper.constEnd(); ++it)
|
|
{
|
|
ret.paramNames += it.value().first;
|
|
ret.paramTypes += it.value().second;
|
|
QString tmp = it.value().second;
|
|
if (!it.value().second.endsWith(QLatin1Char('&')))
|
|
tmp += QLatin1Char(' ');
|
|
tmp += it.value().first;
|
|
signature += tmp;
|
|
}
|
|
ret.signature += signature.join(QStringLiteral(", "));
|
|
return ret;
|
|
}
|
|
|
|
DbNotifcationInformation XmlReader::parseDbNotification(const QDomElement &customFunc) const
|
|
{
|
|
DbNotifcationInformation ret(parseCustomFunction(customFunc, CustomServiceFunctionInformation::Type::DbNotify));
|
|
|
|
const QString attribute(getAttribute(customFunc, QStringLiteral("notificationTypes")).toLower());
|
|
const QStringList nfTypes = attribute.split(QLatin1Char(','));
|
|
static const QMap<QString, DbNotifcationInformation::NotificationType> s_nfTypeMap =
|
|
{
|
|
{ QLatin1String("beforeinsert"), DbNotifcationInformation::NotificationType::BeforeInsert },
|
|
{ QLatin1String("afterinsert"), DbNotifcationInformation::NotificationType::AfterInsert },
|
|
{ QLatin1String("beforeupdate"), DbNotifcationInformation::NotificationType::BeforeUpdate },
|
|
{ QLatin1String("afterupdate"), DbNotifcationInformation::NotificationType::AfterUpdate },
|
|
{ QLatin1String("beforedelete"), DbNotifcationInformation::NotificationType::BeforeDelete },
|
|
{ QLatin1String("afterdelete"), DbNotifcationInformation::NotificationType::AfterDelete }
|
|
};
|
|
for (const QString &nfType : nfTypes)
|
|
{
|
|
const auto it = s_nfTypeMap.find(nfType);
|
|
if (it == s_nfTypeMap.end())
|
|
throw XmlReaderException(QStringLiteral("Can not find notification type %2 in type map for function %1").arg(ret.methodName, nfType));
|
|
ret.notificationType += it.value();
|
|
}
|
|
if (ret.notificationType.isEmpty())
|
|
throw XmlReaderException(QStringLiteral("Notification function '%1' without a valid notification type").arg(ret.methodName));
|
|
if (ret.paramTypes.size() != 1)
|
|
throw XmlReaderException(QStringLiteral("Notification function '%1' must have exactly one parameter").arg(ret.methodName));
|
|
|
|
return ret;
|
|
}
|
|
|
|
TopicTypeInformation XmlReader::loadExternalTopicType(const QString &externalServiceName, const QString &topicTypeName)
|
|
{
|
|
DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << topicTypeName;
|
|
|
|
if (!loadExternalService(externalServiceName))
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not find service " << externalServiceName
|
|
<< " referenced by external topic " << topicTypeName;
|
|
return TopicTypeInformation();
|
|
}
|
|
auto extXmlReader = m_externalXmlInformations.value(externalServiceName);
|
|
if (!extXmlReader)
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'";
|
|
return TopicTypeInformation();
|
|
}
|
|
|
|
TopicTypeInformation tti = extXmlReader->getIdlStructInformation().value(topicTypeName);
|
|
if (tti.isValid())
|
|
{
|
|
tti.name = externalServiceName + QLatin1String("::") + tti.name;
|
|
m_externalTopicTypes += tti;
|
|
m_usedServices[externalServiceName] = true;
|
|
return tti;
|
|
}
|
|
WARNING_LOG(m_logger) << "Could not find topic " << topicTypeName
|
|
<< " in external service definition for service "
|
|
<< externalServiceName;
|
|
return TopicTypeInformation();
|
|
}
|
|
|
|
EnumInformation XmlReader::getExternalEnumeration(const QString &externalServiceName, const QString &enumName, bool verbose)
|
|
{
|
|
DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << enumName;
|
|
|
|
if (!loadExternalService(externalServiceName))
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not find service '" << externalServiceName << "'"
|
|
<< " referenced by external enumeration " << enumName;
|
|
return EnumInformation();
|
|
}
|
|
auto extXmlReader = m_externalXmlInformations.value(externalServiceName);
|
|
if (!extXmlReader)
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'";
|
|
return EnumInformation();
|
|
}
|
|
|
|
const QVector<EnumInformation> enums = extXmlReader->getEnumerations();
|
|
for (const EnumInformation &ei : enums)
|
|
{
|
|
if (ei.enumName == enumName)
|
|
{
|
|
m_usedServices[externalServiceName] = true;
|
|
return ei;
|
|
}
|
|
}
|
|
if (verbose)
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not find enumeration " << enumName
|
|
<< " in external service definition for '"
|
|
<< externalServiceName << "'";
|
|
}
|
|
return EnumInformation();
|
|
}
|
|
|
|
|
|
bool XmlReader::loadExternalService(const QString &serviceName)
|
|
{
|
|
if (serviceName == getServiceNamespace())
|
|
return false;
|
|
if (m_externalXmlInformations.contains(serviceName))
|
|
return true;
|
|
|
|
QString fileName = xmlFileForService(serviceName);
|
|
if (fileName.isEmpty())
|
|
{
|
|
const QVector<QString> dirs = NGL::directories(NGL::FileType::ConfigFile);
|
|
for (const QString &dirname : dirs)
|
|
{
|
|
QDirIterator dirIt(dirname + QStringLiteral("/xml"), QDir::Files | QDir::NoDotAndDotDot | QDir::Readable);
|
|
while (dirIt.hasNext())
|
|
{
|
|
dirIt.next();
|
|
if (!dirIt.fileName().endsWith(QLatin1String(".xml")))
|
|
continue;
|
|
|
|
fileName = dirIt.filePath();
|
|
const QString svcNamespace = serviceNamespaceFromFile(fileName);
|
|
if (svcNamespace == serviceName)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (fileName.isEmpty())
|
|
return false;
|
|
|
|
DEBUG_LOG(m_logger) << "xml file for " << serviceName << ": " << fileName;
|
|
auto extXmlReader = QSharedPointer<XmlReader>(new XmlReader);
|
|
if (!extXmlReader->readServiceFile(fileName, QString(), this))
|
|
{
|
|
WARNING_LOG(m_logger) << "Error reading service file " << fileName
|
|
<< " for service " << serviceName;
|
|
return false;
|
|
}
|
|
m_externalXmlInformations[serviceName] = extXmlReader;
|
|
if (!m_usedServices.contains(serviceName))
|
|
m_usedServices[serviceName] = false;
|
|
return true;
|
|
}
|
|
|
|
void XmlReader::loadIdlInfofile(const QString &idlServiceInfoFile)
|
|
{
|
|
if (idlServiceInfoFile.isEmpty())
|
|
return;
|
|
QFile f(idlServiceInfoFile);
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not read idl service info file " << idlServiceInfoFile;
|
|
return;
|
|
}
|
|
const QList<QByteArray> files = f.readAll().split('\n');
|
|
for (const QByteArray &filePath : files)
|
|
{
|
|
// comment
|
|
if (filePath.startsWith("--") || filePath.trimmed().isEmpty())
|
|
continue;
|
|
const QString fileName = QString::fromLocal8Bit(filePath.trimmed());
|
|
const QVector<QStringRef> parts = fileName.splitRef(QLatin1Char('/'));
|
|
getServiceNameInfoFromFilePath(parts); // what/ever/ServiceFooBar/sfb_service.xml -> ServiceFooBar <-> sfb
|
|
const auto fn = parts.last().trimmed();
|
|
if (fn.endsWith(QLatin1String(".xml")))
|
|
{
|
|
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).split(QLatin1Char('_')).at(0);
|
|
m_xmlFiles[svcAbbreveation.toString()] = fileName;
|
|
DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation.toString() << " = " << fileName;
|
|
}
|
|
else
|
|
{
|
|
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).toString().toLower();
|
|
if (svcAbbreveation.endsWith(QLatin1String("_enums")))
|
|
continue;
|
|
m_idlFiles[svcAbbreveation] = fileName;
|
|
DEBUG_LOG(m_logger) << "idl: " << svcAbbreveation << " = " << fileName;
|
|
}
|
|
}
|
|
}
|
|
|
|
QString XmlReader::getIdlFileForService(const QMap<QString, QString> &data,
|
|
const QString &serviceName) const
|
|
{
|
|
const QString serviceAbbr = serviceNameAbbreviation(serviceName);
|
|
return data.value(serviceAbbr);
|
|
}
|
|
|
|
|
|
QString XmlReader::serviceNameAbbreviation(const QString &serviceName) const
|
|
{
|
|
return m_fullServiceName2Abbreviation.value(serviceName);
|
|
}
|
|
|
|
void XmlReader::getServiceNameInfoFromFilePath(const QVector<QStringRef> &parts)
|
|
{
|
|
if (parts.size() < 2)
|
|
return;
|
|
|
|
// foo_service.xml -> foo
|
|
const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first().toString();
|
|
const QString serviceName = parts.at(parts.size() - 2).toString();
|
|
DEBUG_LOG(m_logger) << "serviceName: " << serviceName << " = " << abbreviation;
|
|
m_fullServiceName2Abbreviation[serviceName] = abbreviation;
|
|
}
|
|
|
|
ExerciseDependency XmlReader::string2ExerciseDependency(const QString &str)
|
|
{
|
|
if (str.isEmpty())
|
|
return ExerciseDependency::Undefined;
|
|
const QStringList sd = str.toLower().split(QLatin1Char(','));
|
|
int ret = ExerciseDependency::Undefined;
|
|
for (const QString &s : sd)
|
|
{
|
|
if (s == QLatin1String("global"))
|
|
ret |= ExerciseDependency::Global;
|
|
else if (s == QLatin1String("exercise"))
|
|
ret |= ExerciseDependency::Exercise;
|
|
else if (s == QLatin1String("globalandexercise"))
|
|
ret |= ExerciseDependency::GlobalAndExercise;
|
|
}
|
|
return static_cast<ExerciseDependency>(ret);
|
|
}
|
|
|
|
QString XmlReader::serviceNamespaceFromFile(const QString &fn)
|
|
{
|
|
QFile f(fn);
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
return QString();
|
|
QXmlStreamReader sr(&f);
|
|
while (!sr.atEnd())
|
|
{
|
|
sr.readNext();
|
|
if (sr.isStartElement() && sr.name() == QLatin1String("service"))
|
|
return (sr.attributes().value(QLatin1String("namespace"))).toString();
|
|
}
|
|
return QString();
|
|
}
|
|
|