#include "XmlReader.h" #include #include #include #include #include #include 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(','), QString::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 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 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 QVector 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()); } 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 : qAsConst(m_functionTopics)) { const QVector sl1 = ft.requestTopicType.splitRef(QStringLiteral("::")); if (sl1.size() == 2) loadExternalTopicType(sl1.at(0).toString(), sl1.at(1).toString()); const QVector 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 XmlReader::parseForeignKey(const QDomElement &foreignKey) const { QSharedPointer 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> 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 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 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 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(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 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 QVector parts = fileName.splitRef(QLatin1Char('/')); 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.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 &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 &parts) { if (parts.size() < 2) return; 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(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(); }