- training_area_telegrams.cpp: Fixed QJsonObject initializer syntax (Qt6 requires {{}} not ({}))
- training_area_telegrams.cpp: Fixed fromJson() methods to access m_* members directly
- training_area_telegrams.cpp: Fixed brace mismatches in multiple toJson() methods
- training_area_telegrams.h: Changed Header struct to public members for consistency
- tx_mixin.cpp: Fixed getEquipmentNumber() template for direct member access
- lts_coordinate.cpp: Added missing #include <iomanip>
- MIGRATION_STATUS.md: Added migration progress tracking document
C++17 constraint enforced due to RTI Connext DDS 7.7.0 incompatibility with C++20+
Build status: BUILD_RTI=OFF successful (0 errors, warnings only)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
896 lines
35 KiB
C++
896 lines
35 KiB
C++
|
|
|
|
#include "XmlReader.h"
|
|
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
#include <QDirIterator>
|
|
#include <QtXml/QDomDocument>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QXmlStreamReader>
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
#include <utility>
|
|
|
|
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();
|
|
|
|
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;
|
|
|
|
if (parent)
|
|
{
|
|
m_idlFiles = parent->m_idlFiles;
|
|
m_xmlFiles = parent->m_xmlFiles;
|
|
m_fullServiceName2Abbreviation = parent->m_fullServiceName2Abbreviation;
|
|
m_externalXmlInformations = parent->m_externalXmlInformations;
|
|
}
|
|
|
|
loadIdlInfofile(idlServiceInfoFile);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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(','), Qt::SkipEmptyParts);
|
|
INFO_LOG(m_logger) << "groups: " << groupNamesString;
|
|
}
|
|
else
|
|
m_groupNames.clear();
|
|
|
|
m_exerciseDependency = string2ExerciseDependency(getAttribute(domRoot, QStringLiteral("exerciseDependency"), true));
|
|
|
|
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)
|
|
{
|
|
|
|
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;
|
|
|
|
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());
|
|
|
|
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;
|
|
|
|
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)";
|
|
}
|
|
|
|
void XmlReader::parseTopicTypes(const QDomElement& rootElement)
|
|
{
|
|
if (rootElement.isNull())
|
|
return;
|
|
|
|
QDomElement domIdlStruct = rootElement.firstChildElement(QStringLiteral("topicType"));
|
|
while (!domIdlStruct.isNull())
|
|
{
|
|
TopicTypeInformation isi;
|
|
isi.name = getAttribute(domIdlStruct, QStringLiteral("name"));
|
|
isi.baseValueType = getAttribute(domIdlStruct, QStringLiteral("baseValueType"), true);
|
|
|
|
const QStringList sl = isi.baseValueType.split(QStringLiteral("::"));
|
|
if (sl.size() == 2)
|
|
loadExternalTopicType(sl.at(0), sl.at(1));
|
|
|
|
const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true);
|
|
|
|
#ifdef RTI_HEADER_AVAILABLE
|
|
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;
|
|
#else
|
|
// RTI not available - skip extensibility parsing
|
|
Q_UNUSED(tmp)
|
|
#endif
|
|
|
|
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());
|
|
}
|
|
|
|
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;
|
|
|
|
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 : std::as_const(m_functionTopics))
|
|
{
|
|
const QStringList sl1 = ft.requestTopicType.split(QStringLiteral("::"));
|
|
if (sl1.size() == 2)
|
|
loadExternalTopicType(sl1.at(0), sl1.at(1));
|
|
const QStringList sl2 = ft.replyTopicType.split(QStringLiteral("::"));
|
|
if (sl2.size() == 2)
|
|
loadExternalTopicType(sl2.at(0), sl2.at(1));
|
|
}
|
|
}
|
|
|
|
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"));
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
|
|
if (filePath.startsWith("--") || filePath.trimmed().isEmpty())
|
|
continue;
|
|
const QString fileName = QString::fromLocal8Bit(filePath.trimmed());
|
|
const QStringList parts = fileName.split(QStringLiteral("/"));
|
|
getServiceNameInfoFromFilePath(parts);
|
|
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] = fileName;
|
|
DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation << " = " << fileName;
|
|
}
|
|
else
|
|
{
|
|
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).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 QStringList& parts)
|
|
{
|
|
if (parts.size() < 2)
|
|
return;
|
|
|
|
const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first();
|
|
const QString serviceName = parts.at(parts.size() - 2);
|
|
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();
|
|
}
|