#include "StructBuilder.h" #include #include using namespace CodeGen; class DataTypeInfoIdl : public DataTypeInfo { protected: DataTypeInfoIdl(const QString &name, const QString &idlType, bool isPod) : DataTypeInfo(name, isPod) , m_idlType(idlType) {} public: QString idlType() const override final { return m_idlType; } private: const QString m_idlType; }; class DataTypeInfoSimple : public DataTypeInfoIdl { public: DataTypeInfoSimple(const QString &name, const QString &idlType, const QString &ddsType, const QString &sqlType, const QString &cppType, bool isPod) : DataTypeInfoIdl(name, idlType, isPod) , m_ddsType(ddsType) , m_sqlType(sqlType) , m_cppType(cppType) {} QString ddsType() const override final { return m_ddsType; } QString sqlType() const override final { return m_sqlType; } QString cppType() const override final { return m_cppType; } protected: QString m_ddsType; QString m_sqlType; QString m_cppType; }; class DataTypeInfoStructure final : public DataTypeInfoSimple { public: DataTypeInfoStructure(const QString &name, const QString &idlType, StructBuilder *structBuilder) : DataTypeInfoSimple(name, idlType, QString(), idlType, idlType, false) { const CompleteTopicTypeInformation ctti = structBuilder->topicTypeInformationByStructType(idlType); Q_ASSERT(ctti.isValid()); m_member.reserve(ctti.members.size()); for (const IdlData &idlData : ctti.members) m_member += DataTypeInfo::fromIdlDataType(idlData.name, idlData.type, structBuilder); m_serviceName = ctti.serviceName; if (ctti.name.startsWith(m_serviceName + QStringLiteral("::"))) m_ddsType = ctti.name; else m_ddsType = m_serviceName + QStringLiteral("::") + ctti.name; } bool isStructure() const override final { return true; } virtual QVector> member() const override final { return m_member; } private: QVector> m_member; QString m_serviceName; }; class DataTypeInfoSequence final : public DataTypeInfoIdl { public: DataTypeInfoSequence(const QString &name, const QString &idlType, StructBuilder *structBuilder, const QString &svcName) : DataTypeInfoIdl(name, idlType, false) { const int start = idlType.indexOf(QLatin1Char('<')); const int end = idlType.lastIndexOf(QLatin1Char('>')); Q_ASSERT(start >= 0 && end >= 0 && end > start); const QString inner = idlType.mid(start + 1, end - start - 1); m_innerDataType = DataTypeInfo::fromIdlDataType(QStringLiteral("inner"), inner, structBuilder, svcName); Q_ASSERT(m_innerDataType); } QString ddsType() const override final { return QLatin1String("std::vector<") + m_innerDataType->ddsType() + QLatin1Char('>'); } QString sqlType() const override final { return QStringLiteral("jsonb"); } QString cppType() const override final { return QLatin1String("std::vector<") + m_innerDataType->cppType() + QLatin1Char('>'); } QString cppTypeConstRef() const override final { return QLatin1String("const std::vector<") + m_innerDataType->cppType() + QLatin1String("> &"); } virtual bool isSequence() const override final { return true; } QSharedPointer innerDataType() const override final { return m_innerDataType; } private: QSharedPointer m_innerDataType; }; QSharedPointer DataTypeInfo::fromIdlDataType(const QString &name, const QString &idlType, StructBuilder *structBuilder, const QString &svcName) { const QString fullIdlType = (svcName.isEmpty() || idlType.contains(QLatin1String("::"))) ? idlType : svcName + QLatin1String("::") + idlType; if (idlType == QLatin1String("sequence")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("std::vector"), QStringLiteral("bytea"), QStringLiteral("QByteArray"), false)); if (idlType == QLatin1String("boolean")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("bool"), QStringLiteral("boolean"), QStringLiteral("bool"), true)); if (idlType == QLatin1String("char")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("char"), QStringLiteral("char"), QStringLiteral("char"), true)); if (idlType == QLatin1String("double")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("double"), QStringLiteral("double precision"), QStringLiteral("double"), true)); if (idlType == QLatin1String("long")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("int32_t"), QStringLiteral("integer"), QStringLiteral("qint32"), true)); if (idlType == QLatin1String("long long")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("rti::core::int64"), QStringLiteral("bigint"), QStringLiteral("qint64"), true)); if (idlType == QLatin1String("short")) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("int16_t"), QStringLiteral("smallint"), QStringLiteral("qint16"), true)); if (idlType.startsWith(QLatin1String("string"))) return QSharedPointer(new DataTypeInfoSimple(name, idlType, QStringLiteral("std::string"), QStringLiteral("text"), QStringLiteral("std::string"), false)); if (idlType.startsWith(QLatin1String("sequence"))) return QSharedPointer(new DataTypeInfoSequence(name, idlType, structBuilder, svcName)); const EnumInformation ei = structBuilder->enumInformationByType(fullIdlType, true, false); if (ei.isValid()) return QSharedPointer(new DataTypeInfoSimple(name, idlType, ei.enumName, QStringLiteral("integer"), ei.enumName, true)); const CompleteTopicTypeInformation ctti = structBuilder->topicTypeInformationByStructType(fullIdlType, true); if (ctti.isValid()) return QSharedPointer(new DataTypeInfoStructure(name, idlType, structBuilder)); return QSharedPointer(new DataTypeInfoSimple(name, idlType, idlType, QStringLiteral("integer"), idlType, true)); } StructBuilder::StructBuilder(XmlReader *xmlReader) : m_needPoint3d(false) , m_needPgArrayInclude(false) , m_xmlReader(xmlReader) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.StructBuilder")) , m_loggerCSV(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("fileCSV")) {} bool StructBuilder::buildStructures() { m_structInfos.clear(); m_topicTypeInfos.clear(); m_enumInfos.clear(); m_sqlTableInformations.clear(); m_sqlTableInformationsAlreadyAvailable.clear(); m_sqlViewInformations.clear(); m_externalTopicTypes.clear(); m_serializeFromToJsonTopics.clear(); m_jsonStructureInformation.clear(); m_needPoint3d = false; m_needPgArrayInclude = false; const QMap > tables = m_xmlReader->getTables(); for (const QSharedPointer &ti : tables) { CompleteTableInformation cti(*ti); if (cti.exerciseDependency == ExerciseDependency::Undefined) cti.exerciseDependency = m_xmlReader->defaultExerciseDependency(); m_sqlTableInformations[ti->name] = cti; } const QMap > views = m_xmlReader->getViews(); for (const QSharedPointer &vi : views) { ViewInformation cti(*vi); if (cti.exerciseDependency == ExerciseDependency::Undefined) cti.exerciseDependency = m_xmlReader->defaultExerciseDependency(); m_sqlViewInformations[vi->name] = cti; } const QVector enumData = m_xmlReader->getEnumerations(); for (const EnumInformation &ei : enumData) { if (!handleEnum(ei)) return false; } const QVector structsToGenerate = m_xmlReader->getStructToGenerate(); for (const ServiceStructInformation &ssi : structsToGenerate) { if (!handleTable(ssi)) return false; } const QVector idlStructsToGenerate = m_xmlReader->getIdlStructToGenerate(); for (const TopicTypeInformation &tti : idlStructsToGenerate) { DEBUG_LOG(m_loggerCSV) << tti.name; if (!handleIdlStructInfo(tti, m_topicTypeInfos)) return false; } const QVector idlExternalTopicTypes = m_xmlReader->getExternalTopicTypes(); for (const TopicTypeInformation &tti : idlExternalTopicTypes) { const ServiceTypeStruct sts = extractServiceAndTypeFromTypename(tti.name); if (!sts.isExternalType()) { ERROR_LOG(m_logger) << "Invalid external topic type name: '" << tti.name << "'"; return false; } if (!handleIdlStructInfo(tti, m_externalTopicTypes, sts.service)) return false; } return true; } bool StructBuilder::handleEnum(const EnumInformation &ei) { m_enumInfos.push_back(ei); if (ei.tableName.isEmpty()) return true; QSharedPointer ti = m_xmlReader->getTable(ei.tableName); if (ti.isNull()) { WARNING_LOG(m_logger) << "Could not find table information for enum: " << ei.enumName << ", table: " << ei.tableName; return false; } CompleteTableInformation cti = m_sqlTableInformations[ei.tableName]; for (const ColumnInformation &ci : qAsConst(ti->plainColumns)) { MemberInformation mi; mi.db = FullQualifiedColumnInformation(ci, ei.tableName); cti.columns.push_back(std::move(mi)); } m_sqlTableInformations[ei.tableName] = cti; return true; } CompleteStructInformation StructBuilder::structInformationByTableName(const QString &tableName) const { for (const CompleteStructInformation &csi : m_structInfos) { if (csi.primaryTable->name == tableName) return csi; } return CompleteStructInformation(); } EnumInformation StructBuilder::enumInformationByType(const QString &type, bool checkExternals, bool verbose) { if (type.isEmpty()) return EnumInformation(); const ServiceTypeStruct extInfo = extractServiceAndTypeFromTypename(type); if (extInfo.isExternalType()) { if (!checkExternals) { ERROR_LOG(m_logger) << "Could not find enumeration '" << type << "'"; return EnumInformation(); } if (extInfo.type.isEmpty()) { WARNING_LOG(m_logger) << "Could not parse external enumeration '" << type << "'"; return EnumInformation(); } return m_xmlReader->getExternalEnumeration(extInfo.service, extInfo.type, verbose); } for (const EnumInformation &ei : qAsConst(m_enumInfos)) { if (ei.enumName == type) return ei; } if (verbose) ERROR_LOG(m_logger) << "Could not find enumeration '" << type << "'"; return EnumInformation(); } CompleteTopicTypeInformation StructBuilder::topicTypeInformationByStructType(const QString &type, bool checkExternals) { DEBUG_LOG(m_logger) << "topicTypeInformationByStructType(" << type << ")"; if (type.isEmpty()) return CompleteTopicTypeInformation(); const ServiceTypeStruct extInfo = extractServiceAndTypeFromTypename(type); if (extInfo.isExternalType()) { if (!checkExternals) return CompleteTopicTypeInformation(); return externalTopicTypeByStructType(extInfo); } for (const CompleteTopicTypeInformation &csi : qAsConst(m_topicTypeInfos)) { if (csi.name == type) return csi; } DEBUG_LOG(m_logger) << "Type '" << type << "' not found in idlStructs - maybe not yet exported or an enum"; const QVector idlStructsToGenerate = m_xmlReader->getIdlStructToGenerate(); for (const TopicTypeInformation &tti : idlStructsToGenerate) { if (tti.name == type) { DEBUG_LOG(m_logger) << "Found not yet exported type " << type; if (!handleIdlStructInfo(tti, m_topicTypeInfos)) return CompleteTopicTypeInformation(); return topicTypeInformationByStructType(type); } } ERROR_LOG(m_logger) << "Could not find type '" << type << "' in idlStructs"; return CompleteTopicTypeInformation(); } CompleteTopicTypeInformation StructBuilder::externalTopicTypeByStructType(const ServiceTypeStruct &sts) { const QString typeToSearch = sts.fqType(); DEBUG_LOG(m_logger) << "externalTopicTypeByStructType " << typeToSearch; for (const CompleteTopicTypeInformation &ctti : qAsConst(m_externalTopicTypes)) { if (ctti.name == typeToSearch) return ctti; } if (!loadExternalTopicType(sts)) return CompleteTopicTypeInformation(); return externalTopicTypeByStructType(sts); } bool StructBuilder::loadExternalTopicType(const ServiceTypeStruct &sts) { DEBUG_LOG(m_logger) << "loadExternalTopicType(" << sts.fqType() << ")"; const TopicTypeInformation tti = m_xmlReader->loadExternalTopicType(sts.service, sts.type); if (!tti.isValid()) { DEBUG_LOG(m_logger) << "Could not find external topic '" << sts.fqType() << "'"; return false; } if (!handleIdlStructInfo(tti, m_externalTopicTypes, sts.service)) return false; return true; } bool StructBuilder::getColumnsFromTable(const QString &tableName, QVector &columns, QVector &tableNames) const { if (tableName.isEmpty()) return true; const QString parentTable = m_xmlReader->getStruct2BaseClassRelations().value(tableName); if (!getColumnsFromTable(parentTable, columns, tableNames)) return false; tableNames += tableName; const QSharedPointer ti = m_xmlReader->getTable(tableName); QVector constraints; constraints.reserve(ti->constraints.size()); for (const ConstraintInformation &ci : qAsConst(ti->constraints)) { if (ci.affectedColumns.size() != 1) continue; if (ci.type != ConstraintInformation::Type::index) continue; constraints += ci; } columns.reserve(columns.size() + ti->plainColumns.size()); for (const auto &plainColum : qAsConst(ti->plainColumns)) { ColumnInformationEx cie(ColumnInformationEx(plainColum, ti)); for (int j = 0; j < constraints.size(); ++j) { const ConstraintInformation &ci = constraints.at(j); if (ci.affectedColumns.first() == cie.columnName) { cie.generateSelectRemove = true; constraints.remove(j); break; } } columns += cie; } return true; } bool StructBuilder::handleTable(const ServiceStructInformation &ssi) { DEBUG_LOG(m_loggerCSV) << "StructInfo name: #" << ssi.name; CompleteStructInformation csi; csi.structName = ssi.name; csi.primaryTable = m_xmlReader->getTable(ssi.primaryTable); csi.viewName = csi.primaryTable->name + QLatin1String("_view"); csi.hasStaticContent = ssi.hasStaticContent; csi.dbNotifications = ssi.dbNotifications; DEBUG_LOG(m_loggerCSV) << "primaryTable: " << csi.primaryTable; DEBUG_LOG(m_loggerCSV) << "hasStaticContent: " << csi.hasStaticContent; QVector columns; QVector tableNames; if (!getColumnsFromTable(ssi.primaryTable, columns, tableNames)) { WARNING_LOG(m_logger) << "Could not retrieve columns from table " << ssi.primaryTable; return false; } DEBUG_LOG(m_logger) << "Got " << columns.size() << " columns for table " << ssi.primaryTable; QSet sqlTables; csi.member.reserve(columns.size() + 1); MemberInformation miPkFirstTable, miPkLastTable; for (const ColumnInformationEx &ci : qAsConst(columns)) { DEBUG_LOG(m_logger) << "handling column " << ci.columnName; const QSharedPointer ti = ci.table; if (ti.isNull()) { WARNING_LOG(m_logger) << "Could not find table for column " << ci.columnName << " primary table is " << ssi.primaryTable; return false; } csi.tables += ti; sqlTables += ti->name; MemberInformation memberInfo; memberInfo.ea.column = ci.columnName; memberInfo.ea.table = ti->name; memberInfo.ea.type = ci.datatype; memberInfo.db = FullQualifiedColumnInformation(ci, memberInfo.ea.table); if (ci.hasSequence) { memberInfo.db.usedForInsert = !ci.isPrimaryKey; memberInfo.db.hasSequence = true; } else memberInfo.db.usedForInsert = true; memberInfo.db.usedForView = !ci.isPrimaryKey; memberInfo.cpp.structName = ssi.name; memberInfo.cpp.memberName = camelCaseString(ci.columnName); memberInfo.cpp.type = ci.datatype; memberInfo.cpp.usedForCppStruct = !ci.isPrimaryKey; if (ci.isForeignKey) { const QSharedPointer fki = ti->fkInformationForColumn(ti->name, ci.columnName); if (!fki.isNull()) { for(const auto &tbl : qAsConst(csi.tables)) { if (tbl->name == fki->destination.tableName) { memberInfo.cpp.isReadOnly = true; memberInfo.fk = fki; memberInfo.cpp.memberName = QLatin1String("priv_") + memberInfo.cpp.memberName; break; } } if (memberInfo.fk.isNull()) { if (fki->destination.service.isEmpty()) { for (const EnumInformation &ei : qAsConst(m_enumInfos)) { if (ei.tableName == fki->destination.tableName) { memberInfo.enumeration = QSharedPointer(new EnumInformation(ei)); break; } } } else { QString enumType = fki->destination.service + QStringLiteral("::") + camelCaseString(fki->destination.tableName, true); EnumInformation ei = enumInformationByType(enumType, true, false); DEBUG_LOG(m_logger) << "Checking if " << enumType << " is an enum: " << ei.isValid(); if (!ei.isValid() && fki->destination.tableName.contains(QLatin1Char('_'))) { enumType = fki->destination.service + QStringLiteral("::") + camelCaseString(fki->destination.tableName.section(QLatin1Char('_'), 1), true); ei = enumInformationByType(enumType, true, false); DEBUG_LOG(m_logger) << "Checking if " << enumType << " is an enum: " << ei.isValid(); } if (ei.isValid() && ei.tableName == fki->destination.tableName) memberInfo.enumeration = QSharedPointer(new EnumInformation(ei)); } } } } MemberInformation memberInfoJson = memberInfo; memberInfoJson.cpp.usedForJson = true; memberInfoJson.cpp.usedForCppStruct = false; memberInfoJson.db.isForeignKey = false; memberInfoJson.db.generateSelectRemove = false; memberInfoJson.db.usedForView = false; memberInfoJson.db.usedForInsert = false; if (ci.isOptional) { memberInfo.cpp.isOptional = true; memberInfoJson.cpp.isOptional = true; memberInfo.ea.type = QStringLiteral("QVariant"); memberInfo.cpp.type = memberInfo.ea.type; } if (!fixupCustomDbDatatypes(memberInfo, csi.member) || !fixupQVariantConversions(memberInfo)) return false; if (!fixupCustomDbDatatypes(memberInfoJson, csi.member) || !fixupQVariantConversions(memberInfoJson)) return false; if (ci.isPrimaryKey && tableNames.first() == ti->name) { miPkFirstTable = memberInfo; } if (ci.isPrimaryKey && tableNames.last() == ti->name) { miPkLastTable = memberInfo; } csi.member.push_back(memberInfo); csi.member.push_back(memberInfoJson); addToSqlTableInformation(memberInfo); DEBUG_LOG(m_loggerCSV) << memberInfo.toString(); } if (!miPkFirstTable.isEmpty()) { miPkFirstTable.db.usedForInsert = false; miPkFirstTable.db.usedForView = true; miPkFirstTable.cpp.usedForCppStruct = true; csi.member.push_front(miPkFirstTable); } if (tableNames.size() > 1 && !miPkLastTable.isEmpty()) { miPkLastTable.db.usedForInsert = false; miPkLastTable.db.usedForView = true; miPkLastTable.cpp.usedForCppStruct = true; miPkLastTable.db.isAssignmentForPrimaryTablePk = true; miPkLastTable.db.columnNameView = miPkLastTable.db.table + QLatin1Char('_') + miPkLastTable.db.columnName; QString name = miPkLastTable.db.table; if (name.startsWith(m_xmlReader->getServiceAbbr().toLower())) name = name.mid(m_xmlReader->getServiceAbbr().size() + 1); miPkLastTable.cpp.memberName = camelCaseString(QLatin1String("priv_") + name + QLatin1String("_id")); csi.member.push_back(miPkLastTable); } m_structInfos.push_back(csi); m_sqlTableInformationsAlreadyAvailable += sqlTables; return true; } bool StructBuilder::handleIdlStructInfo(const TopicTypeInformation &tti, QVector &destination, const QString &svcName) { for (const CompleteTopicTypeInformation &c : qAsConst(destination)) { if (c.name == tti.name) return true; } CompleteTopicTypeInformation ctti(tti); ctti.members.reserve(tti.columns.size()); for (const auto &ci : tti.columns) { IdlData idlData(ci); idlData.dataTypeInfo = DataTypeInfo::fromIdlDataType(idlData.name, idlData.type, this, svcName); ctti.members.push_back(idlData); if (idlData.isKey && idlData.isOptional) { ERROR_LOG(m_logger) << "Column option isKey & isOptional are mutually exclusive!"; ERROR_LOG(m_logger) << "Change column '" << tti.name << "." << idlData.name << "'' to have either key or optional parameter "; return false; } } if (!svcName.isEmpty() && !ctti.baseValueType.isEmpty() && !ctti.baseValueType.contains(QLatin1String("::"))) ctti.baseValueType = svcName + QStringLiteral("::") + ctti.baseValueType; ctti.serviceName = svcName.isEmpty() ? m_xmlReader->getServiceNamespace() : svcName; destination.push_back(ctti); return true; } bool StructBuilder::fixupCustomDbDatatypes(MemberInformation &memberInfo, QVector &members) { if (!fixupDatatypePoint2d(memberInfo, members)) return false; if (!fixupDatatypePoint3d(memberInfo, members)) return false; if (!fixupDatatypeIntegerArray(memberInfo)) return false; return true; } bool StructBuilder::fixupDatatypePoint2d(MemberInformation &memberInfo, QVector &members) { if (memberInfo.ea.type.toLower() != QLatin1String("point_2d")) return true; MemberInformation mi = memberInfo; mi.db.datatype = QStringLiteral("double precision"); mi.cpp.type = QStringLiteral("QPointF"); mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); MemberInformation miX = mi; MemberInformation miY = mi; miX.db.columnName = memberInfo.db.columnName + QLatin1String("_x"); miY.db.columnName = memberInfo.db.columnName + QLatin1String("_y"); miY.cpp.usedForCppStruct = false; miY.cpp.usedForJson = false; miX.cpp.toVariantPost = QStringLiteral(".x()"); miY.cpp.toVariantPost = QStringLiteral(".y()"); miX.cpp.fromVariantPre1 = QStringLiteral(".rx()"); miY.cpp.fromVariantPre1 = QStringLiteral(".ry()"); miX.cpp.memberName = camelCaseString(miX.cpp.memberName); miX.cpp.toJsonPre = QStringLiteral("toJson("); miX.cpp.toJsonPost = QStringLiteral(")"); miX.cpp.fromJsonPre2 = QStringLiteral("fromJson("); miX.cpp.fromJsonPost = QStringLiteral(")"); members.push_back(miX); if (memberInfo.cpp.usedForCppStruct) addToSqlTableInformation(miX); DEBUG_LOG(m_loggerCSV) << miX.toString(); memberInfo = miY; return true; } bool StructBuilder::fixupDatatypePoint3d(MemberInformation &memberInfo, QVector &members) { if (memberInfo.ea.type.toLower() != QLatin1String("point_3d")) return true; MemberInformation mi = memberInfo; mi.db.datatype = QStringLiteral("double precision"); mi.cpp.type = QStringLiteral("LtsUtils::QPoint3D"); mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); MemberInformation miX = mi; MemberInformation miY = mi; MemberInformation miZ = mi; miX.db.columnName = memberInfo.db.columnName + QLatin1String("_x"); miY.db.columnName = memberInfo.db.columnName + QLatin1String("_y"); miZ.db.columnName = memberInfo.db.columnName + QLatin1String("_z"); miY.cpp.usedForCppStruct = false; miZ.cpp.usedForCppStruct = false; miY.cpp.usedForJson = false; miZ.cpp.usedForJson = false; miX.cpp.toVariantPost = QStringLiteral(".x()"); miY.cpp.toVariantPost = QStringLiteral(".y()"); miZ.cpp.toVariantPost = QStringLiteral(".z()"); miX.cpp.fromVariantPre1 = QStringLiteral(".rx()"); miY.cpp.fromVariantPre1 = QStringLiteral(".ry()"); miZ.cpp.fromVariantPre1 = QStringLiteral(".rz()"); miX.cpp.memberName = camelCaseString(miX.cpp.memberName); miX.cpp.toJsonPre = QStringLiteral("toJson("); miX.cpp.toJsonPost = QStringLiteral(")"); miX.cpp.fromJsonPre2 = QStringLiteral("fromJson("); miX.cpp.fromJsonPost = QStringLiteral(")"); members.push_back(miX); members.push_back(miY); if (memberInfo.cpp.usedForCppStruct) { addToSqlTableInformation(miX); addToSqlTableInformation(miY); } DEBUG_LOG(m_loggerCSV) << miX.toString(); memberInfo = miZ; m_needPoint3d = true; return true; } bool StructBuilder::fixupDatatypeIntegerArray(MemberInformation &memberInfo) { if (memberInfo.ea.type.toLower() == QLatin1String("integer[]")) { memberInfo.db.datatype = memberInfo.ea.type.toLower(); memberInfo.cpp.type = QStringLiteral("LtsUtils::IntArray"); memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::IntArray::fromPgString("); memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); m_needPgArrayInclude = true; } else if (memberInfo.ea.type.toLower() == QLatin1String("bigint[]")) { memberInfo.db.datatype = memberInfo.ea.type.toLower(); memberInfo.cpp.type = QStringLiteral("LtsUtils::BigintArray"); memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::BigintArray::fromPgString("); memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); m_needPgArrayInclude = true; } else if (memberInfo.cpp.type.toLower() == QLatin1String("sequence")) { memberInfo.db.datatype = QStringLiteral("integer[]"); memberInfo.cpp.type = QStringLiteral("std::vector"); memberInfo.cpp.fromVariantPre2 = QStringLiteral("std::vector(LtsUtils::IntArray::fromPgString("); memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())())"); memberInfo.cpp.toVariantPre = QStringLiteral("LtsUtils::IntArray("); memberInfo.cpp.toVariantPost = QStringLiteral(").toPgString()"); m_needPgArrayInclude = true; } else if (memberInfo.ea.type.toLower() == QLatin1String("integer[][]")) { memberInfo.db.datatype = memberInfo.ea.type.toLower(); memberInfo.cpp.type = QStringLiteral("LtsUtils::IntArray2D"); memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::IntArray2D::fromPgString("); memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); m_needPgArrayInclude = true; } else if (memberInfo.ea.type.toLower() == QLatin1String("bigint[][]")) { memberInfo.db.datatype = memberInfo.ea.type.toLower(); memberInfo.cpp.type = QStringLiteral("LtsUtils::BigintArray2D"); memberInfo.cpp.fromVariantPre2 = QStringLiteral("LtsUtils::BigintArray2D::fromPgString("); memberInfo.cpp.fromVariantPost = QStringLiteral(".toString())"); memberInfo.cpp.toVariantPost = QStringLiteral(".toPgString()"); m_needPgArrayInclude = true; } return true; } bool StructBuilder::fixupQVariantConversions(MemberInformation &mi) const { if (!mi.cpp.toVariantPost.isEmpty()) return true; mi.cpp.toVariantPre.clear(); mi.cpp.toVariantPost.clear(); mi.cpp.fromVariantPre1.clear(); mi.cpp.fromVariantPre2.clear(); mi.cpp.fromVariantPost.clear(); if (mi.enumeration) { QString svcName(mi.enumeration->serviceName); if (!svcName.isEmpty()) svcName += QStringLiteral("::"); mi.cpp.type = svcName + mi.enumeration->enumName; mi.cpp.fromVariantPre2 = QStringLiteral("static_cast<") + svcName + mi.enumeration->enumName + QStringLiteral("_def::type>("); mi.cpp.fromVariantPost = QStringLiteral(".toInt())"); mi.cpp.toVariantPre = QStringLiteral("static_cast("); mi.cpp.toVariantPost = QStringLiteral(".underlying())"); if (!mi.enumeration->values.isEmpty()) { mi.cpp.defaultValue = svcName + mi.enumeration->enumName + QStringLiteral("::") + mi.enumeration->values.first().key; } return true; } const QString datatype = mi.cpp.type.toLower(); if (datatype == QLatin1String("bigint") || datatype == QLatin1String("qint64") || datatype == QLatin1String("long long")) { mi.cpp.type = QStringLiteral("qint64"); mi.cpp.fromVariantPost = QStringLiteral(".toLongLong()"); mi.cpp.fromJsonPost = QStringLiteral(".toDouble()"); mi.cpp.defaultValue = QLatin1Char('0'); } else if (datatype == QLatin1String("boolean") || datatype == QLatin1String("bool")) { mi.cpp.type = QStringLiteral("bool"); mi.cpp.fromVariantPost = QStringLiteral(".toBool()"); mi.cpp.defaultValue = QStringLiteral("false"); } else if (datatype == QLatin1String("bytea") || datatype == QLatin1String("QByteArray")) { mi.cpp.type = QStringLiteral("QByteArray"); mi.cpp.fromVariantPost = QStringLiteral(".toByteArray()"); mi.cpp.toJsonPre = QStringLiteral("QString::fromLatin1("); mi.cpp.toJsonPost = QStringLiteral(".toBase64())"); mi.cpp.fromJsonPre2 = QStringLiteral("QByteArray::fromBase64("); mi.cpp.fromJsonPost = QStringLiteral(".toString().toLatin1())"); mi.cpp.defaultValue.clear(); } else if (datatype == QLatin1String("sequence")) { mi.cpp.type = QStringLiteral("QByteArray"); mi.cpp.ddsType = QStringLiteral("std::vector"); mi.cpp.toVariantPre = QStringLiteral("octetSequenceToQVariant("); mi.cpp.toVariantPost = QStringLiteral(")"); mi.cpp.fromVariantPre2 = QStringLiteral("qVariantToOctetSequence("); mi.cpp.fromVariantPost = QStringLiteral(")"); mi.cpp.toJsonPre = QStringLiteral("toJson("); mi.cpp.toJsonPost = QStringLiteral(")"); mi.cpp.fromJsonPre2 = QStringLiteral("fromJson>("); mi.cpp.fromJsonPost = QStringLiteral(")"); mi.cpp.defaultValue.clear(); } else if (datatype == QLatin1String("char")) { mi.cpp.type = QStringLiteral("char"); mi.cpp.fromVariantPost = QStringLiteral(".toChar().toLatin1()"); mi.cpp.fromJsonPost = QStringLiteral(".toInt()"); mi.cpp.defaultValue = QStringLiteral("\\0"); } else if (datatype == QLatin1String("integer") || datatype == QLatin1String("qint32") || datatype == QLatin1String("long")) { mi.cpp.type = QStringLiteral("qint32"); mi.cpp.fromVariantPost = QStringLiteral(".toInt()"); mi.cpp.defaultValue = QLatin1Char('0'); } else if (datatype == QLatin1String("real") || datatype == QLatin1String("double") || datatype == QLatin1String("double precision") || datatype == QLatin1String("float")) { mi.cpp.type = QStringLiteral("double"); mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); mi.cpp.defaultValue = QStringLiteral("0.0"); mi.db.datatype = QStringLiteral("double precision"); } else if (datatype == QLatin1String("numeric") || datatype == QLatin1String("decimal")) { mi.cpp.type = QStringLiteral("double"); mi.cpp.fromVariantPost = QStringLiteral(".toDouble()"); mi.cpp.defaultValue = QStringLiteral("0.0"); mi.db.datatype = QStringLiteral("numeric"); } else if (datatype == QLatin1String("smallint") || datatype == QLatin1String("qint16") || datatype == QLatin1String("short")) { mi.cpp.type = QStringLiteral("qint16"); mi.cpp.fromVariantPost = QStringLiteral(".toInt()"); mi.cpp.defaultValue = QLatin1Char('0'); } else if (datatype == QLatin1String("text") || datatype == QLatin1String("QString")) { mi.cpp.type = QStringLiteral("QString"); mi.cpp.fromVariantPost = QStringLiteral(".toString()"); mi.cpp.defaultValue = QString(); } else if (datatype == QLatin1String("json") || datatype == QLatin1String("jsonb")) { mi.cpp.type = QStringLiteral("QString"); mi.cpp.fromVariantPre2 = QStringLiteral("qVariantToJsonString("); mi.cpp.fromVariantPost = QStringLiteral(")"); mi.cpp.fromJsonPre2 = QStringLiteral("jsonValueToJsonString("); mi.cpp.fromJsonPost = QStringLiteral(")"); mi.cpp.toVariantPre = QStringLiteral("fixupEmptyJsonString("); mi.cpp.toVariantPost = QStringLiteral(")"); mi.cpp.defaultValue = QStringLiteral("QLatin1String(\"{}\")"); } else if (datatype.startsWith(QLatin1String("string"))) { mi.cpp.type = QStringLiteral("std::string"); mi.cpp.toVariantPre = QStringLiteral("QString::fromStdString("); mi.cpp.toVariantPost = QStringLiteral(")"); mi.cpp.fromVariantPost = QStringLiteral(".toString().toStdString()"); mi.cpp.fromJsonPre2 = QStringLiteral("fromJson("); mi.cpp.fromJsonPost = QStringLiteral(")"); } else if (datatype == QLatin1String("timestamp") || datatype == QLatin1String("qdatetime")) { mi.db.datatype = QStringLiteral("bigint"); if (mi.db.defaultValue.toLower() == QStringLiteral("now()")) mi.db.defaultValue = QStringLiteral("(extract(epoch from now()) * 1000)"); mi.cpp.type = QStringLiteral("QDateTime"); mi.cpp.fromVariantPre2 = QStringLiteral("NSqlQuery::qVariantToQDateTime("); mi.cpp.fromVariantPost = QStringLiteral(")"); mi.cpp.toVariantPre = QStringLiteral("NSqlQuery::qDateTimeToQVariant("); mi.cpp.toVariantPost = QStringLiteral(")"); mi.cpp.toJsonPre = QLatin1String(""); mi.cpp.toJsonPost = QStringLiteral(".toMSecsSinceEpoch()"); mi.cpp.fromJsonPre2 = QStringLiteral("QDateTime::fromMSecsSinceEpoch("); mi.cpp.fromJsonPost = QStringLiteral(".toDouble())"); } else if (datatype == QLatin1String("qvariant")) { } else { ERROR_LOG(m_logger) << "Unknown cpp datatype: " << mi.cpp.type; return false; } return true; } QVector StructBuilder::buildDbTableInformationFromIdlData(const CompleteTopicTypeInformation &ctti, const QString &tableName, const QString &columnNamePrefix, const QString &cppStructure) { QVector ret; if (!ctti.isValid()) return ret; if (!ctti.baseValueType.isEmpty()) { ret += buildDbTableInformationFromIdlData(topicTypeInformationByStructType(ctti.baseValueType), tableName, columnNamePrefix, cppStructure); } for (const IdlData &member : ctti.members) { if (member.dataTypeInfo->isStructure() && !member.isOptional) { const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(member.type); if (!cttiChild.isValid()) { ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; continue; } QString pre; if (!columnNamePrefix.isEmpty()) pre += columnNamePrefix; pre += member.name + QLatin1Char('_'); ret += buildDbTableInformationFromIdlData(cttiChild, tableName, StructBuilder::camelCaseToUnderline(pre), cppStructure + member.name + QStringLiteral("().") ); continue; } if(member.dataTypeInfo->isSequence() && member.dataTypeInfo->innerDataType()->isStructure()) { const CompleteTopicTypeInformation &cttiChild = topicTypeInformationByStructType(member.dataTypeInfo->innerDataType()->idlType()); if (!cttiChild.isValid()) { ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; continue; } buildDbTableInformationFromIdlData(cttiChild, tableName, columnNamePrefix, member.name ); } MemberInformation mi; mi.isDdsKey = member.isKey; mi.db.table = tableName; mi.db.columnName = columnNamePrefix + StructBuilder::camelCaseToUnderline(member.name); mi.db.datatype = member.dataTypeInfo->sqlType(); mi.db.usedForView = true; mi.cpp.memberName = cppStructure + member.name; mi.cpp.type = member.type; mi.cpp.usedForCppStruct = true; mi.cpp.isOptional = member.isOptional; const EnumInformation ei = enumInformationByType(member.type, true, false); if (ei.isValid()) mi.enumeration = QSharedPointer(new EnumInformation(ei)); if ((member.dataTypeInfo->isSequence() && member.dataTypeInfo->innerDataType()->idlType() != QLatin1String("octet")) || (member.dataTypeInfo->isStructure() && member.isOptional)) { mi.db.datatype = QStringLiteral("jsonb"); mi.cpp.toJsonPre = QStringLiteral("toJson("); mi.cpp.toJsonPost = QStringLiteral(")"); mi.cpp.type = member.dataTypeInfo->ddsType(); mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); mi.cpp.fromVariantPost = QStringLiteral(")"); if (member.dataTypeInfo->isSequence()) addToSerializeFromToJsonTopics(member.dataTypeInfo->innerDataType()); else addToSerializeFromToJsonTopics(DataTypeInfo::fromIdlDataType(mi.cpp.memberName, member.type, this)); } else { fixupCustomDbDatatypes(mi, ret); fixupQVariantConversions(mi); } ret += mi; } return ret; } bool StructBuilder::buildJSonStructureInformationFromIdlData(const CompleteTopicTypeInformation &ctti) { if (!ctti.isValid()) return false; if (m_jsonStructureInformation.contains(ctti.name)) return true; if (!ctti.baseValueType.isEmpty()) { if (!buildJSonStructureInformationFromIdlData(topicTypeInformationByStructType(ctti.baseValueType))) return false; } QVector content; content.reserve(ctti.members.size()); for (const IdlData &member : ctti.members) { MemberInformation mi; mi.isDdsKey = member.isKey; mi.db.table = ctti.name; mi.db.columnName = StructBuilder::camelCaseToUnderline(member.name); mi.db.datatype = member.dataTypeInfo->sqlType(); mi.db.usedForView = true; mi.cpp.memberName = member.name; mi.cpp.type = member.type; mi.cpp.usedForCppStruct = true; mi.cpp.isOptional = member.isOptional; if (member.dataTypeInfo->isStructure()) { const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(member.type); if (!cttiChild.isValid()) { ERROR_LOG(m_logger) << "Can not find structure '" << member.type << "'"; continue; } if (!buildJSonStructureInformationFromIdlData(cttiChild)) return false; mi.cpp.toJsonPre = QStringLiteral("toJson("); mi.cpp.toJsonPost = QStringLiteral(")"); mi.cpp.type = member.dataTypeInfo->ddsType(); mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); mi.cpp.fromVariantPost = QStringLiteral(")"); } else if (member.dataTypeInfo->isSequence() && member.dataTypeInfo->innerDataType()->idlType() != QLatin1String("octet")) { auto inner = member.dataTypeInfo->innerDataType(); if (inner->isStructure()) { const CompleteTopicTypeInformation cttiChild = topicTypeInformationByStructType(inner->idlType()); if (cttiChild.isValid()) { if (!buildJSonStructureInformationFromIdlData(cttiChild)) return false; } else WARNING_LOG(m_logger) << "Can not find structure '" << member.type << "'"; } mi.db.datatype = QStringLiteral("jsonb"); mi.cpp.toJsonPre = QStringLiteral("toJson("); mi.cpp.toJsonPost = QStringLiteral(")"); mi.cpp.type = member.dataTypeInfo->ddsType(); mi.cpp.fromVariantPre2 = QStringLiteral("fromJson<") + member.dataTypeInfo->ddsType() + QStringLiteral(">("); mi.cpp.fromVariantPost = QStringLiteral(")"); } else { const EnumInformation ei = enumInformationByType(member.type, true, false); if (ei.isValid()) mi.enumeration = QSharedPointer(new EnumInformation(ei)); fixupCustomDbDatatypes(mi, content); fixupQVariantConversions(mi); } content += mi; } m_jsonStructureInformation[ctti.name] = content; return true; } CompleteTableInformation StructBuilder::completeTableInformation(const CompleteTopicTypeInformation &ctti) { CompleteTableInformation cti; cti.structName = ctti.name; cti.name = QStringLiteral("rec_") + camelCaseToUnderline(ctti.name); cti.comment = QStringLiteral("recorded data for topic ") + ctti.name; cti.columns = buildDbTableInformationFromIdlData(ctti, cti.name, QString(), QString()); buildJSonStructureInformationFromIdlData(ctti); cti.exerciseDependency = ExerciseDependency::Exercise; return cti; } MemberInformation StructBuilder::getRecordPrimaryKey() const { MemberInformation miPrimaryKey; miPrimaryKey.db.isPrimaryKey = true; miPrimaryKey.db.columnName = QStringLiteral("rec_id"); miPrimaryKey.db.datatype = QStringLiteral("bigint"); miPrimaryKey.db.hasSequence = true; miPrimaryKey.db.sequenceStart = 1; miPrimaryKey.db.sequenceIncrement = 1; miPrimaryKey.db.usedForView = false; miPrimaryKey.cpp.type = QStringLiteral("bigint"); miPrimaryKey.cpp.usedForCppStruct = false; fixupQVariantConversions(miPrimaryKey); return miPrimaryKey; } CompleteTableInformation StructBuilder::getRecordingTopicTable(const QString &structName) { const auto vec = structName.contains(QLatin1String("::")) ? m_externalTopicTypes : m_topicTypeInfos; for (const CompleteTopicTypeInformation &ctti : qAsConst(vec)) { if (structName != ctti.name) continue; MemberInformation miPrimaryKey(getRecordPrimaryKey()); miPrimaryKey.db.table = ctti.name; return getRecordingTopicTable(ctti, getRecordPrimaryKey()); } return CompleteTableInformation(); } CompleteTableInformation StructBuilder::getRecordingTopicTable(const CompleteTopicTypeInformation &ctti, const MemberInformation &miPrimaryKey) { CompleteTableInformation cti = completeTableInformation(ctti); cti.columns.push_back(miPrimaryKey); return cti; } QMap StructBuilder::getRecordingTopicTables() { QMap ret; MemberInformation miPrimaryKey(getRecordPrimaryKey()); for (const CompleteTopicTypeInformation &ctti : qAsConst(m_topicTypeInfos)) { if (!ctti.recordTopic) continue; if (!ctti.isDataObject) { WARNING_LOG(m_logger) << "Trying to record topic '" << ctti.name << "' which is not directly published - ignoring!"; continue; } DEBUG_LOG(m_logger) << "Rec table: " << ctti.name; miPrimaryKey.db.table = ctti.name; ret.insert(ctti.name, getRecordingTopicTable(ctti, miPrimaryKey)); } return ret; } void StructBuilder::addToSerializeFromToJsonTopics(const QSharedPointer &toAdd) { typedef QSharedPointer sDTI; for (const sDTI &dti : qAsConst(m_serializeFromToJsonTopics)) { if (dti->cppType() == toAdd->cppType()) return; } m_serializeFromToJsonTopics += toAdd; } QVector> StructBuilder::getSerializeFromToJsonTopics() const { return m_serializeFromToJsonTopics; } QString StructBuilder::firstCharLowercase(const QString &txt) { QString ret = txt; if (txt.isEmpty()) return ret; ret[0] = ret[0].toLower(); return ret; } QString StructBuilder::camelCaseString(const QString &in, bool bFirstUppercase) { Q_DECL_CONSTEXPR const QChar s_underline(QLatin1Char('_')); QString out; out.reserve(in.size()); bool bNextIsUpperCase = bFirstUppercase; for (const auto c : in) { if (c == s_underline) bNextIsUpperCase = true; else { out += (bNextIsUpperCase ? c.toUpper() : c); bNextIsUpperCase = false; } } return out; } QString StructBuilder::camelCaseToUnderline(const QString &in) { QString out; if (in.isEmpty()) return out; out.reserve(in.size()); out += in.at(0).toLower(); for (int i = 1; i < in.size(); ++i) { const QChar c = in.at(i); if (!c.isLetter() || c.isLower()) out += c; else out += QLatin1Char('_') + c.toLower(); } return out; } StructBuilder::ServiceTypeStruct StructBuilder::extractServiceAndTypeFromTypename(const QString &type) { const QVector sl = type.splitRef(QStringLiteral("::")); QStringRef extServiceRef;; QStringRef extTypeRef; if (sl.size() == 1) extTypeRef = sl.at(0); else if (sl.size() == 2) { extServiceRef = sl.at(0); extTypeRef = sl.at(1); } else WARNING_LOG(m_logger) << "Error parsing type: " << type; StructBuilder::ServiceTypeStruct ret; if (!extServiceRef.isEmpty()) { if (extServiceRef.contains(QLatin1Char('<'))) ret.service = extServiceRef.split(QLatin1Char('<')).at(1).toString(); else ret.service = extServiceRef.toString(); } if (!extTypeRef.isEmpty()) { if (extTypeRef.contains(QLatin1Char('>'))) ret.type = extTypeRef.split(QLatin1Char('>')).at(0).toString(); else ret.type = extTypeRef.toString(); } return ret; } void StructBuilder::addToSqlTableInformation(const MemberInformation &mi) { if (!m_sqlTableInformationsAlreadyAvailable.contains(mi.db.table)) m_sqlTableInformations[mi.db.table].columns.append(mi); }