summaryrefslogtreecommitdiffstats
path: root/tools/datatypecodegenerator
diff options
context:
space:
mode:
Diffstat (limited to 'tools/datatypecodegenerator')
-rw-r--r--tools/datatypecodegenerator/CMakeLists.txt29
-rw-r--r--tools/datatypecodegenerator/datatypefilewriter.cpp1287
-rw-r--r--tools/datatypecodegenerator/datatypefilewriter.h80
-rw-r--r--tools/datatypecodegenerator/dependencydatatypevalidator.cpp107
-rw-r--r--tools/datatypecodegenerator/dependencydatatypevalidator.h38
-rw-r--r--tools/datatypecodegenerator/enumeratedtype.cpp58
-rw-r--r--tools/datatypecodegenerator/enumeratedtype.h34
-rw-r--r--tools/datatypecodegenerator/enumeratedvalue.cpp33
-rw-r--r--tools/datatypecodegenerator/enumeratedvalue.h24
-rw-r--r--tools/datatypecodegenerator/field.cpp142
-rw-r--r--tools/datatypecodegenerator/field.h62
-rw-r--r--tools/datatypecodegenerator/import.cpp42
-rw-r--r--tools/datatypecodegenerator/import.h28
-rw-r--r--tools/datatypecodegenerator/main.cpp181
-rw-r--r--tools/datatypecodegenerator/mappingfilegenerator.cpp636
-rw-r--r--tools/datatypecodegenerator/mappingfilegenerator.h64
-rw-r--r--tools/datatypecodegenerator/recursivedescentparser.cpp389
-rw-r--r--tools/datatypecodegenerator/recursivedescentparser.h66
-rw-r--r--tools/datatypecodegenerator/stringidentifier.cpp127
-rw-r--r--tools/datatypecodegenerator/stringidentifier.h101
-rw-r--r--tools/datatypecodegenerator/structuredtype.cpp118
-rw-r--r--tools/datatypecodegenerator/structuredtype.h56
-rw-r--r--tools/datatypecodegenerator/typedictionary.cpp116
-rw-r--r--tools/datatypecodegenerator/typedictionary.h46
-rw-r--r--tools/datatypecodegenerator/util.cpp31
-rw-r--r--tools/datatypecodegenerator/util.h15
-rw-r--r--tools/datatypecodegenerator/visitor.h26
-rw-r--r--tools/datatypecodegenerator/xmlelement.cpp32
-rw-r--r--tools/datatypecodegenerator/xmlelement.h26
29 files changed, 3994 insertions, 0 deletions
diff --git a/tools/datatypecodegenerator/CMakeLists.txt b/tools/datatypecodegenerator/CMakeLists.txt
new file mode 100644
index 0000000..98bc5c3
--- /dev/null
+++ b/tools/datatypecodegenerator/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2023 basysKom GmbH
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qopcuaxmldatatypes2cpp Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name qopcuaxmldatatypes2cpp)
+qt_internal_add_tool(${target_name}
+ TOOLS_TARGET
+ QtOpcUa
+ SOURCES
+ main.cpp
+ util.cpp util.h
+ datatypefilewriter.cpp datatypefilewriter.h
+ dependencydatatypevalidator.cpp dependencydatatypevalidator.h
+ enumeratedtype.cpp enumeratedtype.h
+ enumeratedvalue.cpp enumeratedvalue.h
+ field.cpp field.h
+ import.cpp import.h
+ mappingfilegenerator.cpp mappingfilegenerator.h
+ recursivedescentparser.cpp recursivedescentparser.h
+ stringidentifier.cpp stringidentifier.h
+ structuredtype.cpp structuredtype.h
+ typedictionary.cpp typedictionary.h
+ visitor.h
+ xmlelement.cpp xmlelement.h
+)
+qt_internal_return_unless_building_tools()
diff --git a/tools/datatypecodegenerator/datatypefilewriter.cpp b/tools/datatypecodegenerator/datatypefilewriter.cpp
new file mode 100644
index 0000000..aaa3139
--- /dev/null
+++ b/tools/datatypecodegenerator/datatypefilewriter.cpp
@@ -0,0 +1,1287 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "datatypefilewriter.h"
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "field.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qset.h>
+
+DataTypeFileWriter::DataTypeFileWriter(const QString &path,
+ const QString &prefix,
+ const QString &header)
+ : m_path(path)
+ , m_prefix(prefix)
+ , m_header(header)
+{}
+
+void DataTypeFileWriter::visit(XmlElement *xmlElement)
+{
+ Q_UNUSED(xmlElement);
+}
+
+void DataTypeFileWriter::visit(EnumeratedType *enumeratedType)
+{
+ bool isPrecodedType = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(enumeratedType->name())) {
+ isPrecodedType = true;
+ break;
+ }
+ }
+ if (!isPrecodedType) {
+ m_generateMapping.append(enumeratedType);
+ }
+}
+
+void DataTypeFileWriter::visit(EnumeratedValue *enumeratedValue)
+{
+ Q_UNUSED(enumeratedValue);
+}
+
+void DataTypeFileWriter::visit(Field *field)
+{
+ Q_UNUSED(field);
+}
+
+void DataTypeFileWriter::visit(Import *import)
+{
+ Q_UNUSED(import);
+}
+
+void DataTypeFileWriter::visit(StructuredType *structuredType)
+{
+ if (!structuredType->fields().empty()) {
+ bool isPrecodedType = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(structuredType->name())) {
+ isPrecodedType = true;
+ break;
+ }
+ }
+ if (!isPrecodedType) {
+ m_generateMapping.append(structuredType);
+ }
+ }
+}
+
+void DataTypeFileWriter::visit(TypeDictionary *typeDictionary)
+{
+ Q_UNUSED(typeDictionary);
+}
+
+void DataTypeFileWriter::writeLicenseHeader(QTextStream &output)
+{
+ if (!m_header.isEmpty())
+ output << m_header << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCpp(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ if (!m_generatedStructuredTypeFilenames.contains(
+ QStringLiteral("%1%2").arg(m_prefix, structuredType->name())))
+ m_generatedStructuredTypeFilenames.push_back(
+ QStringLiteral("%1%2").arg(m_prefix, structuredType->name()));
+ output << "#include \"" << m_prefix.toLower() << structuredType->name().toLower() << ".h\"";
+
+ QList<QString> mutualIncludes;
+ for (const auto field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &type : m_generateMapping) {
+ StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(type);
+ if (tempStructuredType) {
+ if (tempStructuredType == structuredType)
+ continue;
+ for (const auto &tempField : tempStructuredType->fields()) {
+ if (typeName == tempStructuredType->name()
+ && tempField->typeNameSecondPart() == structuredType->name()) {
+ if (!mutualIncludes.contains(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), tempStructuredType->name().toLower())))
+ mutualIncludes.push_back(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), tempStructuredType->name().toLower()));
+ }
+ }
+ }
+ }
+ }
+ if (!mutualIncludes.isEmpty()) {
+ output << Util::lineBreak();
+ std::sort(mutualIncludes.begin(), mutualIncludes.end());
+ for (const auto &include : mutualIncludes) {
+ output << include;
+ }
+ }
+ if (structuredType->recursive()) {
+ output << Util::lineBreak();
+ output << "#include <optional>";
+ output << Util::lineBreak();
+ }
+ output << Util::lineBreak(2);
+
+ writeStructuredTypeCppClassComment(structuredType, output);
+ writeStructuredTypeCppDataClass(structuredType, output);
+ writeStructuredTypeCppConstructors(structuredType, output);
+ writeStructuredTypeCppOperatorAssignment(structuredType, output);
+ writeStructuredTypeCppOperatorEquality(structuredType, output);
+ writeStructuredTypeCppQVariant(structuredType, output);
+ writeStructuredTypeCppDestructor(structuredType, output);
+ writeStructuredTypeCppGetterSetter(structuredType, output);
+ writeStructuredTypeCppDebug(structuredType, output);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppClassComment(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "\\class " << m_prefix << structuredType->name() << Util::lineBreak();
+ output << Util::indent(1) << "\\brief the OPC UA " << structuredType->name() << "."
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDataClass(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "class " << m_prefix << structuredType->name() << "Data : public QSharedData"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+
+ writeStructuredTypeCppDataClassMember(structuredType, output);
+
+ output << "};"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDataClassMember(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ if (structuredType->hasUnion()) {
+ for (const auto &possibleUnionMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion()) {
+ if (field->switchField() == possibleUnionMember->name()
+ && !unionMember.contains(possibleUnionMember))
+ unionMember.push_back(possibleUnionMember);
+ }
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+
+ QList<Field *> switchFields;
+ if (structuredType->hasSwitchfield()) {
+ for (const auto &possibleOptionalMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
+ switchFields.push_back(field);
+ }
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->isInStructuredTypeBitMask() && !switchFields.contains(field))
+ continue;
+
+ bool isEnumeration = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1)) {
+ isEnumeration = true;
+ field->setIsEnum(true);
+ }
+ }
+
+ bool isPreCoded = false;
+ if (!arrayLengthfields.contains(field)) {
+ if (!unionMember.contains(field)) {
+ output << Util::indent(1);
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (!field->needContainer()) {
+ output << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " ";
+ } else {
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> ";
+ }
+ } else {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (field->needContainer())
+ output << "QList<";
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " ";
+ break;
+ }
+ }
+ if (!isPreCoded) {
+ if (field->needContainer())
+ output << "QList<" << m_prefix << typeName << "> ";
+ else if (field->recursive())
+ output << "std::optional<" << m_prefix << typeName << "> ";
+ else if (isEnumeration)
+ output << m_prefix << "::" << typeName << " ";
+ else
+ output << m_prefix << typeName << " ";
+ }
+ }
+
+ const auto lowerCaseFieldName = field->lowerFirstName();
+ output << lowerCaseFieldName;
+ if (!field->needContainer()) {
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::byteIdentifier)
+ || field->typeName().contains(StringIdentifier::sbyteIdentifier))
+ output << " {0}";
+ else if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::bitIdentifier))
+ output << " {false}";
+ } else if (field->isEnum()) {
+ const auto enumType = std::find_if(m_enumeratedTypes.constBegin(), m_enumeratedTypes.constEnd(),
+ [field](EnumeratedType *e) { return e->name() == field->typeNameSecondPart(); });
+ const auto firstValueName = enumType != m_enumeratedTypes.constEnd() && !(*enumType)->values().empty()
+ ? (*enumType)->values().first()->name() : "Unknown";
+ if (!firstValueName.isEmpty())
+ output << " {" << m_prefix << "::" << field->typeNameSecondPart() << "::" << firstValueName << "}";
+ else
+ output << " {}";
+ } else {
+ if (field->typeName().contains(StringIdentifier::uaStatusCodeIdentifier))
+ output << " {QOpcUa::UaStatusCode::Good}";
+ }
+ }
+ output << ";"
+ << Util::lineBreak();
+ } else {
+ const auto fieldNameLowerCase = field->lowerFirstName();
+ output << Util::indent(1) << m_prefix << structuredType->name() << "::" << field->name()
+ << " " << fieldNameLowerCase << " = " << m_prefix << structuredType->name()
+ << "::" << field->name() << "::"
+ << "None";
+
+ output << ";"
+ << Util::lineBreak();
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppConstructors(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
+ << "()"
+ << Util::lineBreak();
+ output << " : data(new " << m_prefix << structuredType->name() << "Data)\n";
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Constructs a " << structuredType->name() << " from \\a rhs"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+ output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
+ << "(const " << m_prefix << structuredType->name() << " &rhs)"
+ << Util::lineBreak();
+ output << " : data(rhs.data)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppOperatorAssignment(
+ const StructuredType *structuredType, QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Sets the values from \\a rhs in this " << structuredType->name() << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+ output << m_prefix << structuredType->name() << " &" << m_prefix << structuredType->name()
+ << "::operator=(const " << m_prefix << structuredType->name() << " &rhs)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (this != &rhs)"
+ << Util::lineBreak();
+ output << Util::indent(2) << "data.operator=(rhs.data);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return *this;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppOperatorEquality(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "Returns \\c true if this " << structuredType->name()
+ << " has the same value as \\a rhs"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << "bool " << m_prefix << structuredType->name() << "::operator==(const " << m_prefix
+ << structuredType->name() << " &rhs) const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ QList<Field *> unionSwitchfield;
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleMember->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleMember))
+ arrayLengthfields.push_back(possibleMember);
+ if (possibleMember->name() == field->switchField()
+ && !unionSwitchfield.contains(possibleMember) && field->isUnion())
+ unionSwitchfield.push_back(possibleMember);
+ }
+ }
+
+ if (!unionSwitchfield.isEmpty()) {
+ const auto switchField = unionSwitchfield.first()->lowerFirstName();
+ output << Util::indent(1) << "if (data->" << switchField << " != " << " rhs.data->" << switchField << ")" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+
+ if (structuredType->containsBitMask()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!field->switchField().isEmpty() && !arrayLengthfields.contains(field)) {
+ const auto switchField = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "if (data->" << switchField << " != rhs.data->" << switchField << " || (data->" << switchField << " && ";
+ output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
+ output << "))";
+ output << Util::lineBreak() << Util::indent(2) << "return false;" << Util::lineBreak();
+ }
+ }
+ output << Util::lineBreak();
+ }
+
+ auto counterGeneratedTypes = 0;
+ if (structuredType->hasUnion()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !unionSwitchfield.contains(field) && !field->isInStructuredTypeBitMask()) {
+ if (structuredType->containsBitMask() && !field->switchField().isEmpty())
+ continue;
+
+
+ output << Util::indent(1) << "if (static_cast<qint32>(data->" << unionSwitchfield.first()->lowerFirstName() << ") == " << field->switchValue() << " && ";
+ output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
+ output << ")" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+ }
+
+ output << Util::indent(1) << "return true;" << Util::lineBreak();
+ } else {
+ output << Util::indent(1) << "return ";
+
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !unionSwitchfield.contains(field) && !field->isInStructuredTypeBitMask()) {
+ if (structuredType->containsBitMask() && !field->switchField().isEmpty())
+ continue;
+
+ const auto memberName = field->lowerFirstName();
+ if (counterGeneratedTypes != 0)
+ output << Util::lineBreak() << Util::indent(2) << " && ";
+ output << "data->" << memberName << " == rhs.data->" << memberName;
+ counterGeneratedTypes++;
+ }
+ }
+
+ output << ";" << Util::lineBreak();
+ }
+
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppQVariant(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Converts this " << structuredType->name() << " to \\l QVariant"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << m_prefix << structuredType->name() << "::operator QVariant() const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return QVariant::fromValue(*this);"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDestructor(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << m_prefix << structuredType->name() << "::~" << m_prefix << structuredType->name()
+ << "()"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppGetterSetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ writeStructuredTypeCppGetter(structuredType, output);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppGetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ if (structuredType->hasUnion()) {
+ for (const auto &possibleUnionMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion()) {
+ if (field->switchField() == possibleUnionMember->name()
+ && !unionMember.contains(possibleUnionMember))
+ unionMember.push_back(possibleUnionMember);
+ }
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+
+ QList<Field *> switchFields;
+ if (structuredType->hasSwitchfield()) {
+ for (const auto &possibleOptionalMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
+ switchFields.push_back(field);
+ }
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(field))) {
+ const auto tmpFunctionName = field->lowerFirstName();
+
+ output << "/*!\n";
+ output << Util::indent(1) << "Returns the field " << field->name() << " of this " << structuredType->name() << " object"
+ << Util::lineBreak();
+ output << "*/\n";
+
+ const auto typeName = field->typeNameSecondPart();
+ if (!unionMember.contains(field)) {
+ if (field->needContainer())
+ output << "QList<";
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName());
+ } else {
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ break;
+ }
+ }
+ if (!isPreCoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ if (isEnum)
+ output << m_prefix << "::" << field->typeName().split(":").at(1);
+ else
+ output << m_prefix << typeName;
+ }
+ }
+ if (field->needContainer())
+ output << ">";
+ output << " ";
+
+ } else {
+ output << m_prefix << structuredType->name() << "::" << field->name() << " ";
+ }
+
+ output << m_prefix << structuredType->name() << "::" << tmpFunctionName << "() const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ if (!field->isUnion()) {
+ if (field->recursive())
+ if (field->needContainer()) {
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ } else {
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ".value_or(" << m_prefix
+ << field->typeName().split(QChar::fromLatin1(':')).at(1) << "()"
+ << ");"
+ << Util::lineBreak();
+ }
+ else
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ } else {
+ const auto switchfieldToLower = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "if (data->" << switchfieldToLower << " == " << m_prefix
+ << structuredType->name() << "::" << field->switchField()
+ << "::" << field->name() << ")"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ output << Util::indent(1) << "else"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return {};" << Util::lineBreak();
+ }
+ output << "}"
+ << Util::lineBreak(2);
+ if (!unionMember.contains(field))
+ writeStructuredTypeCppSetter(field, structuredType, output);
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppSetter(const Field *field,
+ const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "Sets the field " << field->name() << " of this " << structuredType->name() << " object to \\a "
+ << field->lowerFirstName() << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << "void " << m_prefix << structuredType->name() << "::set" << field->name() << "(";
+ const auto tmpFunctionName = field->lowerFirstName();
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)) {
+ if (field->needContainer())
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << "> ";
+ else
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << " ";
+ } else {
+ output << "const ";
+ if (field->needContainer())
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << "> &";
+ else
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << " &";
+ }
+ } else {
+ const auto typeName = field->typeNameSecondPart();
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ output << "const ";
+ if (field->needContainer())
+ output << "QList<";
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " &";
+ break;
+ }
+ }
+
+ if (!isPreCoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ output << "const ";
+ if (field->needContainer())
+ output << "QList <" << m_prefix << typeName << "> &";
+ else if (isEnum)
+ output << m_prefix << "::" << field->typeName().split(":").at(1) << " &";
+ else
+ output << m_prefix << typeName << " &";
+ }
+ }
+ output << tmpFunctionName << ")"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "data->" << tmpFunctionName << " = " << tmpFunctionName << ";"
+ << Util::lineBreak();
+ if (field->isUnion()) {
+ const auto switchfieldToLower = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "data->" << switchfieldToLower << " = " << m_prefix << structuredType->name()
+ << "::" << field->switchField() << "::" << field->name() << ";"
+ << Util::lineBreak();
+ }
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeader(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "#pragma once"
+ << Util::lineBreak();
+
+ writeStructuredTypeHeaderIncludes(structuredType, output);
+
+ output << "class " << m_prefix << structuredType->name() << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+
+ writeStructuredTypeHeaderUnion(structuredType, output);
+
+ output << Util::indent(1) << m_prefix << structuredType->name() << "();"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << "(const " << m_prefix
+ << structuredType->name() << " &);"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << " &operator=(const " << m_prefix
+ << structuredType->name() << " &rhs);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool operator==(const " << m_prefix << structuredType->name() << " &rhs) const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "inline bool operator!=(const " << m_prefix << structuredType->name() << " &rhs) const"
+ << Util::lineBreak(1)
+ << Util::indent(2) << "{ return !(*this == rhs); }"
+ << Util::lineBreak();
+ output << Util::indent(1) << "operator QVariant() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "~" << m_prefix << structuredType->name() << "();"
+ << Util::lineBreak(2);
+
+ writeStructuredTypeHeaderGetterSetter(structuredType, output);
+
+ writeStructuredTypeHeaderDebug(structuredType, output);
+
+ output << "private:"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QSharedDataPointer<" << m_prefix << structuredType->name() << "Data> data;"
+ << Util::lineBreak();
+ output << "};"
+ << Util::lineBreak(2);
+ output << "Q_DECLARE_METATYPE(" << m_prefix << structuredType->name() << ")"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderIncludes(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<QString> qtCoreIncludes{"#include <QSharedData>\n",
+ "#include <QVariant>\n"};
+ QList<QString> qtOpcUaIncludes{};
+ QList<QString> qtClassIncludes;
+ QList<QString> qtPredifinedClasses;
+
+ for (const auto &field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+ QString include;
+ if (field->typeName().startsWith(QStringLiteral("%1:").arg(StringIdentifier::binaryTypeIdentifier))) {
+ if (typeName == StringIdentifier::datetimeIdentifier) {
+ include = "#include <QDateTime>\n";
+ } else if (typeName == StringIdentifier::byteStringIdentifier) {
+ include = "#include <QByteArray>\n";
+ } else if (typeName == StringIdentifier::charArrayIdentifier) {
+ include = "#include <QString>\n";
+ } else if (typeName == StringIdentifier::guidIdentifier) {
+ include = "#include <QUuid>\n";
+ }
+
+ if (!qtCoreIncludes.contains(include)) {
+ qtCoreIncludes.push_back(include);
+ }
+ } else {
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(typeName)) {
+ isPrecoded = true;
+ if (!precodedType.filename().isEmpty()) {
+ include = QStringLiteral("#include <QtOpcUa/%1>\n")
+ .arg(precodedType.filename());
+ break;
+ }
+ }
+ }
+ if (typeName != structuredType->name()) {
+ if (!isPrecoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == typeName)
+ isEnum = true;
+ }
+ if (isEnum) {
+ if (!qtClassIncludes.contains(
+ QStringLiteral("#include \"%1enumerations.h\"\n")
+ .arg(m_prefix.toLower())))
+ qtClassIncludes.push_back(
+ QStringLiteral("#include \"%1enumerations.h\"\n")
+ .arg(m_prefix.toLower()));
+
+ } else {
+ bool isMutualInclude = false;
+ for (const auto &type : m_generateMapping) {
+ StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(
+ type);
+ if (tempStructuredType) {
+ if (tempStructuredType == structuredType)
+ continue;
+ for (const auto &tempField : tempStructuredType->fields()) {
+ const auto tempTypeName = tempField->typeNameSecondPart();
+ if (typeName == tempStructuredType->name()
+ && tempTypeName == structuredType->name()) {
+ isMutualInclude = true;
+ if (!qtPredifinedClasses.contains(
+ QStringLiteral("%1%2")
+ .arg(m_prefix, tempStructuredType->name())))
+ qtPredifinedClasses.push_back(
+ QStringLiteral("%1%2")
+ .arg(m_prefix, tempStructuredType->name()));
+ }
+ }
+ }
+ }
+
+ if (!qtClassIncludes.contains(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), typeName.toLower()))
+ && !isMutualInclude
+ && typeName != StringIdentifier::xmlElementIdentifier)
+ qtClassIncludes.push_back(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), typeName.toLower()));
+ }
+ } else {
+ if (!qtOpcUaIncludes.contains(include)) {
+ qtOpcUaIncludes.push_back(include);
+ }
+ }
+ }
+ }
+ if (field->needContainer()) {
+ include = "#include <QList>\n";
+ if (!qtCoreIncludes.contains(include)) {
+ qtCoreIncludes.push_back(include);
+ }
+ }
+ }
+ if (!qtClassIncludes.isEmpty()) {
+ std::sort(qtClassIncludes.begin(), qtClassIncludes.end());
+ for (const auto &include : qtClassIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ }
+ std::sort(qtOpcUaIncludes.begin(), qtOpcUaIncludes.end());
+ for (const auto &include : qtOpcUaIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ std::sort(qtCoreIncludes.begin(), qtCoreIncludes.end());
+ for (const auto &include : qtCoreIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+
+ if (!qtPredifinedClasses.isEmpty()) {
+ std::sort(qtPredifinedClasses.begin(), qtPredifinedClasses.end());
+ for (const auto &predefinedClass : qtPredifinedClasses) {
+ output << "class " << predefinedClass << ";"
+ << Util::lineBreak();
+ }
+ }
+
+ output << "class " << m_prefix << structuredType->name() << "Data;"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderUnion(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ if (structuredType->hasUnion()) {
+ QList<Field *> unions;
+ QList<Field *> switchFields;
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion())
+ switchFields.push_back(field);
+ }
+ for (const auto &switchField : switchFields) {
+ for (const auto &structuredTypeField : structuredType->fields()) {
+ if (switchField->switchField() == structuredTypeField->name()) {
+ if (!unions.contains(structuredTypeField))
+ unions.push_back(structuredTypeField);
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+ for (const auto &field : unions) {
+ output << Util::indent(1) << "enum class " << field->name() << " {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "None = 0,\n";
+ for (int i = 0; i < switchFields.size(); i++) {
+ const int nextField = i + 1;
+ if (!arrayLengthfields.contains(switchFields.at(i))) {
+ if (nextField >= switchFields.size()) {
+ if (switchFields.at(i)->switchField() == field->name()) {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << Util::lineBreak();
+ output << Util::indent(1) << "};"
+ << Util::lineBreak(2);
+ }
+ } else {
+ if (field->name() == switchFields.at(nextField)->switchField()) {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << ","
+ << Util::lineBreak();
+ } else {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << Util::lineBreak();
+ output << Util::indent(1) << "};"
+ << Util::lineBreak(2);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderGetterSetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ QList<Field *> arrayLengthfields;
+ QList<Field *> switchFields;
+ for (const auto &possibleMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleMember->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleMember))
+ arrayLengthfields.push_back(possibleMember);
+ if (field->isUnion() && field->switchField() == possibleMember->name()
+ && !unionMember.contains(possibleMember))
+ unionMember.push_back(possibleMember);
+ if (!possibleMember->switchField().isEmpty() && field->name() == possibleMember->switchField())
+ switchFields.push_back(field);
+ }
+ }
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(field))) {
+ const auto typeName = field->typeNameSecondPart();
+ const auto tmpFunctionName = field->lowerFirstName();
+ if (!unionMember.contains(field)) {
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)) {
+ if (!field->needContainer()) {
+ output << Util::indent(1)
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "("
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ } else {
+ if (!field->needContainer()) {
+ output << Util::indent(1)
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const "
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ }
+ } else {
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (preCodedType.deEncoderName().isEmpty()) {
+ output << Util::indent(1);
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.className();
+ if (field->needContainer())
+ output << ">";
+ output << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const ";
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.className();
+ if (field->needContainer())
+ output << ">";
+ output << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1);
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const ";
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ break;
+ }
+ }
+
+ if (!isPreCoded) {
+ if (field->needContainer()) {
+ output << Util::indent(1) << "QList <" << m_prefix << typeName << "> "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const QList <"
+ << m_prefix << typeName << "> &" << tmpFunctionName
+ << ");"
+ << Util::lineBreak(2);
+ } else {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ if (isEnum) {
+ output << Util::indent(1) << m_prefix << "::" << typeName << " "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const " << m_prefix
+ << "::" << typeName << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << m_prefix << typeName << " "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const " << m_prefix
+ << typeName << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ }
+ }
+ }
+ } else { // is union switchfield, create just getter
+ output << Util::indent(1) << field->name() << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderDebug(const StructuredType *structuredType, QTextStream &output)
+{
+ output << Util::indent(1) << "friend QDebug operator<<(QDebug debug, const " << m_prefix << structuredType->name() << " &v);";
+ output << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDebug(const StructuredType *structuredType, QTextStream &output)
+{
+ const auto typeName = QStringLiteral("%1%2").arg(m_prefix, structuredType->name());
+
+ output << "/*!" << Util::lineBreak();
+ output << Util::indent(1) << "Prints the field values of object \\a v to \\a debug" << Util::lineBreak();
+ output << "*/" << Util::lineBreak();
+ output << "QDebug operator<<(QDebug debug, const " << typeName << " &v)" << Util::lineBreak();
+ output << "{" << Util::lineBreak();
+ output << Util::indent(1) << "QDebugStateSaver saver(debug);" << Util::lineBreak();
+ output << Util::indent(1) << "debug.nospace().noquote();" << Util::lineBreak(2);
+ output << Util::indent(1) << "debug << \"" << typeName << " (\";" << Util::lineBreak(2);
+
+ if (!structuredType->hasUnion() && structuredType->hasSwitchfield())
+ output << Util::indent(1) << "bool firstFieldPrinted = false;" << Util::lineBreak(2);
+
+ bool isFirst = true;
+
+ QSet<Field *> lengthFields;
+
+ for (const auto &field : structuredType->fields()) {
+ for (const auto &innerField : structuredType->fields()) {
+ if (!field->lengthField().isEmpty() && field->lengthField() == innerField->name())
+ lengthFields.insert(innerField);
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (structuredType->hasUnion() && field == structuredType->fields().constFirst())
+ continue;
+
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (lengthFields.contains(field))
+ continue;
+
+ if (structuredType->hasUnion())
+ output << Util::indent(1) << "if (static_cast<quint32>(v." << Util::lowerFirstLetter(field->switchField()) << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
+ else if (!field->switchField().isEmpty())
+ output << Util::indent(1) << "if (v." << Util::lowerFirstLetter(field->switchField()) << "()) {" << Util::lineBreak();
+
+ int indentOffset = 0;
+ if (structuredType->hasUnion() || !field->switchField().isEmpty())
+ indentOffset = 1;
+
+ if (!structuredType->hasUnion() && !isFirst) {
+ if (structuredType->hasSwitchfield())
+ output << Util::indent(1 + indentOffset) << "if (firstFieldPrinted)" << Util::lineBreak();
+ output << Util::indent(1 + (structuredType->hasSwitchfield() ? indentOffset + 1 : 0)) << "debug << \", \";" << Util::lineBreak();
+ }
+
+ output << Util::indent(1 + indentOffset) << "debug << \"" << field->name() << ": \";" << Util::lineBreak();
+
+ // Not all types have an operator<< for QDebug...
+ const auto isPrecoded = std::find_if(StringIdentifier::opcUaPrecodedTypes.constBegin(), StringIdentifier::opcUaPrecodedTypes.constEnd(),
+ [&](const auto &entry) { return entry.name() == field->typeNameSecondPart(); });
+
+ if (isPrecoded == StringIdentifier::opcUaPrecodedTypes.constEnd() || StringIdentifier::precodedTypesWithDebugOperator.contains(field->typeNameSecondPart())) {
+ output << Util::indent(1 + indentOffset) << "debug << v." << field->lowerFirstName() << "();" << Util::lineBreak();
+ } else {
+ if (field->needContainer()) {
+ const auto tempListName = QStringLiteral("%1%2").arg(field->lowerFirstName(), "Strings");
+ output << Util::indent(1 + indentOffset) << "QStringList " << tempListName << ";" << Util::lineBreak();
+ output << Util::indent(1 + indentOffset) << "for (int i = 0; i < v." << field->lowerFirstName() << "().size(); ++i)" << Util::lineBreak();
+ output << Util::indent(2 + indentOffset) << tempListName << ".push_back(\"" << isPrecoded->className() << "(...)\"" << ");" << Util::lineBreak();
+ output << Util::indent(1 + indentOffset) << "debug << \"QList(\" << " << tempListName << ".join(\", \") << \")\";";
+ } else {
+ output << Util::indent(1 + indentOffset) << "debug << \"" << isPrecoded->className() << "(...)\";";
+ }
+ output << Util::lineBreak();
+ }
+
+ if (!structuredType->hasUnion() && structuredType->hasSwitchfield() && field != structuredType->fields().constLast())
+ output << Util::indent(1 + indentOffset) << "firstFieldPrinted = true;" << Util::lineBreak();
+
+ if (structuredType->hasUnion() || !field->switchField().isEmpty())
+ output << Util::indent(1) << "}" << Util::lineBreak();
+
+ output << Util::lineBreak();
+
+ isFirst = false;
+ }
+
+ output << Util::indent(1) << "debug << \")\";" << Util::lineBreak();
+ output << Util::indent(1) << "return debug;" << Util::lineBreak();
+ output << "}" << Util::lineBreak();
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::writeEnumeratedTypes()
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1enumerations.h").arg(m_prefix.toLower());
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnableToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+ writeLicenseHeader(output);
+
+ if (!m_generatedEnumeratedTypeFilenames.contains(
+ QStringLiteral("%1enumerations").arg(m_prefix.toLower())))
+ m_generatedEnumeratedTypeFilenames.push_back(
+ QStringLiteral("%1enumerations").arg(m_prefix.toLower()));
+ output << "#pragma once"
+ << Util::lineBreak();
+ output << "#include <QMetaType>"
+ << Util::lineBreak(2);
+ output << "namespace " << m_prefix << " {"
+ << Util::lineBreak();
+ output << "Q_NAMESPACE"
+ << Util::lineBreak(2);
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ output << "enum class " << enumeratedType->name() << " {" << Util::lineBreak(1);
+ for (const auto &enumeratedValue : enumeratedType->values()) {
+ output << Util::indent(1) << enumeratedValue->name() << " = " << enumeratedValue->value();
+ if (!enumeratedType->values().endsWith(enumeratedValue))
+ output << ",";
+ output << Util::lineBreak();
+ }
+ output << "};"
+ << Util::lineBreak();
+ output << "Q_ENUM_NS(" << enumeratedType->name() << ")"
+ << Util::lineBreak(2);
+ }
+
+ output << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ return GeneratingError::NoError;
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateFile(const XmlElement *type,
+ const QString &fileExtension)
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1%2%3").arg(m_prefix.toLower(),
+ type->name().toLower(),
+ fileExtension);
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnableToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+ writeLicenseHeader(output);
+
+ auto structuredType = dynamic_cast<const StructuredType *>(type);
+ if (structuredType) {
+ if (fileExtension == StringIdentifier::headerIdentifier) {
+ writeStructuredTypeHeader(structuredType, output);
+ } else {
+ if (fileExtension == StringIdentifier::cppIdentifier) {
+ writeStructuredTypeCpp(structuredType, output);
+ }
+ }
+ }
+ file.close();
+ return NoError;
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateTypes(
+ const QList<XmlElement *> &types)
+{
+ m_generateMapping.append(types);
+ for (const auto &type : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(type);
+ if (enumeratedType)
+ if (!m_enumeratedTypes.contains(enumeratedType))
+ m_enumeratedTypes.append(enumeratedType);
+ }
+ if (writeEnumeratedTypes() != GeneratingError::NoError) {
+ return GeneratingError::UnableToWrite;
+ }
+
+ for (const auto &type : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(type);
+ if (structuredType) {
+ if (generateFile(type, StringIdentifier::headerIdentifier) != GeneratingError::NoError)
+ return GeneratingError::UnableToWrite;
+ if (generateFile(type, StringIdentifier::cppIdentifier) != GeneratingError::NoError)
+ return GeneratingError::UnableToWrite;
+ }
+ }
+ return NoError;
+}
+
+QList<XmlElement *> DataTypeFileWriter::generateMapping() const
+{
+ return m_generateMapping;
+}
+
+void DataTypeFileWriter::setGenerateMapping(const QList<XmlElement *> &generateMapping)
+{
+ m_generateMapping = generateMapping;
+}
diff --git a/tools/datatypecodegenerator/datatypefilewriter.h b/tools/datatypecodegenerator/datatypefilewriter.h
new file mode 100644
index 0000000..aca22a7
--- /dev/null
+++ b/tools/datatypecodegenerator/datatypefilewriter.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "visitor.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qtextstream.h>
+
+class DataTypeFileWriter : public Visitor
+{
+public:
+ enum GeneratingError { NoError, UnableToWrite };
+ DataTypeFileWriter(const QString &path,
+ const QString &prefix,
+ const QString &header);
+ ~DataTypeFileWriter() override = default;
+
+ void visit(EnumeratedType *enumeratedType) override;
+ void visit(EnumeratedValue *enumeratedValue) override;
+ void visit(Field *field) override;
+ void visit(Import *import) override;
+ void visit(StructuredType *structuredType) override;
+ void visit(TypeDictionary *typeDictionary) override;
+ void visit(XmlElement *xmlElement) override;
+
+ GeneratingError generateFile(const XmlElement *type, const QString &fileExtension);
+ GeneratingError generateTypes(const QList<XmlElement *> &types);
+
+ QList<XmlElement *> generateMapping() const;
+ void setGenerateMapping(const QList<XmlElement *> &generateMapping);
+
+private:
+ QString m_path;
+ QString m_prefix;
+ QString m_header;
+ QList<QString> m_generatedEnumeratedTypeFilenames;
+ QList<QString> m_generatedStructuredTypeFilenames;
+ QList<XmlElement *> m_generateMapping;
+ QList<EnumeratedType *> m_enumeratedTypes;
+
+ void writeLicenseHeader(QTextStream &output);
+
+ void writeStructuredTypeCpp(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppClassComment(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppDataClass(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppDataClassMember(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppConstructors(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppOperatorAssignment(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppOperatorEquality(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppQVariant(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppDestructor(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppGetterSetter(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppGetter(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppSetter(const Field *field,
+ const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeStructuredTypeHeader(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeHeaderIncludes(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeHeaderUnion(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeHeaderGetterSetter(const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeStructuredTypeHeaderDebug(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppDebug(const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeEnumeratedType(const EnumeratedType *enumeratedType, QTextStream &output);
+ GeneratingError writeEnumeratedTypes();
+};
diff --git a/tools/datatypecodegenerator/dependencydatatypevalidator.cpp b/tools/datatypecodegenerator/dependencydatatypevalidator.cpp
new file mode 100644
index 0000000..243fd14
--- /dev/null
+++ b/tools/datatypecodegenerator/dependencydatatypevalidator.cpp
@@ -0,0 +1,107 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "dependencydatatypevalidator.h"
+#include "enumeratedtype.h"
+#include "field.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+DependencyDataTypeValidator::DependencyDataTypeValidator()
+ : m_readResolveDependencies(ReadDependencies)
+{}
+
+void DependencyDataTypeValidator::visit(EnumeratedType *enumeratedType)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ResolveDependencies) {
+ if (m_unresolvedDependencyStringList.contains(enumeratedType->name())) {
+ m_unresolvedDependencyStringList.removeAll(enumeratedType->name());
+ m_resolvedDependencyElementList.push_back(enumeratedType);
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(EnumeratedValue *enumeratedValue)
+{
+ Q_UNUSED(enumeratedValue);
+}
+
+void DependencyDataTypeValidator::visit(Field *field)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ReadDependencies) {
+ if (!field->typeName().contains("opc:")) {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &precoded : StringIdentifier::opcUaPrecodedTypes) {
+ if (precoded.contains(typeName)) {
+ return;
+ }
+ }
+ m_unresolvedDependencyStringList.push_back(typeName);
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(Import *import)
+{
+ Q_UNUSED(import);
+}
+
+void DependencyDataTypeValidator::visit(StructuredType *structuredType)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ResolveDependencies) {
+ if (m_unresolvedDependencyStringList.contains(structuredType->name())) {
+ m_unresolvedDependencyStringList.removeAll(structuredType->name());
+ m_resolvedDependencyElementList.push_back(structuredType);
+ for (const auto &field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+
+ if (!StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ bool isPrecoded = false;
+ for (const auto &precoded : StringIdentifier::opcUaPrecodedTypes) {
+ if (precoded.contains(typeName)) {
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded && !m_unresolvedDependencyStringList.contains(typeName)) {
+ bool isResolved = false;
+ for (const auto &type : m_resolvedDependencyElementList) {
+ if (type->name() == typeName) {
+ isResolved = true;
+ break;
+ }
+ }
+ if (!isResolved)
+ m_unresolvedDependencyStringList.push_back(typeName);
+ }
+ }
+ }
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(TypeDictionary *typeDictionary)
+{
+ Q_UNUSED(typeDictionary);
+}
+
+void DependencyDataTypeValidator::visit(XmlElement *xmlElement)
+{
+ Q_UNUSED(xmlElement);
+}
+
+QStringList DependencyDataTypeValidator::unresolvedDependencyStringList() const
+{
+ return m_unresolvedDependencyStringList;
+}
+
+QList<XmlElement *> DependencyDataTypeValidator::resolvedDependencyElementList() const
+{
+ return m_resolvedDependencyElementList;
+}
+
+void DependencyDataTypeValidator::setReadResolveDependencies(
+ const ReadResolveDependencies &readResolveDependencies)
+{
+ m_readResolveDependencies = readResolveDependencies;
+}
diff --git a/tools/datatypecodegenerator/dependencydatatypevalidator.h b/tools/datatypecodegenerator/dependencydatatypevalidator.h
new file mode 100644
index 0000000..aad3e4e
--- /dev/null
+++ b/tools/datatypecodegenerator/dependencydatatypevalidator.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "visitor.h"
+
+#include <QtCore/qlist.h>
+
+class XmlElement;
+
+class DependencyDataTypeValidator : public Visitor
+{
+public:
+ enum ReadResolveDependencies { ReadDependencies, ResolveDependencies };
+
+ DependencyDataTypeValidator();
+ ~DependencyDataTypeValidator() override = default;
+
+ void visit(EnumeratedType *enumeratedType) override;
+ void visit(EnumeratedValue *enumeratedValue) override;
+ void visit(Field *field) override;
+ void visit(Import *import) override;
+ void visit(StructuredType *structuredType) override;
+ void visit(TypeDictionary *typeDictionary) override;
+ void visit(XmlElement *xmlElement) override;
+
+ QStringList unresolvedDependencyStringList() const;
+
+ QList<XmlElement *> resolvedDependencyElementList() const;
+
+ void setReadResolveDependencies(const ReadResolveDependencies &readResolveDependencies);
+
+private:
+ ReadResolveDependencies m_readResolveDependencies;
+ QList<XmlElement *> m_resolvedDependencyElementList;
+ QStringList m_unresolvedDependencyStringList;
+};
diff --git a/tools/datatypecodegenerator/enumeratedtype.cpp b/tools/datatypecodegenerator/enumeratedtype.cpp
new file mode 100644
index 0000000..b31ed43
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedtype.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+EnumeratedType::EnumeratedType(const QString &name, quint32 lengthInBits)
+ : XmlElement(name)
+ , m_lengthInBits(lengthInBits)
+{}
+
+EnumeratedType::~EnumeratedType()
+{
+ qDeleteAll(m_values);
+}
+
+quint32 EnumeratedType::lengthInBits() const
+{
+ return m_lengthInBits;
+}
+
+void EnumeratedType::setLengthInBits(quint32 lengthInBits)
+{
+ m_lengthInBits = lengthInBits;
+}
+
+void EnumeratedType::addValue(EnumeratedValue *enumeratedValue)
+{
+ m_values.push_back(enumeratedValue);
+}
+
+void EnumeratedType::print() const
+{
+ XmlElement::print();
+ qDebug() << "LengthInBits: " << m_lengthInBits;
+ for (int i = 0; i < m_values.size(); i++)
+ m_values.at(i)->print();
+}
+
+void EnumeratedType::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (auto &enumeratedValues : m_values)
+ enumeratedValues->accept(visitor);
+}
+
+QList<EnumeratedValue *> EnumeratedType::values() const
+{
+ return m_values;
+}
+
+void EnumeratedType::setValues(const QList<EnumeratedValue *> &values)
+{
+ m_values = values;
+}
diff --git a/tools/datatypecodegenerator/enumeratedtype.h b/tools/datatypecodegenerator/enumeratedtype.h
new file mode 100644
index 0000000..7e212e6
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedtype.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+#include <QtCore/qlist.h>
+
+class EnumeratedValue;
+class Visitor;
+
+class EnumeratedType : public XmlElement
+
+{
+public:
+ EnumeratedType(const QString &name, quint32 lengthInBits);
+ ~EnumeratedType();
+
+ void addValue(EnumeratedValue *enumeratedValue);
+ void print() const override;
+
+ quint32 lengthInBits() const;
+ void setLengthInBits(quint32 lengthInBits);
+
+ QList<EnumeratedValue *> values() const;
+ void setValues(const QList<EnumeratedValue *> &values);
+
+ virtual void accept(Visitor *visitor) override;
+
+private:
+ quint32 m_lengthInBits;
+ QList<EnumeratedValue *> m_values;
+};
diff --git a/tools/datatypecodegenerator/enumeratedvalue.cpp b/tools/datatypecodegenerator/enumeratedvalue.cpp
new file mode 100644
index 0000000..f28769c
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedvalue.cpp
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "enumeratedvalue.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+EnumeratedValue::EnumeratedValue(const QString &name, qint32 value)
+ : XmlElement(name)
+ , m_value(value)
+{}
+
+void EnumeratedValue::print() const
+{
+ XmlElement::print();
+ qDebug() << "Value: " << m_value;
+}
+
+void EnumeratedValue::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
+
+qint32 EnumeratedValue::value() const
+{
+ return m_value;
+}
+
+void EnumeratedValue::setValue(qint32 value)
+{
+ m_value = value;
+}
diff --git a/tools/datatypecodegenerator/enumeratedvalue.h b/tools/datatypecodegenerator/enumeratedvalue.h
new file mode 100644
index 0000000..fc1d85c
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedvalue.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Visitor;
+
+class EnumeratedValue : public XmlElement
+{
+public:
+ EnumeratedValue(const QString &name, qint32 value);
+ ~EnumeratedValue() override = default;
+
+ void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ qint32 value() const;
+ void setValue(qint32 value);
+
+private:
+ qint32 m_value;
+};
diff --git a/tools/datatypecodegenerator/field.cpp b/tools/datatypecodegenerator/field.cpp
new file mode 100644
index 0000000..97499a9
--- /dev/null
+++ b/tools/datatypecodegenerator/field.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "field.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qstringlist.h>
+
+Field::Field(const QString &name, const QString &typeName, const QString &lengthField)
+ : XmlElement(name)
+ , m_lengthField(lengthField)
+ , m_typeName(typeName)
+{}
+
+QString Field::typeName() const
+{
+ return m_typeName;
+}
+
+QString Field::typeNameSecondPart() const
+{
+ return m_typeName.split(':').value(1, QString());
+}
+
+void Field::setTypeName(const QString &typeName)
+{
+ m_typeName = typeName;
+}
+
+QString Field::lengthField() const
+{
+ return m_lengthField;
+}
+
+void Field::setLengthField(const QString &lengthField)
+{
+ m_lengthField = lengthField;
+}
+
+bool Field::hasLengthField() const
+{
+ return m_hasLengthField;
+}
+
+void Field::setHasLengthField(bool hasLengthField)
+{
+ m_hasLengthField = hasLengthField;
+}
+
+bool Field::needContainer() const
+{
+ return m_needContainer;
+}
+
+void Field::setNeedContainer(bool needContainer)
+{
+ m_needContainer = needContainer;
+}
+
+bool Field::isInStructuredTypeBitMask() const
+{
+ return m_isInStructuredTypeBitMask;
+}
+
+void Field::setIsInStructuredTypeBitMask(bool isInStructuredTypeBitMask)
+{
+ m_isInStructuredTypeBitMask = isInStructuredTypeBitMask;
+}
+
+int Field::length() const
+{
+ return m_length;
+}
+
+void Field::setLength(int length)
+{
+ m_length = length;
+}
+
+QString Field::switchField() const
+{
+ return m_switchField;
+}
+
+void Field::setSwitchField(const QString &switchField)
+{
+ m_switchField = switchField;
+}
+
+int Field::switchValue() const
+{
+ return m_switchValue;
+}
+
+void Field::setSwitchValue(int switchValue)
+{
+ m_switchValue = switchValue;
+}
+
+bool Field::isUnion() const
+{
+ return m_isUnion;
+}
+
+void Field::setIsUnion(bool isUnion)
+{
+ m_isUnion = isUnion;
+}
+
+bool Field::recursive() const
+{
+ return m_recursive;
+}
+
+void Field::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+bool Field::isEnum() const
+{
+ return m_isEnum;
+}
+
+void Field::setIsEnum(bool newIsEnum)
+{
+ m_isEnum = newIsEnum;
+}
+
+void Field::print() const
+{
+ XmlElement::print();
+ qDebug() << "Type name: " << m_typeName;
+ if (!m_lengthField.isEmpty())
+ qDebug() << "Length field: " << m_lengthField;
+}
+
+void Field::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
diff --git a/tools/datatypecodegenerator/field.h b/tools/datatypecodegenerator/field.h
new file mode 100644
index 0000000..0e36bcf
--- /dev/null
+++ b/tools/datatypecodegenerator/field.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Field : public XmlElement
+{
+public:
+ Field(const QString &name, const QString &typeName, const QString &lengthField = QString());
+ ~Field() override = default;
+
+ void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ QString typeName() const;
+ QString typeNameSecondPart() const;
+ void setTypeName(const QString &typeName);
+
+ QString lengthField() const;
+ void setLengthField(const QString &lengthField);
+
+ bool hasLengthField() const;
+ void setHasLengthField(bool hasLengthField);
+
+ bool needContainer() const;
+ void setNeedContainer(bool needContainer);
+
+ bool isInStructuredTypeBitMask() const;
+ void setIsInStructuredTypeBitMask(bool isInStructuredTypeBitMask);
+
+ int length() const;
+ void setLength(int length);
+
+ QString switchField() const;
+ void setSwitchField(const QString &switchField);
+
+ int switchValue() const;
+ void setSwitchValue(int switchValue);
+
+ bool isUnion() const;
+ void setIsUnion(bool isUnion);
+
+ bool recursive() const;
+ void setRecursive(bool recursive);
+
+ bool isEnum() const;
+ void setIsEnum(bool newIsEnum);
+private:
+ bool m_hasLengthField{false};
+ bool m_needContainer{false};
+ bool m_isInStructuredTypeBitMask{false};
+ bool m_isUnion{false};
+ int m_length;
+ int m_switchValue{0};
+ bool m_recursive{false};
+ bool m_isEnum{false};
+ QString m_lengthField;
+ QString m_switchField;
+ QString m_typeName;
+};
diff --git a/tools/datatypecodegenerator/import.cpp b/tools/datatypecodegenerator/import.cpp
new file mode 100644
index 0000000..7702430
--- /dev/null
+++ b/tools/datatypecodegenerator/import.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "import.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+Import::Import(const QString &nameSpace, const QString &location)
+ : m_location(location)
+ , m_nameSpace(nameSpace)
+{}
+
+QString Import::nameSpace() const
+{
+ return m_nameSpace;
+}
+
+void Import::setNameSpace(const QString &nameSpace)
+{
+ m_nameSpace = nameSpace;
+}
+
+QString Import::location() const
+{
+ return m_location;
+}
+
+void Import::setLocation(const QString &location)
+{
+ m_location = location;
+}
+
+void Import::print() const
+{
+ qDebug() << "Namespace:" << m_nameSpace << "\tLocation:" << m_location;
+}
+
+void Import::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
diff --git a/tools/datatypecodegenerator/import.h b/tools/datatypecodegenerator/import.h
new file mode 100644
index 0000000..9343904
--- /dev/null
+++ b/tools/datatypecodegenerator/import.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Visitor;
+
+class Import : public XmlElement
+{
+public:
+ Import(const QString &nameSpace, const QString &location);
+ ~Import() override = default;
+
+ virtual void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ QString nameSpace() const;
+ void setNameSpace(const QString &nameSpace);
+
+ QString location() const;
+ void setLocation(const QString &location);
+
+private:
+ QString m_location;
+ QString m_nameSpace;
+};
diff --git a/tools/datatypecodegenerator/main.cpp b/tools/datatypecodegenerator/main.cpp
new file mode 100644
index 0000000..20ab28a
--- /dev/null
+++ b/tools/datatypecodegenerator/main.cpp
@@ -0,0 +1,181 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "recursivedescentparser.h"
+
+#include <QtCore/qcommandlineoption.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qxmlstream.h>
+
+#include <cstdlib>
+
+bool readBsdFile(RecursiveDescentParser &recursiveDescentParser,
+ const QString &fileName,
+ bool dependencyInput)
+{
+ switch (recursiveDescentParser.parseFile(fileName, dependencyInput)) {
+ case RecursiveDescentParser::NoError:
+ return true;
+ case RecursiveDescentParser::InvalidFileName:
+ qCritical() << "Error: File does not exist:" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidTypeDictionaryEntry:
+ qCritical() << "Error: Invalid TypeDictionary entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidStructuredTypeEntry:
+ qCritical() << "Error: Invalid StructuredType entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidEnumeratedTypeEntry:
+ qCritical() << "Error: Invalid EnumeratedType entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidImportEntry:
+ qCritical() << "Error: Invalid Import entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidFieldEntry:
+ qCritical() << "Error: Invalid Field entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidEnumeratedValueEntry:
+ qCritical() << "Error: Invalid EnumeratedValue entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::CannotFullyGenerateNamespaceZero:
+ qCritical() << "Error: Full generation of namespace 0 is currently not "
+ "supported";
+ return false;
+ case RecursiveDescentParser::MissingDependency:
+ qCritical() << "Error: Missing dependency Type found in" << fileName;
+ return false;
+ default:
+ qCritical() << "Error: Unknown parsing error occurred";
+ return false;
+ }
+}
+
+bool generateBsdFiles(RecursiveDescentParser &recursiveDescentParser,
+ const QString &outputPath,
+ const QString &dataPrefix,
+ const QString &outputFileHeader)
+{
+ switch (recursiveDescentParser.generateInputFiles(outputPath,
+ dataPrefix,
+ outputFileHeader)) {
+ case RecursiveDescentParser::NoError:
+ return true;
+ case RecursiveDescentParser::UnableToWriteFile:
+ qCritical() << "Error: Unable to write files at specified path.";
+ return false;
+ case RecursiveDescentParser::MissingDependency:
+ qCritical() << "Error: Unresolved dependent type occurred.";
+ return false;
+ case RecursiveDescentParser::UnableToResolveDependency:
+ qCritical() << "Error: Unresolvable mapping occurred.";
+ default:
+ qCritical() << "Error: Unknown file generating error occurred.";
+ return false;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ const QString appName = QStringLiteral("qopcuaxmldatatypes2cpp");
+ const QString appVersion = QStringLiteral("1.0");
+
+ auto arguments = a.arguments();
+ arguments.replace(0, appName);
+
+ const auto outputFileHeader = QStringLiteral("/*\n"
+ " * This file was generated by %1 version %2\n"
+ " * Command line used: %3\n"
+ " */")
+ .arg(appName,
+ appVersion,
+ arguments.join(QLatin1Char(' ')));
+
+ QCoreApplication::setApplicationName(appName);
+ QCoreApplication::setApplicationVersion(appVersion);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(
+ "Code generator for custom data models.\n"
+ "Converts OPC UA .bsd files into enums and C++ data classes and generates a class "
+ "to decode and encode the values from/to a QOpcUaExtensionObject with binary body or a QByteArray.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ const QCommandLineOption inputFileOption(QStringList() << "i"
+ << "input",
+ "A primary input file. Will generate code for all contained types and "
+ "check for missing dependencies",
+ "file");
+ parser.addOption(inputFileOption);
+ const QCommandLineOption inputDependencyFileOption(
+ QStringList() << "d"
+ << "dependencyinput",
+ "A dependency input file. Only types required by primary input files will be generated",
+ "file");
+ parser.addOption(inputDependencyFileOption);
+ const QCommandLineOption outputDirectoryPathOption(QStringList() << "o"
+ << "output",
+ "output directory for the generated C++ files.",
+ "path");
+ parser.addOption(outputDirectoryPathOption);
+ const QCommandLineOption
+ outputPrefixOption(QStringList() << "p"
+ << "prefix",
+ "prefix for the generated files, default is GeneratedOpcUa",
+ "prefix",
+ "GeneratedOpcUa");
+ parser.addOption(outputPrefixOption);
+
+ parser.process(a);
+
+ if (!parser.isSet(inputFileOption)) {
+ qCritical() << "Error: At least one input file must be specified";
+ parser.showHelp(1);
+ return EXIT_FAILURE;
+ }
+
+ if (!parser.isSet(outputDirectoryPathOption)) {
+ qCritical() << "Error: The output path must be specified.";
+ parser.showHelp(1);
+ return EXIT_FAILURE;
+ }
+
+ if (parser.values(outputPrefixOption).size() > 1)
+ qInfo() << "Info: The first output prefix will be used";
+ if (!parser.values(outputPrefixOption)
+ .at(0)
+ .contains(QRegularExpression("^[A-Za-z]+[A-Za-z0-9]*$"))) {
+ qCritical() << "Error: The prefix contains illegal characters";
+ qInfo() << "Info: The prefix must consist of letters and numbers and start with a letter";
+ return EXIT_FAILURE;
+ }
+
+ const auto dataPrefix = parser.value(outputPrefixOption);
+
+ auto success = true;
+ RecursiveDescentParser recursiveDescentParser;
+ const QStringList inputFileNames = parser.values(inputFileOption);
+ for (const auto &fileName : inputFileNames)
+ success &= readBsdFile(recursiveDescentParser, fileName, false);
+ const QStringList dependencyInputFileNames = parser.values(inputDependencyFileOption);
+ for (const auto &fileName : dependencyInputFileNames)
+ success &= readBsdFile(recursiveDescentParser, fileName, true);
+ if (success) {
+ const auto outputPath = parser.value(outputDirectoryPathOption);
+ if (generateBsdFiles(recursiveDescentParser,
+ outputPath,
+ dataPrefix,
+ outputFileHeader)) {
+ qInfo() << "Info: All types were successfully generated";
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
diff --git a/tools/datatypecodegenerator/mappingfilegenerator.cpp b/tools/datatypecodegenerator/mappingfilegenerator.cpp
new file mode 100644
index 0000000..de9e2d7
--- /dev/null
+++ b/tools/datatypecodegenerator/mappingfilegenerator.cpp
@@ -0,0 +1,636 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "enumeratedtype.h"
+#include "field.h"
+#include "mappingfilegenerator.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+
+MappingFileGenerator::MappingFileGenerator(const QList<XmlElement *> &generateMapping,
+ const QString &path,
+ const QString &prefix,
+ const QString &header)
+ : m_generateMapping(generateMapping)
+ , m_path(path)
+ , m_prefix(prefix)
+ , m_header(header)
+{
+ for (const auto &type : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(type);
+ if (structuredType) {
+ for (const auto &possibleField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleField->name() == field->lengthField()
+ && !m_lengthFields.contains(possibleField))
+ m_lengthFields.push_back(possibleField);
+
+ if (!m_switchFields.contains(possibleField)
+ && possibleField->name() == field->switchField())
+ m_switchFields.push_back(possibleField);
+ }
+ }
+ }
+ }
+}
+
+void MappingFileGenerator::addGenerateMapping(
+ const QList<XmlElement *> &generateMapping)
+{
+ for (const auto &mapping : generateMapping) {
+ if (!m_generateMapping.contains(mapping))
+ m_generateMapping.push_back(mapping);
+ }
+ for (const auto &mapping : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ for (const auto &possibleField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleField->name() == field->lengthField()
+ && !m_lengthFields.contains(possibleField))
+ m_lengthFields.push_back(possibleField);
+
+ if (!m_switchFields.contains(possibleField)
+ && possibleField->name() == field->switchField())
+ m_switchFields.push_back(possibleField);
+ }
+ }
+ }
+ }
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMapping()
+{
+ auto error = sortMappings();
+ if (error != NoError)
+ return error;
+
+ if (generateMappingHeader() == NoError)
+ error = generateMappingCpp();
+ else
+ error = UnanbleToWrite;
+ return error;
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMappingHeader()
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1binarydeencoder%2")
+ .arg(m_prefix.toLower(), StringIdentifier::headerIdentifier);
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnanbleToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+
+ // Print header (if present)
+ if (!m_header.isEmpty())
+ output << m_header << Util::lineBreak(2);
+ output << "#pragma once"
+ << Util::lineBreak(2);
+ QList<QString> includes;
+ for (auto mapping : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
+ if (enumeratedType) {
+ if (!includes.contains(
+ QLatin1String("#include \"%1enumerations.h\"%2").arg(m_prefix.toLower(), Util::lineBreak())))
+ includes.push_back(
+ QLatin1String("#include \"%1enumerations.h\"%2").arg(m_prefix.toLower(), Util::lineBreak()));
+ } else {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType)
+ if (!includes.contains(
+ QLatin1String("#include \"%1%2.h\"%3")
+ .arg(m_prefix.toLower(), structuredType->name().toLower(), Util::lineBreak())))
+ includes.push_back(
+ QLatin1String("#include \"%1%2.h\"%3")
+ .arg(m_prefix.toLower(), structuredType->name().toLower(), Util::lineBreak()));
+ }
+ }
+
+ includes.sort();
+ for (const auto &include : includes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ output << "#include <QtOpcUa/QOpcUaBinaryDataEncoding>"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+
+ output << "class " << m_prefix << "BinaryDeEncoder"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << "BinaryDeEncoder(QByteArray *buffer);"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << "BinaryDeEncoder(QOpcUaExtensionObject &object);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "T decode(bool &success);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QList<T> decodeArray(bool &success);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool encode(const T &src);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool encodeArray(const QList<T> &src);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "QOpcUaBinaryDataEncoding &binaryDataEncoding();"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+
+ output << "private:"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QScopedPointer<QOpcUaBinaryDataEncoding> m_binaryDataEncoding;"
+ << Util::lineBreak();
+
+ output << "};"
+ << "\n\n";
+ generateDeEncodingArray(output);
+ generateDeEncoding(output);
+ return NoError;
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMappingCpp()
+{
+ QFile file;
+ QDir dir(m_path);
+ const auto fileName = QStringLiteral("%1binarydeencoder%2")
+ .arg(m_prefix.toLower(), StringIdentifier::cppIdentifier);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnanbleToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+
+ // Print header (if present)
+ if (!m_header.isEmpty())
+ output << m_header << "\n\n";
+
+ output << "#include \"" << m_prefix.toLower() << "binarydeencoder.h\"" << Util::lineBreak();
+ output << Util::lineBreak();
+ output << m_prefix << "BinaryDeEncoder::" << m_prefix << "BinaryDeEncoder (QByteArray *buffer)"
+ << Util::lineBreak();
+ output << Util::indent(1) << ": m_binaryDataEncoding(new QOpcUaBinaryDataEncoding(buffer))"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+ output << m_prefix << "BinaryDeEncoder::" << m_prefix
+ << "BinaryDeEncoder "
+ "(QOpcUaExtensionObject &object) "
+ << Util::lineBreak();
+ output << Util::indent(1) << ": m_binaryDataEncoding(new "
+ "QOpcUaBinaryDataEncoding(&object.encodedBodyRef()))"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ output << "QOpcUaBinaryDataEncoding &" << m_prefix << "BinaryDeEncoder::binaryDataEncoding()"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return *m_binaryDataEncoding.data();"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ return NoError;
+}
+
+void MappingFileGenerator::generateFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "temp.set" << field->name() << "(";
+ generateScalarOrArrayDecoding(output, structuredType, field);
+ output << ");" << Util::lineBreak();
+ output << Util::indent(level) << "if (!success)" << Util::lineBreak();
+ output << Util::indent(level + 1) << "return {};" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateScalarOrArrayDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
+{
+ Q_UNUSED(structuredType);
+ const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(field->typeName());
+ if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
+ output << "m_binaryDataEncoding->decode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(success)";
+ return;
+ }
+
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ output << "m_binaryDataEncoding->decode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<";
+ if (!precodedType.deEncoderName().isEmpty())
+ output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
+ else
+ output << precodedType.className();
+ output << ">(success)";
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded) {
+ output << "decode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(field->typeName()) << ">(success)";
+ }
+}
+
+void MappingFileGenerator::generateOptionalFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ int index = -1;
+ QSet<QString> usedSwitchFields;
+ for (const auto &current : structuredType->fields()) {
+ if (!current->switchField().isEmpty() && !usedSwitchFields.contains(current->switchField())) {
+ index++;
+ usedSwitchFields.insert(current->switchField());
+ }
+
+ if (current == field)
+ break;
+ }
+
+ output << Util::indent(level) << "if (decodingMask & " << QStringLiteral("(1 << %1)").arg(index) << ") {" << Util::lineBreak();
+ generateFieldDecoding(output, structuredType, field, level + 1);
+ output << Util::indent(level + 1) << "temp.set" << field->switchField() << "(true);" << Util::lineBreak();
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateUnionFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << QStringLiteral("if (switchField == %1) {").arg(field->switchValue()) << Util::lineBreak();
+ generateFieldDecoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (!";
+ generateScalarOrArrayEncoding(output, structuredType, field);
+ output << ")" << Util::lineBreak();
+ output << Util::indent(level + 1) << "return false;" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateScalarOrArrayEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
+{
+ Q_UNUSED(structuredType);
+ const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(field->typeName());
+ if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
+ output << "m_binaryDataEncoding->encode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(src." << field->lowerFirstName() << "())";
+ return;
+ }
+
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ output << "m_binaryDataEncoding->encode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<";
+ if (!precodedType.deEncoderName().isEmpty())
+ output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
+ else
+ output << precodedType.className();
+ output << ">(src." << field->lowerFirstName() << "())";
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded) {
+ output << "encode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(field->typeName()) << ">(src." << field->lowerFirstName() << "())";
+ }
+}
+
+void MappingFileGenerator::generateOptionalFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (src." << Util::lowerFirstLetter(field->switchField()) << "()) {" << Util::lineBreak();
+ generateFieldEncoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateUnionFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
+ generateFieldEncoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateDeEncodingArray(QTextStream &output)
+{
+ output << "template<typename T>"
+ << Util::lineBreak();
+ output << "inline QList<T> " << m_prefix << "BinaryDeEncoder::decodeArray(bool &success)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QList<T> temp;"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "qint32 size = m_binaryDataEncoding->decode<qint32>(success);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (!success)"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return {};";
+ output << Util::lineBreak(2);
+ output << Util::indent(1) << "for (int i = 0; i < size; ++i) {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "temp.push_back(decode<T>(success));"
+ << Util::lineBreak();
+ output << Util::indent(2) << "if (!success)"
+ << Util::lineBreak();
+ output << Util::indent(3) << "return {};"
+ << Util::lineBreak();
+ output << Util::indent(1) << "}"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "return temp;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+ output << "template<typename T>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder::encodeArray(const QList<T> &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (src.size() > (std::numeric_limits<qint32>::max()))"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return false;"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<qint32>(src.size()))"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return false;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "for (const auto &element : src) {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "if (!encode<T>(element))"
+ << Util::lineBreak();
+ output << Util::indent(3) << "return false;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "}"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return true;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateDeEncoding(QTextStream &output)
+{
+ for (const auto &mapping : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
+ if (enumeratedType) {
+ generateDecodingEnumeratedType(output, enumeratedType);
+ generateEncodingEnumeratedType(output, enumeratedType);
+ } else {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ generateDecodingStructuredType(output, structuredType);
+ generateEncodingStructuredType(output, structuredType);
+ }
+ }
+ }
+}
+
+void MappingFileGenerator::generateDecodingEnumeratedType(QTextStream &output,
+ const EnumeratedType *enumeratedType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline " << m_prefix << "::" << enumeratedType->name() << " " << m_prefix
+ << "BinaryDeEncoder::decode(bool &success)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return static_cast<" << m_prefix << "::" << enumeratedType->name() << ">("
+ "m_binaryDataEncoding->decode<qint32>(success));"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateEncodingEnumeratedType(QTextStream &output,
+ const EnumeratedType *enumeratedType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder"
+ << "::encode<" << m_prefix << "::" << enumeratedType->name() << ">(const " << m_prefix
+ << "::" << enumeratedType->name() << " &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return m_binaryDataEncoding->encode<qint32>(static_cast<qint32>(src));"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateDecodingStructuredType(QTextStream &output,
+ const StructuredType *structuredType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline " << m_prefix << structuredType->name() << " " << m_prefix
+ << "BinaryDeEncoder::decode<" << m_prefix << structuredType->name() << ">(bool &success)" << Util::lineBreak()
+ << "{" << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << " temp;"
+ << Util::lineBreak(2);
+
+ if (structuredType->containsBitMask()) {
+ output << Util::indent(1) <<"const auto decodingMask = "
+ << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
+ << Util::indent(1) << "if (!success)" << Util::lineBreak()
+ << Util::indent(2) << "return {};" << Util::lineBreak(2);
+ } else if (structuredType->hasUnion()) {
+ output << Util::indent(1) <<"const auto switchField = "
+ << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
+ << Util::indent(1) << "if (!success)" << Util::lineBreak()
+ << Util::indent(2) << "return {};" << Util::lineBreak(2);
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (m_lengthFields.contains(field))
+ continue;
+
+ // The switch field is not a member of the generated data class
+ if (structuredType->hasUnion() && field == structuredType->fields().constFirst())
+ continue;
+
+ if (!field->switchField().isEmpty() && !field->isUnion())
+ generateOptionalFieldDecoding(output, structuredType, field);
+ else if (structuredType->hasUnion())
+ generateUnionFieldDecoding(output, structuredType, field);
+ else
+ generateFieldDecoding(output, structuredType, field);
+
+ output << Util::lineBreak();
+ }
+
+ output << Util::indent(1) << "return temp;" << Util::lineBreak();
+ output << "}" << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateEncodingStructuredType(QTextStream &output,
+ const StructuredType *structuredType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder"
+ << "::encode<" << m_prefix << structuredType->name() << ">(const " << m_prefix
+ << structuredType->name() << " &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+
+ // Create encoding mask for bitmask
+ if (structuredType->hasSwitchfield() && !structuredType->hasUnion()) {
+ output << Util::indent(1) << "quint32 encodingMask = 0;" << Util::lineBreak();
+ int index = 0;
+ for (const auto &field : structuredType->fields()) {
+ if (!m_switchFields.contains(field))
+ continue;
+ output << Util::indent(1) << "encodingMask |= ((src." << field->lowerFirstName() << "() ? 1 : 0) << " << index++ << ");" << Util::lineBreak();
+ }
+ output << Util::lineBreak();
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<quint32>(encodingMask))" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ } else if (structuredType->hasUnion()) {
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<quint32>(static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "())))" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (m_lengthFields.contains(field))
+ continue;
+
+ if (m_switchFields.contains(field))
+ continue;
+
+ // Ignore reserved fields
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (structuredType->hasUnion())
+ generateUnionFieldEncoding(output, structuredType, field);
+ else if (!field->switchField().isEmpty() && !field->isUnion())
+ generateOptionalFieldEncoding(output, structuredType, field);
+ else
+ generateFieldEncoding(output, structuredType, field);
+
+ output << Util::lineBreak();
+ }
+
+ output << Util::indent(1) << "return true;" << Util::lineBreak();
+ output << "}" << Util::lineBreak(2);
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::sortMappings()
+{
+ QList<XmlElement *> baseType;
+ QList<XmlElement *> extendedType;
+ for (const auto &mapping : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ bool isBaseType = true;
+ for (const auto &field : structuredType->fields()) {
+ bool isPreCoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ isPreCoded = true;
+ break;
+ }
+ }
+ if (!isPreCoded)
+ if (!StringIdentifier::typeNameDataTypeConverter.contains(field->typeName()))
+ if (Util::removeNamespace(field->typeName()) != structuredType->name())
+ isBaseType = false;
+ }
+ if (isBaseType)
+ baseType.push_back(structuredType);
+ else
+ extendedType.push_back(structuredType);
+ } else {
+ baseType.push_front(mapping);
+ }
+ }
+ m_generateMapping = baseType;
+
+ while (!extendedType.isEmpty()) {
+ QList<XmlElement *> tmpExtendedType = extendedType;
+ for (int i = 0; i < extendedType.size(); i++) {
+ const auto structuredType = dynamic_cast<StructuredType *>(extendedType.at(i));
+ if (structuredType) {
+ bool independentExtended = true;
+ for (const auto &field : structuredType->fields()) {
+ bool isPreCoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ isPreCoded = true;
+ break;
+ }
+ }
+ bool basicdependecy = false;
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName()))
+ basicdependecy = true;
+ else if (!isPreCoded
+ && Util::removeNamespace(field->typeName()) != structuredType->name()) {
+ for (const auto &type : m_generateMapping) {
+ if (type->name() == Util::removeNamespace(field->typeName())) {
+ basicdependecy = true;
+ }
+ }
+ } else if (!isPreCoded
+ && Util::removeNamespace(field->typeName()) == structuredType->name()) {
+ basicdependecy = true;
+ } else if (isPreCoded)
+ basicdependecy = true;
+ independentExtended &= basicdependecy;
+ }
+
+ if (independentExtended) {
+ m_generateMapping.append(structuredType);
+ extendedType.removeAt(extendedType.indexOf(structuredType));
+ }
+ }
+ }
+ if (extendedType == tmpExtendedType) {
+ return MappingFileGenerator::MappingError::UnableToResolve;
+ }
+ }
+ return MappingFileGenerator::MappingError::NoError;
+}
diff --git a/tools/datatypecodegenerator/mappingfilegenerator.h b/tools/datatypecodegenerator/mappingfilegenerator.h
new file mode 100644
index 0000000..923e69b
--- /dev/null
+++ b/tools/datatypecodegenerator/mappingfilegenerator.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qlist.h>
+#include <QtCore/qtextstream.h>
+
+#include <cmath>
+#include <iostream>
+
+class EnumeratedType;
+class Field;
+class StructuredType;
+class XmlElement;
+
+class MappingFileGenerator
+{
+public:
+ enum MappingError { NoError, UnanbleToWrite, UnableToResolve };
+
+ MappingFileGenerator(const QList<XmlElement *> &generateMapping,
+ const QString &path,
+ const QString &prefix,
+ const QString &header);
+ ~MappingFileGenerator() = default;
+
+ void addGenerateMapping(const QList<XmlElement *> &generateMapping);
+
+ MappingError generateMapping();
+
+private:
+ QList<Field *> m_lengthFields;
+ QList<Field *> m_switchFields;
+ QList<XmlElement *> m_generateMapping;
+ QString m_path;
+ QString m_prefix;
+ QString m_header;
+
+ MappingError generateMappingHeader();
+ MappingError generateMappingCpp();
+
+ void generateFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateScalarOrArrayDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field);
+ void generateOptionalFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateUnionFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+
+ void generateFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateScalarOrArrayEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field);
+ void generateOptionalFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateUnionFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+
+ void generateDeEncodingArray(QTextStream &output);
+
+ void generateDeEncoding(QTextStream &output);
+
+ void generateDecodingEnumeratedType(QTextStream &output, const EnumeratedType *enumeratedType);
+ void generateEncodingEnumeratedType(QTextStream &output, const EnumeratedType *enumeratedType);
+
+ void generateDecodingStructuredType(QTextStream &output, const StructuredType *structuredType);
+ void generateEncodingStructuredType(QTextStream &output, const StructuredType *structuredType);
+
+ MappingFileGenerator::MappingError sortMappings();
+};
diff --git a/tools/datatypecodegenerator/recursivedescentparser.cpp b/tools/datatypecodegenerator/recursivedescentparser.cpp
new file mode 100644
index 0000000..92e8adf
--- /dev/null
+++ b/tools/datatypecodegenerator/recursivedescentparser.cpp
@@ -0,0 +1,389 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "datatypefilewriter.h"
+#include "dependencydatatypevalidator.h"
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "field.h"
+#include "import.h"
+#include "mappingfilegenerator.h"
+#include "recursivedescentparser.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+#include "typedictionary.h"
+
+#include <QtCore/qfile.h>
+
+RecursiveDescentParser::~RecursiveDescentParser()
+{
+ qDeleteAll(m_mapTypeDictionary);
+}
+
+void RecursiveDescentParser::printInOrder() const
+{
+ for (const auto &entry : m_mapTypeDictionary) {
+ entry->print();
+ }
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedType(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ EnumeratedType *enumeratedType = new EnumeratedType(
+ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::lengthInBitsIdentifier).toInt());
+ typeDictionary->addType(enumeratedType);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::enumeratedValueIdentifier)
+ error = parseEnumeratedValue(xmlStreamReader, enumeratedType);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidEnumeratedTypeEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedValue(
+ QXmlStreamReader &xmlStreamReader, EnumeratedType *enumeratedType)
+{
+ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
+
+ if (name.isEmpty())
+ return ParsingError::InvalidEnumeratedValueEntry;
+
+ EnumeratedValue *enumeratedValue = new EnumeratedValue(
+ name, xmlStreamReader.attributes().value(StringIdentifier::valueIdentifier).toInt());
+ enumeratedType->addValue(enumeratedValue);
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidEnumeratedValueEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseField(
+ QXmlStreamReader &xmlStreamReader, StructuredType *structuredType)
+{
+ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
+
+ if (name.isEmpty())
+ return ParsingError::InvalidFieldEntry;
+
+ Field *field = new Field(
+ name,
+ xmlStreamReader.attributes().value(StringIdentifier::typeNameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::lengthFieldIdentifier).toString());
+ if (!field->lengthField().isEmpty())
+ field->setNeedContainer(true);
+
+ if (field->typeName().endsWith(QStringLiteral(":%1").arg(structuredType->name())))
+ field->setRecursive(true);
+
+ if (!xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toString().isEmpty()) {
+ field->setLength(xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toUInt());
+ field->setIsInStructuredTypeBitMask(true);
+ }
+ if (!xmlStreamReader.attributes()
+ .value(StringIdentifier::switchFieldIdentifier)
+ .toString()
+ .isEmpty()) {
+ structuredType->setHasSwitchfield(true);
+ field->setSwitchField(
+ xmlStreamReader.attributes().value(StringIdentifier::switchFieldIdentifier).toString());
+ }
+ if (!xmlStreamReader.attributes()
+ .value(StringIdentifier::switchValueIdentifier)
+ .toString()
+ .isEmpty()) {
+ structuredType->setHasUnion(true);
+ field->setIsUnion(true);
+ field->setSwitchValue(
+ xmlStreamReader.attributes().value(StringIdentifier::switchValueIdentifier).toInt());
+ }
+ if (field->typeName() == StringIdentifier::opcBitIdentifier)
+ structuredType->setContainsBitMask(true);
+ if (field->typeName().contains(StringIdentifier::bitIdentifier)
+ && (field->name().contains(StringIdentifier::specifiedIdentifier)
+ || field->name().contains(StringIdentifier::reservedIdentifier))
+ && structuredType->containsBitMask())
+ field->setIsInStructuredTypeBitMask(true);
+ structuredType->addField(field);
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidFieldEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseFile(
+ const QString &fileName, const bool &dependencyTypeDictionary)
+{
+ ParsingError error = NoError;
+ QFile xmlFile(fileName);
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return InvalidFileName;
+ QXmlStreamReader xmlStreamReader;
+ xmlStreamReader.setDevice((&xmlFile));
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::typeDictionaryIdentifier)
+ error = parseTypeDictionary(xmlStreamReader, dependencyTypeDictionary);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidTypeDictionaryEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ xmlFile.close();
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseImport(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ Import *import = new Import(
+ xmlStreamReader.attributes().value(StringIdentifier::nameSpaceIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::locationIdentifier).toString());
+ typeDictionary->addType(import);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidImportEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseStructuredType(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ StructuredType *structuredType = new StructuredType(
+ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::baseTypeIdentifier).toString());
+ if (StringIdentifier::buildInTypesWithBitMask.contains(structuredType->name())) {
+ structuredType->setIsBuiltInType(true);
+ structuredType->setContainsBitMask(true);
+ }
+ structuredType->setTargetNamespace(typeDictionary->targetNamespace());
+ typeDictionary->addType(structuredType);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->recursive()) {
+ structuredType->setRecursive(true);
+ break;
+ }
+ }
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::fieldIdentifier)
+ error = parseField(xmlStreamReader, structuredType);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidStructuredTypeEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseTypeDictionary(
+ QXmlStreamReader &xmlStreamReader, const bool &dependencyTypeDictionary)
+{
+ QMap<QString, QString> namespaces;
+ for (int i = 0; i < xmlStreamReader.namespaceDeclarations().size(); i++)
+ namespaces.insert(xmlStreamReader.namespaceDeclarations().at(i).prefix().toString(),
+ xmlStreamReader.namespaceDeclarations().at(i).namespaceUri().toString());
+ m_mapTypeDictionary.insert(
+ xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString(),
+ new TypeDictionary(dependencyTypeDictionary,
+ xmlStreamReader.attributes()
+ .value(StringIdentifier::defaultByteOrderIdentifier)
+ .toString(),
+ xmlStreamReader.attributes()
+ .value(StringIdentifier::targetNamespaceIdentifier)
+ .toString(),
+ namespaces));
+ QString targetNamespace
+ = xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString();
+ ParsingError error = NoError;
+ if (!dependencyTypeDictionary && targetNamespace == StringIdentifier::namespaceZeroIdentifier)
+ return CannotFullyGenerateNamespaceZero;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::enumeratedTypeIdentifier)
+ error = parseEnumeratedType(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (xmlStreamReader.name() == StringIdentifier::structuredTypeIdentifier)
+ error = parseStructuredType(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (xmlStreamReader.name() == StringIdentifier::importIdentifier)
+ error = parseImport(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidTypeDictionaryEntry;
+ }
+ }
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::skipKnownElement(
+ QXmlStreamReader &xmlStreamReader)
+{
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::documentationIdentifier
+ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier)
+ skipKnownElement(xmlStreamReader);
+ else
+ error = UnknownEntry;
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::generateInputFiles(
+ const QString &path,
+ const QString &prefix,
+ const QString &header)
+{
+ DependencyDataTypeValidator dependencyDataTypeValidator;
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
+ if (!it.value()->dependencyTypeDictionary())
+ it.value()->accept(&dependencyDataTypeValidator);
+ }
+
+ dependencyDataTypeValidator.setReadResolveDependencies(
+ DependencyDataTypeValidator::ResolveDependencies);
+
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
+ it.value()->accept(&dependencyDataTypeValidator);
+
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
+ it.value()->accept(&dependencyDataTypeValidator);
+
+ if (dependencyDataTypeValidator.unresolvedDependencyStringList().isEmpty()) {
+ DataTypeFileWriter filewriter(path,
+ prefix,
+ header);
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
+ if (!it.value()->dependencyTypeDictionary())
+ it.value()->accept(&filewriter);
+ };
+ if (filewriter.generateMapping().isEmpty()) {
+ return UnableToWriteFile;
+ }
+ MappingFileGenerator mappingFileGenerator(filewriter.generateMapping(), path, prefix, header);
+ if (filewriter.generateTypes(dependencyDataTypeValidator.resolvedDependencyElementList())
+ != DataTypeFileWriter::GeneratingError::NoError) {
+ return UnableToWriteFile;
+ }
+
+ mappingFileGenerator.addGenerateMapping(dependencyDataTypeValidator.resolvedDependencyElementList());
+
+ if (mappingFileGenerator.generateMapping() != MappingFileGenerator::MappingError::NoError)
+ return UnableToWriteFile;
+ } else {
+ return MissingDependency;
+ }
+
+ return NoError;
+}
+
+bool RecursiveDescentParser::isKnownElement(const QXmlStreamReader &xmlStreamReader)
+{
+ return xmlStreamReader.name() == StringIdentifier::documentationIdentifier
+ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier;
+}
+
+QString RecursiveDescentParser::permittedName(const QString &name) const
+{
+ if (name.isEmpty())
+ return {};
+
+ auto result = name;
+
+ if (StringIdentifier::illegalNames.contains(name)) {
+ result = Util::lowerFirstLetter(name);
+ result = QStringLiteral("_%1").arg(name);
+ }
+
+ if (result.contains(QStringLiteral(" ")))
+ result.replace(QStringLiteral(" "), QStringLiteral("_"));
+ if (name.contains(QStringLiteral("-")))
+ result.replace(QStringLiteral("-"), QStringLiteral("_"));
+
+ return result;
+}
diff --git a/tools/datatypecodegenerator/recursivedescentparser.h b/tools/datatypecodegenerator/recursivedescentparser.h
new file mode 100644
index 0000000..3e540f4
--- /dev/null
+++ b/tools/datatypecodegenerator/recursivedescentparser.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qmap.h>
+#include <QtCore/qxmlstream.h>
+
+class EnumeratedType;
+class StructuredType;
+class TypeDictionary;
+
+class RecursiveDescentParser
+{
+ Q_GADGET
+public:
+ enum ParsingError {
+ NoError,
+ InvalidFileName,
+ InvalidTypeDictionaryEntry,
+ InvalidImportEntry,
+ InvalidEnumeratedTypeEntry,
+ InvalidStructuredTypeEntry,
+ InvalidEnumeratedValueEntry,
+ InvalidFieldEntry,
+ UnknownEntry,
+ MissingDependency,
+ CannotFullyGenerateNamespaceZero,
+ UnableToWriteFile,
+ UnableToResolveDependency
+ };
+ Q_ENUM(ParsingError)
+
+ RecursiveDescentParser() = default;
+ ~RecursiveDescentParser();
+
+ void printInOrder() const;
+ ParsingError parseEnumeratedType(QXmlStreamReader &xmlStreamReader,
+ TypeDictionary *typeDictionary);
+
+ ParsingError parseEnumeratedValue(QXmlStreamReader &xmlStreamReader,
+ EnumeratedType *enumeratedType);
+ ParsingError parseField(QXmlStreamReader &xmlStreamReader, StructuredType *structuredType);
+
+ ParsingError parseFile(const QString &fileName, const bool &dependencyTypeDictionary);
+ ParsingError parseImport(QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary);
+
+ ParsingError parseStructuredType(QXmlStreamReader &xmlStreamReader,
+ TypeDictionary *typeDictionary);
+ ParsingError parseTypeDictionary(QXmlStreamReader &xmlStreamReader,
+ const bool &dependencyTypeDictionary);
+
+ ParsingError skipKnownElement(QXmlStreamReader &xmlStreamReader);
+ ParsingError generateInputFiles(const QString &path,
+ const QString &prefix,
+ const QString &header);
+
+ ParsingError dependencyCheck();
+ bool isKnownElement(const QXmlStreamReader &xmlStreamReader);
+
+ QString permittedName(const QString &name) const;
+
+private:
+ QString m_fileName;
+ QMap<QString, TypeDictionary *> m_mapTypeDictionary;
+};
diff --git a/tools/datatypecodegenerator/stringidentifier.cpp b/tools/datatypecodegenerator/stringidentifier.cpp
new file mode 100644
index 0000000..1a2b9bb
--- /dev/null
+++ b/tools/datatypecodegenerator/stringidentifier.cpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "stringidentifier.h"
+
+const QString StringIdentifier::baseTypeIdentifier = "BaseType";
+const QString StringIdentifier::binaryTypeIdentifier = "opc";
+const QString StringIdentifier::bitIdentifier = "Bit";
+const QString StringIdentifier::booleanIdentifier = "Boolean";
+const QString StringIdentifier::byteIdentifier = "Byte";
+const QString StringIdentifier::sbyteIdentifier = "SByte";
+const QString StringIdentifier::byteStringIdentifier = "ByteString";
+const QString StringIdentifier::charArrayIdentifier = "CharArray";
+const QString StringIdentifier::cppIdentifier = ".cpp";
+const QString StringIdentifier::datetimeIdentifier = "DateTime";
+const QString StringIdentifier::defaultByteOrderIdentifier = "DefaultByteOrder";
+const QString StringIdentifier::documentationIdentifier = "Documentation";
+const QString StringIdentifier::doubleIdentifier = "Double";
+const QString StringIdentifier::enumeratedTypeIdentifier = "EnumeratedType";
+const QString StringIdentifier::enumeratedValueIdentifier = "EnumeratedValue";
+const QString StringIdentifier::fieldIdentifier = "Field";
+const QString StringIdentifier::floatIdentifier = "Float";
+const QString StringIdentifier::guidIdentifier = "Guid";
+const QString StringIdentifier::headerIdentifier = ".h";
+const QString StringIdentifier::importIdentifier = "Import";
+const QString StringIdentifier::integerIdentifier = "Int";
+const QString StringIdentifier::lengthIdentifier = "Length";
+const QString StringIdentifier::lengthFieldIdentifier = "LengthField";
+const QString StringIdentifier::lengthInBitsIdentifier = "LengthInBits";
+const QString StringIdentifier::locationIdentifier = "Location";
+const QString StringIdentifier::mainIdentifier = "main.cpp";
+const QString StringIdentifier::makelistIdentifier = "CMakeLists.txt";
+const QString StringIdentifier::nameIdentifier = "Name";
+const QString StringIdentifier::nameSpaceIdentifier = "Namespace";
+const QString StringIdentifier::namespaceZeroIdentifier = "http://opcfoundation.org/UA/";
+const QString StringIdentifier::opaqueTypeIdentifier = "OpaqueType";
+const QString StringIdentifier::projectIdentifier = "cmakeqt6";
+const QString StringIdentifier::qByteArrayIdentifier = "QByteArray";
+const QString StringIdentifier::qStringIdentifier = "QString";
+const QString StringIdentifier::uaStatusCodeIdentifier = "StatusCode";
+const QString StringIdentifier::reservedIdentifier = "Reserved";
+const QString StringIdentifier::specifiedIdentifier = "Specified";
+const QString StringIdentifier::structuredTypeIdentifier = "StructuredType";
+const QString StringIdentifier::switchFieldIdentifier = "SwitchField";
+const QString StringIdentifier::switchValueIdentifier = "SwitchValue";
+const QString StringIdentifier::targetNamespaceIdentifier = "TargetNamespace";
+const QString StringIdentifier::typeDictionaryIdentifier = "TypeDictionary";
+const QString StringIdentifier::typeNameIdentifier = "TypeName";
+const QString StringIdentifier::valueIdentifier = "Value";
+const QString StringIdentifier::xmlElementIdentifier = "XmlElement";
+const QString StringIdentifier::opcBitIdentifier = "opc:Bit";
+
+const QList<StringIdentifier::OpcUaPrecodedType> StringIdentifier::opcUaPrecodedTypes{
+ OpcUaPrecodedType("ApplicationRecordDataType", "QOpcUaApplicationRecordDataType",
+ "QOpcUaApplicationRecordDataType"),
+ OpcUaPrecodedType("Argument", "QOpcUaArgument", "QOpcUaArgument"),
+ OpcUaPrecodedType("AxisInformation", "QOpcUaAxisInformation", "QOpcUaAxisInformation"),
+ OpcUaPrecodedType("ComplexNumber", "QOpcUaComplexNumber", "QOpcUaComplexNumber"),
+ OpcUaPrecodedType("DoubleComplexNumber", "QOpcUaDoubleComplexNumber", "QOpcUaDoubleComplexNumber"),
+ OpcUaPrecodedType("EUInformation", "QOpcUaEUInformation", "QOpcUaEUInformation"),
+ OpcUaPrecodedType("ExpandedNodeId", "QOpcUaExpandedNodeId", "QOpcUaExpandedNodeId"),
+ OpcUaPrecodedType("ExtensionObject", "QOpcUaExtensionObject", "QOpcUaExtensionObject"),
+ OpcUaPrecodedType("LocalizedText", "QOpcUaLocalizedText", "QOpcUaLocalizedText"),
+ OpcUaPrecodedType("NodeId", QString(), "QString", "QString"),
+ OpcUaPrecodedType("QualifiedName", "QOpcUaQualifiedName", "QOpcUaQualifiedName"),
+ OpcUaPrecodedType("Range", "QOpcUaRange", "QOpcUaRange"),
+ OpcUaPrecodedType("StatusCode", "qopcuatype.h", "QOpcUa::UaStatusCode"),
+ OpcUaPrecodedType("XVType", "QOpcUaXValue", "QOpcUaXValue"),
+ OpcUaPrecodedType("DiagnosticInfo", "QOpcUaDiagnosticInfo", "QOpcUaDiagnosticInfo"),
+ OpcUaPrecodedType("StructureField", "QOpcUaStructureField", "QOpcUaStructureField"),
+ OpcUaPrecodedType("StructureDefinition", "QOpcUaStructureDefinition", "QOpcUaStructureDefinition"),
+ OpcUaPrecodedType("EnumField", "QOpcUaEnumField", "QOpcUaEnumField"),
+ OpcUaPrecodedType("EnumDefinition", "QOpcUaEnumDefinition", "QOpcUaEnumDefinition")
+};
+
+const QList<QString> StringIdentifier::buildInTypesWithBitMask = {"DiagnosticInfo",
+ "LocalizedText",
+ "Variant",
+ "DataValue"};
+
+const QMap<QString, QString> StringIdentifier::typeNameDataTypeConverter
+ = {{"opc:Bit", "bool"},
+ {"opc:Boolean", "bool"},
+ {"opc:Byte", "quint8"},
+ {"opc:ByteString", "QByteArray"},
+ {"opc:CharArray", "QString"},
+ {"opc:DateTime", "QDateTime"},
+ {"opc:Double", "double"},
+ {"opc:Float", "float"},
+ {"opc:Int16", "qint16"},
+ {"opc:Int32", "qint32"},
+ {"opc:Int64", "qint64"},
+ {"opc:SByte", "qint8"},
+ {"opc:String", "QString"},
+ {"opc:UInt16", "quint16"},
+ {"opc:UInt32", "quint32"},
+ {"opc:UInt64", "quint64"},
+ {"opc:Guid", "QUuid"},
+ {"ua:XmlElement", "QString"}};
+
+const QList<QString> StringIdentifier::illegalNames
+ = {"Boolean", "boolean", "Int16", "int16", "Float", "float",
+ "Datetime", "datetime", "byteString", "ByteString", "XmlElement", "xmlElement",
+ "byte", "Byte", "SByte", "sByte", "Int32", "int32",
+ "Int64", "int64", "Double", "double", "String", "string"};
+
+const QSet<QString> StringIdentifier::precodedTypesWithDebugOperator = { "LocalizedText", "NodeId", "QualifiedName" };
+
+QString StringIdentifier::OpcUaPrecodedType::name() const
+{
+ return m_name;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::filename() const
+{
+ return m_filename;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::className() const
+{
+ return m_className;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::deEncoderName() const
+{
+ return m_deEncoderName;
+}
diff --git a/tools/datatypecodegenerator/stringidentifier.h b/tools/datatypecodegenerator/stringidentifier.h
new file mode 100644
index 0000000..53dcff4
--- /dev/null
+++ b/tools/datatypecodegenerator/stringidentifier.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstring.h>
+
+class StringIdentifier
+{
+public:
+ static const QString baseTypeIdentifier;
+ static const QString binaryTypeIdentifier;
+ static const QString bitIdentifier;
+ static const QString booleanIdentifier;
+ static const QString byteIdentifier;
+ static const QString sbyteIdentifier;
+ static const QString byteStringIdentifier;
+ static const QString charArrayIdentifier;
+ static const QString cppIdentifier;
+ static const QString datetimeIdentifier;
+ static const QString defaultByteOrderIdentifier;
+ static const QString documentationIdentifier;
+ static const QString doubleIdentifier;
+ static const QString enumeratedTypeIdentifier;
+ static const QString enumeratedValueIdentifier;
+ static const QString fieldIdentifier;
+ static const QString floatIdentifier;
+ static const QString guidIdentifier;
+ static const QString headerIdentifier;
+ static const QString importIdentifier;
+ static const QString integerIdentifier;
+ static const QString lengthIdentifier;
+ static const QString lengthFieldIdentifier;
+ static const QString lengthInBitsIdentifier;
+ static const QString locationIdentifier;
+ static const QString mainIdentifier;
+ static const QString makelistIdentifier;
+ static const QString nameIdentifier;
+ static const QString nameSpaceIdentifier;
+ static const QString namespaceZeroIdentifier;
+ static const QString opaqueTypeIdentifier;
+ static const QString projectIdentifier;
+ static const QString qByteArrayIdentifier;
+ static const QString qStringIdentifier;
+ static const QString uaStatusCodeIdentifier;
+ static const QString reservedIdentifier;
+ static const QString specifiedIdentifier;
+ static const QString structuredTypeIdentifier;
+ static const QString switchFieldIdentifier;
+ static const QString switchValueIdentifier;
+ static const QString targetNamespaceIdentifier;
+ static const QString typeDictionaryIdentifier;
+ static const QString typeNameIdentifier;
+ static const QString valueIdentifier;
+ static const QString xmlElementIdentifier;
+ static const QString opcBitIdentifier;
+
+ class OpcUaPrecodedType
+ {
+ public:
+ OpcUaPrecodedType(const QString &typeName,
+ const QString &fileName,
+ const QString &className,
+ const QString &deEncoderName = QString())
+ : m_name(typeName)
+ , m_filename(fileName)
+ , m_className(className)
+ , m_deEncoderName(deEncoderName)
+ {}
+
+ bool contains(QString name) const
+ {
+ return m_name == name || m_filename == name || m_className == name;
+ }
+
+ QString name() const;
+
+ QString filename() const;
+
+ QString className() const;
+
+ QString deEncoderName() const;
+
+ private:
+ QString m_name;
+ QString m_filename;
+ QString m_className;
+ QString m_deEncoderName;
+ };
+
+ static const QList<OpcUaPrecodedType> opcUaPrecodedTypes;
+ static const QList<QString> buildInTypesWithBitMask;
+ static const QMap<QString, QString> typeNameDataTypeConverter;
+
+ static const QList<QString> illegalNames;
+
+ static const QSet<QString> precodedTypesWithDebugOperator;
+};
diff --git a/tools/datatypecodegenerator/structuredtype.cpp b/tools/datatypecodegenerator/structuredtype.cpp
new file mode 100644
index 0000000..948e9bc
--- /dev/null
+++ b/tools/datatypecodegenerator/structuredtype.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "field.h"
+#include "structuredtype.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+StructuredType::StructuredType(const QString &name, const QString &baseType)
+ : XmlElement(name)
+ , m_baseType(baseType)
+{}
+
+StructuredType::~StructuredType()
+{
+ qDeleteAll(m_fields);
+}
+
+QString StructuredType::baseType() const
+{
+ return m_baseType;
+}
+
+void StructuredType::setBaseType(const QString &baseType)
+{
+ m_baseType = baseType;
+}
+
+void StructuredType::addField(Field *field)
+{
+ m_fields.push_back(field);
+}
+
+void StructuredType::print() const
+{
+ XmlElement::print();
+ qDebug() << "BaseType: " << m_baseType;
+ for (int i = 0; i < m_fields.size(); i++)
+ m_fields.at(i)->print();
+}
+
+void StructuredType::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (const auto &field : m_fields)
+ field->accept(visitor);
+}
+
+QList<Field *> StructuredType::fields() const
+{
+ return m_fields;
+}
+
+void StructuredType::setFields(const QList<Field *> &fields)
+{
+ m_fields = fields;
+}
+
+bool StructuredType::containsBitMask() const
+{
+ return m_containsBitMask;
+}
+
+void StructuredType::setContainsBitMask(bool containsBitMask)
+{
+ m_containsBitMask = containsBitMask;
+}
+
+bool StructuredType::isBuiltInType() const
+{
+ return m_isBuiltInType;
+}
+
+void StructuredType::setIsBuiltInType(bool isBuiltInType)
+{
+ m_isBuiltInType = isBuiltInType;
+}
+
+bool StructuredType::hasSwitchfield() const
+{
+ return m_hasSwitchfield;
+}
+
+void StructuredType::setHasSwitchfield(bool hasSwitchfield)
+{
+ m_hasSwitchfield = hasSwitchfield;
+}
+
+bool StructuredType::hasUnion() const
+{
+ return m_hasUnion;
+}
+
+void StructuredType::setHasUnion(bool hasUnion)
+{
+ m_hasUnion = hasUnion;
+}
+
+QString StructuredType::targetNamespace() const
+{
+ return m_targetNamespace;
+}
+
+void StructuredType::setTargetNamespace(const QString &targetNamespace)
+{
+ m_targetNamespace = targetNamespace;
+}
+
+bool StructuredType::recursive() const
+{
+ return m_recursive;
+}
+
+void StructuredType::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
diff --git a/tools/datatypecodegenerator/structuredtype.h b/tools/datatypecodegenerator/structuredtype.h
new file mode 100644
index 0000000..13decd5
--- /dev/null
+++ b/tools/datatypecodegenerator/structuredtype.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+#include <QtCore/qlist.h>
+
+class Field;
+
+class StructuredType : public XmlElement
+{
+public:
+ StructuredType(const QString &name, const QString &baseType);
+ ~StructuredType();
+
+ QString baseType() const;
+ void setBaseType(const QString &baseType);
+
+ void addField(Field *field);
+
+ void print() const override;
+ void accept(Visitor *visitor) override;
+
+ QList<Field *> fields() const;
+ void setFields(const QList<Field *> &fields);
+
+ bool containsBitMask() const;
+ void setContainsBitMask(bool containsBitMask);
+
+ bool isBuiltInType() const;
+ void setIsBuiltInType(bool isBuiltInType);
+
+ bool hasSwitchfield() const;
+ void setHasSwitchfield(bool hasSwitchfield);
+
+ bool hasUnion() const;
+ void setHasUnion(bool hasUnion);
+
+ QString targetNamespace() const;
+ void setTargetNamespace(const QString &targetNamespace);
+
+ bool recursive() const;
+ void setRecursive(bool recursive);
+
+private:
+ bool m_containsBitMask{false};
+ bool m_isBuiltInType{false};
+ bool m_hasSwitchfield{false};
+ bool m_hasUnion{false};
+ bool m_recursive{false};
+ QString m_baseType;
+ QString m_targetNamespace;
+ QList<Field *> m_fields;
+};
diff --git a/tools/datatypecodegenerator/typedictionary.cpp b/tools/datatypecodegenerator/typedictionary.cpp
new file mode 100644
index 0000000..7accd34
--- /dev/null
+++ b/tools/datatypecodegenerator/typedictionary.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "typedictionary.h"
+
+#include "enumeratedtype.h"
+#include "import.h"
+#include "structuredtype.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+TypeDictionary::TypeDictionary(bool dependencyTypeDictionary,
+ const QString &defaultByOrder,
+ const QString &targetNamespace,
+ const QMap<QString, QString> &namespaces)
+ : m_dependencyTypeDictionary(dependencyTypeDictionary)
+ , m_defaultByOrder(defaultByOrder)
+ , m_targetNamespace(targetNamespace)
+ , m_namespaces(namespaces)
+{}
+
+TypeDictionary::~TypeDictionary()
+{
+ qDeleteAll(m_types);
+}
+
+QMap<QString, QString> TypeDictionary::namespaces() const
+{
+ return m_namespaces;
+}
+
+void TypeDictionary::setNamespaces(const QMap<QString, QString> &namespaces)
+{
+ m_namespaces = namespaces;
+}
+
+QString TypeDictionary::defaultByOrder() const
+{
+ return m_defaultByOrder;
+}
+
+void TypeDictionary::setDefaultByOrder(const QString &defaultByOrder)
+{
+ m_defaultByOrder = defaultByOrder;
+}
+
+QString TypeDictionary::targetNamespace() const
+{
+ return m_targetNamespace;
+}
+
+void TypeDictionary::setTargetNamespace(const QString &targetNamespace)
+{
+ m_targetNamespace = targetNamespace;
+}
+
+QList<const Import *> TypeDictionary::imports() const
+{
+ QList<const Import *> imports;
+ for (auto element : m_types) {
+ Import *import = dynamic_cast<Import *>(element);
+ if (import) {
+ imports.push_back(import);
+ }
+ }
+ return imports;
+}
+
+bool TypeDictionary::dependencyTypeDictionary() const
+{
+ return m_dependencyTypeDictionary;
+}
+
+QList<XmlElement *> TypeDictionary::types() const
+{
+ return m_types;
+}
+
+void TypeDictionary::addType(XmlElement *type)
+{
+ m_types.push_back(type);
+}
+
+void TypeDictionary::print() const
+{
+ for (auto it = m_namespaces.constBegin(); it != m_namespaces.constEnd(); ++it) {
+ qDebug() << it.key() << ": " << it.value();
+ }
+
+ qDebug() << "defaultByOrder:" << m_defaultByOrder << "\tTargetNamespace:" << m_targetNamespace;
+
+ for (auto element : m_types) {
+ const auto *enumeratedType = dynamic_cast<EnumeratedType *>(element);
+ if (enumeratedType) {
+ enumeratedType->print();
+ } else {
+ const auto *structuredType = dynamic_cast<StructuredType *>(element);
+ if (structuredType) {
+ structuredType->print();
+ } else {
+ const auto *import = dynamic_cast<Import *>(element);
+ if (import) {
+ import->print();
+ }
+ }
+ }
+ }
+}
+
+void TypeDictionary::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (const auto &element : m_types)
+ element->accept(visitor);
+}
diff --git a/tools/datatypecodegenerator/typedictionary.h b/tools/datatypecodegenerator/typedictionary.h
new file mode 100644
index 0000000..abab83e
--- /dev/null
+++ b/tools/datatypecodegenerator/typedictionary.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qmap.h>
+
+class Import;
+class Visitor;
+class XmlElement;
+
+class TypeDictionary
+{
+public:
+ TypeDictionary(bool dependencyTypeDictionary,
+ const QString &defaultByOrder,
+ const QString &targetNamespace,
+ const QMap<QString, QString> &namespaces);
+ ~TypeDictionary();
+
+ void addType(XmlElement *type);
+ void print() const;
+ void accept(Visitor *visitor);
+
+ QMap<QString, QString> namespaces() const;
+ void setNamespaces(const QMap<QString, QString> &namespaces);
+
+ QString defaultByOrder() const;
+ void setDefaultByOrder(const QString &defaultByOrder);
+
+ QString targetNamespace() const;
+ void setTargetNamespace(const QString &targetNamespace);
+
+ QList<const Import *> imports() const;
+
+ bool dependencyTypeDictionary() const;
+
+ QList<XmlElement *> types() const;
+
+private:
+ bool m_dependencyTypeDictionary;
+ QString m_defaultByOrder;
+ QString m_targetNamespace;
+ QMap<QString, QString> m_namespaces;
+ QList<XmlElement *> m_types;
+};
diff --git a/tools/datatypecodegenerator/util.cpp b/tools/datatypecodegenerator/util.cpp
new file mode 100644
index 0000000..7a05e22
--- /dev/null
+++ b/tools/datatypecodegenerator/util.cpp
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+
+#include <QtCore/qstringlist.h>
+
+QString Util::indent(int level)
+{
+ return QStringLiteral("%1").arg(" ", level * 4, ' ');
+}
+
+QString Util::lineBreak(int n)
+{
+ return QStringLiteral("%1").arg("\n", n, '\n');
+}
+
+QString Util::removeNamespace(const QString &typeName)
+{
+ return typeName.split(":").value(1, QString());
+}
+
+QString Util::lowerFirstLetter(const QString &temp)
+{
+ if (temp.isEmpty())
+ return temp;
+
+ auto result = temp;
+ result.front() = result.at(0).toLower();
+ return result;
+}
diff --git a/tools/datatypecodegenerator/util.h b/tools/datatypecodegenerator/util.h
new file mode 100644
index 0000000..5e877b1
--- /dev/null
+++ b/tools/datatypecodegenerator/util.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qstring.h>
+
+namespace Util
+{
+ QString indent(int level);
+ QString lineBreak(int n = 1);
+
+ QString removeNamespace(const QString &typeName);
+ QString lowerFirstLetter(const QString &temp);
+}
diff --git a/tools/datatypecodegenerator/visitor.h b/tools/datatypecodegenerator/visitor.h
new file mode 100644
index 0000000..732ebda
--- /dev/null
+++ b/tools/datatypecodegenerator/visitor.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class EnumeratedType;
+class EnumeratedValue;
+class Import;
+class Field;
+class StructuredType;
+class TypeDictionary;
+
+class Visitor
+{
+public:
+ virtual ~Visitor() = default;
+ virtual void visit(XmlElement *xmlElement) = 0;
+ virtual void visit(EnumeratedType *enumteratedType) = 0;
+ virtual void visit(EnumeratedValue *enumeratedValue) = 0;
+ virtual void visit(Import *import) = 0;
+ virtual void visit(Field *field) = 0;
+ virtual void visit(StructuredType *structuredType) = 0;
+ virtual void visit(TypeDictionary *typeDictionary) = 0;
+};
diff --git a/tools/datatypecodegenerator/xmlelement.cpp b/tools/datatypecodegenerator/xmlelement.cpp
new file mode 100644
index 0000000..d28e036
--- /dev/null
+++ b/tools/datatypecodegenerator/xmlelement.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "xmlelement.h"
+
+#include "util.h"
+
+#include <QtCore/qdebug.h>
+
+XmlElement::XmlElement(const QString &name)
+ : m_name(name)
+{}
+
+void XmlElement::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString XmlElement::name() const
+{
+ return m_name;
+}
+
+QString XmlElement::lowerFirstName() const
+{
+ return Util::lowerFirstLetter(m_name);
+}
+
+void XmlElement::print() const
+{
+ qDebug() << "name: " << m_name;
+}
diff --git a/tools/datatypecodegenerator/xmlelement.h b/tools/datatypecodegenerator/xmlelement.h
new file mode 100644
index 0000000..12809f2
--- /dev/null
+++ b/tools/datatypecodegenerator/xmlelement.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qstring.h>
+
+class Visitor;
+
+class XmlElement
+{
+public:
+ XmlElement() = default;
+ XmlElement(const QString &name);
+ virtual ~XmlElement() = default;
+
+ void setName(const QString &name);
+ QString name() const;
+ QString lowerFirstName() const;
+
+ virtual void print() const;
+ virtual void accept(Visitor *visitor) = 0;
+
+private:
+ QString m_name;
+};