1// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "util.h"
5#include "datatypefilewriter.h"
6#include "enumeratedtype.h"
7#include "enumeratedvalue.h"
8#include "field.h"
9#include "stringidentifier.h"
10#include "structuredtype.h"
11
12#include <QtCore/qdir.h>
13#include <QtCore/qfile.h>
14#include <QtCore/qset.h>
15
16using namespace Qt::Literals::StringLiterals;
17
18DataTypeFileWriter::DataTypeFileWriter(const QString &path,
19 const QString &prefix,
20 const QString &header)
21 : m_path(path)
22 , m_prefix(prefix)
23 , m_header(header)
24{}
25
26void DataTypeFileWriter::visit(XmlElement *xmlElement)
27{
28 Q_UNUSED(xmlElement);
29}
30
31void DataTypeFileWriter::visit(EnumeratedType *enumeratedType)
32{
33 bool isPrecodedType = false;
34 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
35 if (precodedType.contains(name: enumeratedType->name())) {
36 isPrecodedType = true;
37 break;
38 }
39 }
40 if (!isPrecodedType) {
41 m_generateMapping.append(t: enumeratedType);
42 }
43}
44
45void DataTypeFileWriter::visit(EnumeratedValue *enumeratedValue)
46{
47 Q_UNUSED(enumeratedValue);
48}
49
50void DataTypeFileWriter::visit(Field *field)
51{
52 Q_UNUSED(field);
53}
54
55void DataTypeFileWriter::visit(Import *import)
56{
57 Q_UNUSED(import);
58}
59
60void DataTypeFileWriter::visit(StructuredType *structuredType)
61{
62 if (!structuredType->fields().empty()) {
63 bool isPrecodedType = false;
64 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
65 if (precodedType.contains(name: structuredType->name())) {
66 isPrecodedType = true;
67 break;
68 }
69 }
70 if (!isPrecodedType) {
71 m_generateMapping.append(t: structuredType);
72 }
73 }
74}
75
76void DataTypeFileWriter::visit(TypeDictionary *typeDictionary)
77{
78 Q_UNUSED(typeDictionary);
79}
80
81void DataTypeFileWriter::writeLicenseHeader(QTextStream &output)
82{
83 if (!m_header.isEmpty())
84 output << m_header << Util::lineBreak(n: 2);
85}
86
87void DataTypeFileWriter::writeStructuredTypeCpp(const StructuredType *structuredType,
88 QTextStream &output)
89{
90 if (!m_generatedStructuredTypeFilenames.contains(
91 str: u"%1%2"_s.arg(args&: m_prefix, args: structuredType->name())))
92 m_generatedStructuredTypeFilenames.push_back(
93 t: u"%1%2"_s.arg(args&: m_prefix, args: structuredType->name()));
94 output << "#include \"" << m_prefix.toLower() << structuredType->name().toLower() << ".h\"";
95
96 QList<QString> mutualIncludes;
97 const auto typeFields = structuredType->fields();
98 for (const auto field : typeFields) {
99 const auto typeName = field->typeNameSecondPart();
100 for (const auto &type : std::as_const(t&: m_generateMapping)) {
101 StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(type);
102 if (tempStructuredType) {
103 if (tempStructuredType == structuredType)
104 continue;
105 const auto tempFields = tempStructuredType->fields();
106 for (const auto &tempField : tempFields) {
107 if (typeName == tempStructuredType->name()
108 && tempField->typeNameSecondPart() == structuredType->name()) {
109 if (!mutualIncludes.contains(
110 str: u"#include \"%1%2.h\"\n"_s
111 .arg(args: m_prefix.toLower(), args: tempStructuredType->name().toLower())))
112 mutualIncludes.push_back(
113 t: u"#include \"%1%2.h\"\n"_s
114 .arg(args: m_prefix.toLower(), args: tempStructuredType->name().toLower()));
115 }
116 }
117 }
118 }
119 }
120 if (!mutualIncludes.isEmpty()) {
121 output << Util::lineBreak();
122 std::sort(first: mutualIncludes.begin(), last: mutualIncludes.end());
123 for (const auto &include : mutualIncludes) {
124 output << include;
125 }
126 }
127 if (structuredType->recursive()) {
128 output << Util::lineBreak();
129 output << "#include <optional>";
130 output << Util::lineBreak();
131 }
132 output << Util::lineBreak(n: 2);
133
134 writeStructuredTypeCppClassComment(structuredType, output);
135 writeStructuredTypeCppDataClass(structuredType, output);
136 writeStructuredTypeCppConstructors(structuredType, output);
137 writeStructuredTypeCppOperatorAssignment(structuredType, output);
138 writeStructuredTypeCppOperatorEquality(structuredType, output);
139 writeStructuredTypeCppQVariant(structuredType, output);
140 writeStructuredTypeCppDestructor(structuredType, output);
141 writeStructuredTypeCppGetterSetter(structuredType, output);
142 writeStructuredTypeCppDebug(structuredType, output);
143}
144
145void DataTypeFileWriter::writeStructuredTypeCppClassComment(const StructuredType *structuredType,
146 QTextStream &output)
147{
148 output << "/*!"
149 << Util::lineBreak();
150 output << Util::indent(level: 1) << "\\class " << m_prefix << structuredType->name() << Util::lineBreak();
151 output << Util::indent(level: 1) << "\\brief the OPC UA " << structuredType->name() << "."
152 << Util::lineBreak();
153 output << "*/"
154 << Util::lineBreak();
155}
156
157void DataTypeFileWriter::writeStructuredTypeCppDataClass(const StructuredType *structuredType,
158 QTextStream &output)
159{
160 output << "class " << m_prefix << structuredType->name() << "Data : public QSharedData"
161 << Util::lineBreak();
162 output << "{"
163 << Util::lineBreak();
164 output << "public:"
165 << Util::lineBreak();
166
167 writeStructuredTypeCppDataClassMember(structuredType, output);
168
169 output << "};"
170 << Util::lineBreak(n: 2);
171}
172
173void DataTypeFileWriter::writeStructuredTypeCppDataClassMember(const StructuredType *structuredType,
174 QTextStream &output)
175{
176 const auto typeFields = structuredType->fields();
177 QList<Field *> unionMember;
178 if (structuredType->hasUnion()) {
179 for (const auto &possibleUnionMember : typeFields) {
180 for (const auto &field : typeFields) {
181 if (field->isUnion()) {
182 if (field->switchField() == possibleUnionMember->name()
183 && !unionMember.contains(t: possibleUnionMember))
184 unionMember.push_back(t: possibleUnionMember);
185 }
186 }
187 }
188 }
189 QList<Field *> arrayLengthfields;
190 for (const auto &possibleArrayLengthField : typeFields) {
191 for (const auto &field : typeFields) {
192 if (possibleArrayLengthField->name() == field->lengthField()
193 && !arrayLengthfields.contains(t: possibleArrayLengthField))
194 arrayLengthfields.push_back(t: possibleArrayLengthField);
195 }
196 }
197
198 QList<Field *> switchFields;
199 if (structuredType->hasSwitchfield()) {
200 for (const auto &possibleOptionalMember : typeFields) {
201 for (const auto &field : typeFields) {
202 if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
203 switchFields.push_back(t: field);
204 }
205 }
206 }
207
208 for (const auto &field : typeFields) {
209 if (field->isInStructuredTypeBitMask() && !switchFields.contains(t: field))
210 continue;
211
212 bool isEnumeration = false;
213 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
214 if (enumeratedType->name() == field->typeName().split(sep: ':'_L1).at(i: 1)) {
215 isEnumeration = true;
216 field->setIsEnum(true);
217 }
218 }
219
220 bool isPreCoded = false;
221 if (!arrayLengthfields.contains(t: field)) {
222 if (!unionMember.contains(t: field)) {
223 output << Util::indent(level: 1);
224 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName())) {
225 if (!field->needContainer()) {
226 output << StringIdentifier::typeNameDataTypeConverter.value(
227 key: field->typeName())
228 << " ";
229 } else {
230 output << "QList<"
231 << StringIdentifier::typeNameDataTypeConverter.value(
232 key: field->typeName())
233 << "> ";
234 }
235 } else {
236 const auto typeName = field->typeNameSecondPart();
237 for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
238 if (preCodedType.contains(name: typeName)) {
239 isPreCoded = true;
240 if (field->needContainer())
241 output << "QList<";
242 if (preCodedType.deEncoderName().isEmpty())
243 output << preCodedType.className();
244 else
245 output << preCodedType.deEncoderName();
246 if (field->needContainer())
247 output << ">";
248 output << " ";
249 break;
250 }
251 }
252 if (!isPreCoded) {
253 if (field->needContainer())
254 output << "QList<" << m_prefix << typeName << "> ";
255 else if (field->recursive())
256 output << "std::optional<" << m_prefix << typeName << "> ";
257 else if (isEnumeration)
258 output << m_prefix << "::" << typeName << " ";
259 else
260 output << m_prefix << typeName << " ";
261 }
262 }
263
264 const auto lowerCaseFieldName = field->lowerFirstName();
265 output << lowerCaseFieldName;
266 if (!field->needContainer()) {
267 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName())) {
268 if (field->typeName().contains(s: StringIdentifier::integerIdentifier)
269 || field->typeName().contains(s: StringIdentifier::doubleIdentifier)
270 || field->typeName().contains(s: StringIdentifier::floatIdentifier)
271 || field->typeName().contains(s: StringIdentifier::byteIdentifier)
272 || field->typeName().contains(s: StringIdentifier::sbyteIdentifier))
273 output << " {0}";
274 else if (field->typeName().contains(s: StringIdentifier::booleanIdentifier)
275 || field->typeName().contains(s: StringIdentifier::bitIdentifier))
276 output << " {false}";
277 } else if (field->isEnum()) {
278 const auto enumType = std::find_if(first: m_enumeratedTypes.constBegin(), last: m_enumeratedTypes.constEnd(),
279 pred: [field](EnumeratedType *e) { return e->name() == field->typeNameSecondPart(); });
280 const auto firstValueName = enumType != m_enumeratedTypes.constEnd() && !(*enumType)->values().empty()
281 ? (*enumType)->values().first()->name() : u"Unknown"_s;
282 if (!firstValueName.isEmpty())
283 output << " {" << m_prefix << "::" << field->typeNameSecondPart() << "::" << firstValueName << "}";
284 else
285 output << " {}";
286 } else {
287 if (field->typeName().contains(s: StringIdentifier::uaStatusCodeIdentifier))
288 output << " {QOpcUa::UaStatusCode::Good}";
289 }
290 }
291 output << ";"
292 << Util::lineBreak();
293 } else {
294 const auto fieldNameLowerCase = field->lowerFirstName();
295 output << Util::indent(level: 1) << m_prefix << structuredType->name() << "::" << field->name()
296 << " " << fieldNameLowerCase << " = " << m_prefix << structuredType->name()
297 << "::" << field->name() << "::"
298 << "None";
299
300 output << ";"
301 << Util::lineBreak();
302 }
303 }
304 }
305}
306
307void DataTypeFileWriter::writeStructuredTypeCppConstructors(const StructuredType *structuredType,
308 QTextStream &output)
309{
310 output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
311 << "()"
312 << Util::lineBreak();
313 output << " : data(new " << m_prefix << structuredType->name() << "Data)\n";
314 output << "{"
315 << Util::lineBreak();
316 output << "}"
317 << Util::lineBreak(n: 2);
318
319 output << "/*!"
320 << Util::lineBreak();
321 output << " Constructs a " << structuredType->name() << " from \\a rhs"
322 << Util::lineBreak();
323 output << "*/"
324 << Util::lineBreak();
325 output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
326 << "(const " << m_prefix << structuredType->name() << " &rhs)"
327 << Util::lineBreak();
328 output << " : data(rhs.data)"
329 << Util::lineBreak();
330 output << "{"
331 << Util::lineBreak();
332 output << "}"
333 << Util::lineBreak(n: 2);
334}
335
336void DataTypeFileWriter::writeStructuredTypeCppOperatorAssignment(
337 const StructuredType *structuredType, QTextStream &output)
338{
339 output << "/*!"
340 << Util::lineBreak();
341 output << " Sets the values from \\a rhs in this " << structuredType->name() << Util::lineBreak();
342 output << "*/"
343 << Util::lineBreak();
344 output << m_prefix << structuredType->name() << " &" << m_prefix << structuredType->name()
345 << "::operator=(const " << m_prefix << structuredType->name() << " &rhs)"
346 << Util::lineBreak();
347 output << "{"
348 << Util::lineBreak();
349 output << Util::indent(level: 1) << "if (this != &rhs)"
350 << Util::lineBreak();
351 output << Util::indent(level: 2) << "data.operator=(rhs.data);"
352 << Util::lineBreak();
353 output << Util::indent(level: 1) << "return *this;"
354 << Util::lineBreak();
355 output << "}"
356 << Util::lineBreak(n: 2);
357}
358
359void DataTypeFileWriter::writeStructuredTypeCppOperatorEquality(const StructuredType *structuredType,
360 QTextStream &output)
361{
362 output << "/*!"
363 << Util::lineBreak();
364 output << Util::indent(level: 1) << "Returns \\c true if this " << structuredType->name()
365 << " has the same value as \\a rhs"
366 << Util::lineBreak();
367 output << "*/"
368 << Util::lineBreak();
369
370 output << "bool " << m_prefix << structuredType->name() << "::operator==(const " << m_prefix
371 << structuredType->name() << " &rhs) const"
372 << Util::lineBreak();
373 output << "{"
374 << Util::lineBreak();
375 QList<Field *> unionSwitchfield;
376 QList<Field *> arrayLengthfields;
377
378 const auto typeFields = structuredType->fields();
379
380 for (const auto &possibleMember : typeFields) {
381 for (const auto &field : typeFields) {
382 if (possibleMember->name() == field->lengthField()
383 && !arrayLengthfields.contains(t: possibleMember))
384 arrayLengthfields.push_back(t: possibleMember);
385 if (possibleMember->name() == field->switchField()
386 && !unionSwitchfield.contains(t: possibleMember) && field->isUnion())
387 unionSwitchfield.push_back(t: possibleMember);
388 }
389 }
390
391 if (!unionSwitchfield.isEmpty()) {
392 const auto switchField = unionSwitchfield.first()->lowerFirstName();
393 output << Util::indent(level: 1) << "if (data->" << switchField << " != " << " rhs.data->" << switchField << ")" << Util::lineBreak();
394 output << Util::indent(level: 2) << "return false;" << Util::lineBreak(n: 2);
395 }
396
397 if (structuredType->containsBitMask()) {
398 for (const auto &field : typeFields) {
399 if (!field->switchField().isEmpty() && !arrayLengthfields.contains(t: field)) {
400 const auto switchField = Util::lowerFirstLetter(temp: field->switchField());
401 output << Util::indent(level: 1) << "if (data->" << switchField << " != rhs.data->" << switchField << " || (data->" << switchField << " && ";
402 output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
403 output << "))";
404 output << Util::lineBreak() << Util::indent(level: 2) << "return false;" << Util::lineBreak();
405 }
406 }
407 output << Util::lineBreak();
408 }
409
410 auto counterGeneratedTypes = 0;
411 if (structuredType->hasUnion()) {
412 for (const auto &field : typeFields) {
413 if (!arrayLengthfields.contains(t: field) && !unionSwitchfield.contains(t: field) && !field->isInStructuredTypeBitMask()) {
414 if (structuredType->containsBitMask() && !field->switchField().isEmpty())
415 continue;
416
417
418 output << Util::indent(level: 1) << "if (static_cast<qint32>(data->" << unionSwitchfield.first()->lowerFirstName() << ") == " << field->switchValue() << " && ";
419 output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
420 output << ")" << Util::lineBreak();
421 output << Util::indent(level: 2) << "return false;" << Util::lineBreak(n: 2);
422 }
423 }
424
425 output << Util::indent(level: 1) << "return true;" << Util::lineBreak();
426 } else {
427 output << Util::indent(level: 1) << "return ";
428
429 for (const auto &field : typeFields) {
430 if (!arrayLengthfields.contains(t: field) && !unionSwitchfield.contains(t: field) && !field->isInStructuredTypeBitMask()) {
431 if (structuredType->containsBitMask() && !field->switchField().isEmpty())
432 continue;
433
434 const auto memberName = field->lowerFirstName();
435 if (counterGeneratedTypes != 0)
436 output << Util::lineBreak() << Util::indent(level: 2) << " && ";
437 output << "data->" << memberName << " == rhs.data->" << memberName;
438 counterGeneratedTypes++;
439 }
440 }
441
442 output << ";" << Util::lineBreak();
443 }
444
445 output << "}"
446 << Util::lineBreak(n: 2);
447}
448
449void DataTypeFileWriter::writeStructuredTypeCppQVariant(const StructuredType *structuredType,
450 QTextStream &output)
451{
452 output << "/*!"
453 << Util::lineBreak();
454 output << " Converts this " << structuredType->name() << " to \\l QVariant"
455 << Util::lineBreak();
456 output << "*/"
457 << Util::lineBreak();
458
459 output << m_prefix << structuredType->name() << "::operator QVariant() const"
460 << Util::lineBreak();
461 output << "{"
462 << Util::lineBreak();
463 output << Util::indent(level: 1) << "return QVariant::fromValue(*this);"
464 << Util::lineBreak();
465 output << "}"
466 << Util::lineBreak(n: 2);
467}
468
469void DataTypeFileWriter::writeStructuredTypeCppDestructor(const StructuredType *structuredType,
470 QTextStream &output)
471{
472 output << m_prefix << structuredType->name() << "::~" << m_prefix << structuredType->name()
473 << "()"
474 << Util::lineBreak();
475 output << "{"
476 << Util::lineBreak();
477 output << "}"
478 << Util::lineBreak(n: 2);
479}
480
481void DataTypeFileWriter::writeStructuredTypeCppGetterSetter(const StructuredType *structuredType,
482 QTextStream &output)
483{
484 writeStructuredTypeCppGetter(structuredType, output);
485}
486
487void DataTypeFileWriter::writeStructuredTypeCppGetter(const StructuredType *structuredType,
488 QTextStream &output)
489{
490 QList<Field *> unionMember;
491
492 const auto typeFields = structuredType->fields();
493
494 if (structuredType->hasUnion()) {
495 for (const auto &possibleUnionMember : typeFields) {
496 for (const auto &field : typeFields) {
497 if (field->isUnion()) {
498 if (field->switchField() == possibleUnionMember->name()
499 && !unionMember.contains(t: possibleUnionMember))
500 unionMember.push_back(t: possibleUnionMember);
501 }
502 }
503 }
504 }
505 QList<Field *> arrayLengthfields;
506 for (const auto &possibleArrayLengthField : typeFields) {
507 for (const auto &field : typeFields) {
508 if (possibleArrayLengthField->name() == field->lengthField()
509 && !arrayLengthfields.contains(t: possibleArrayLengthField))
510 arrayLengthfields.push_back(t: possibleArrayLengthField);
511 }
512 }
513
514 QList<Field *> switchFields;
515 if (structuredType->hasSwitchfield()) {
516 for (const auto &possibleOptionalMember : typeFields) {
517 for (const auto &field : typeFields) {
518 if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
519 switchFields.push_back(t: field);
520 }
521 }
522 }
523
524 for (const auto &field : typeFields) {
525 if (!arrayLengthfields.contains(t: field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(t: field))) {
526 const auto tmpFunctionName = field->lowerFirstName();
527
528 output << "/*!\n";
529 output << Util::indent(level: 1) << "Returns the field " << field->name() << " of this " << structuredType->name() << " object"
530 << Util::lineBreak();
531 output << "*/\n";
532
533 const auto typeName = field->typeNameSecondPart();
534 if (!unionMember.contains(t: field)) {
535 if (field->needContainer())
536 output << "QList<";
537 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName())) {
538 output << StringIdentifier::typeNameDataTypeConverter.value(key: field->typeName());
539 } else {
540 bool isPreCoded = false;
541 for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
542 if (preCodedType.contains(name: typeName)) {
543 isPreCoded = true;
544 if (preCodedType.deEncoderName().isEmpty())
545 output << preCodedType.className();
546 else
547 output << preCodedType.deEncoderName();
548 break;
549 }
550 }
551 if (!isPreCoded) {
552 bool isEnum = false;
553 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
554 if (enumeratedType->name() == field->typeName().split(sep: ':'_L1).at(i: 1))
555 isEnum = true;
556 }
557 if (isEnum)
558 output << m_prefix << "::" << field->typeName().split(sep: ':'_L1).at(i: 1);
559 else
560 output << m_prefix << typeName;
561 }
562 }
563 if (field->needContainer())
564 output << ">";
565 output << " ";
566
567 } else {
568 output << m_prefix << structuredType->name() << "::" << field->name() << " ";
569 }
570
571 output << m_prefix << structuredType->name() << "::" << tmpFunctionName << "() const"
572 << Util::lineBreak();
573 output << "{"
574 << Util::lineBreak();
575 if (!field->isUnion()) {
576 if (field->recursive())
577 if (field->needContainer()) {
578 output << Util::indent(level: 1) << "return data->" << tmpFunctionName << ";"
579 << Util::lineBreak();
580 } else {
581 output << Util::indent(level: 1) << "return data->" << tmpFunctionName << ".value_or(" << m_prefix
582 << field->typeName().split(sep: QChar::fromLatin1(c: ':')).at(i: 1) << "()"
583 << ");"
584 << Util::lineBreak();
585 }
586 else
587 output << Util::indent(level: 1) << "return data->" << tmpFunctionName << ";"
588 << Util::lineBreak();
589 } else {
590 const auto switchfieldToLower = Util::lowerFirstLetter(temp: field->switchField());
591 output << Util::indent(level: 1) << "if (data->" << switchfieldToLower << " == " << m_prefix
592 << structuredType->name() << "::" << field->switchField()
593 << "::" << field->name() << ")"
594 << Util::lineBreak();
595 output << Util::indent(level: 2) << "return data->" << tmpFunctionName << ";"
596 << Util::lineBreak();
597 output << Util::indent(level: 1) << "else"
598 << Util::lineBreak();
599 output << Util::indent(level: 2) << "return {};" << Util::lineBreak();
600 }
601 output << "}"
602 << Util::lineBreak(n: 2);
603 if (!unionMember.contains(t: field))
604 writeStructuredTypeCppSetter(field, structuredType, output);
605 }
606 }
607}
608
609void DataTypeFileWriter::writeStructuredTypeCppSetter(const Field *field,
610 const StructuredType *structuredType,
611 QTextStream &output)
612{
613 output << "/*!"
614 << Util::lineBreak();
615 output << Util::indent(level: 1) << "Sets the field " << field->name() << " of this " << structuredType->name() << " object to \\a "
616 << field->lowerFirstName() << Util::lineBreak();
617 output << "*/"
618 << Util::lineBreak();
619
620 output << "void " << m_prefix << structuredType->name() << "::set" << field->name() << "(";
621 const auto tmpFunctionName = field->lowerFirstName();
622 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName())) {
623 if (field->typeName().contains(s: StringIdentifier::booleanIdentifier)
624 || field->typeName().contains(s: StringIdentifier::integerIdentifier)
625 || field->typeName().contains(s: StringIdentifier::floatIdentifier)
626 || field->typeName().contains(s: StringIdentifier::doubleIdentifier)) {
627 if (field->needContainer())
628 output << "QList<"
629 << StringIdentifier::typeNameDataTypeConverter.value(key: field->typeName())
630 << "> ";
631 else
632 output << StringIdentifier::typeNameDataTypeConverter.value(key: field->typeName())
633 << " ";
634 } else {
635 output << "const ";
636 if (field->needContainer())
637 output << "QList<"
638 << StringIdentifier::typeNameDataTypeConverter.value(key: field->typeName())
639 << "> &";
640 else
641 output << StringIdentifier::typeNameDataTypeConverter.value(key: field->typeName())
642 << " &";
643 }
644 } else {
645 const auto typeName = field->typeNameSecondPart();
646 bool isPreCoded = false;
647 for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
648 if (preCodedType.contains(name: typeName)) {
649 isPreCoded = true;
650 output << "const ";
651 if (field->needContainer())
652 output << "QList<";
653 if (preCodedType.deEncoderName().isEmpty())
654 output << preCodedType.className();
655 else
656 output << preCodedType.deEncoderName();
657 if (field->needContainer())
658 output << ">";
659 output << " &";
660 break;
661 }
662 }
663
664 if (!isPreCoded) {
665 bool isEnum = false;
666 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
667 if (enumeratedType->name() == field->typeName().split(sep: ':'_L1).at(i: 1))
668 isEnum = true;
669 }
670 output << "const ";
671 if (field->needContainer())
672 output << "QList <" << m_prefix << typeName << "> &";
673 else if (isEnum)
674 output << m_prefix << "::" << field->typeName().split(sep: ':'_L1).at(i: 1) << " &";
675 else
676 output << m_prefix << typeName << " &";
677 }
678 }
679 output << tmpFunctionName << ")"
680 << Util::lineBreak();
681 output << "{"
682 << Util::lineBreak();
683 output << Util::indent(level: 1) << "data->" << tmpFunctionName << " = " << tmpFunctionName << ";"
684 << Util::lineBreak();
685 if (field->isUnion()) {
686 const auto switchfieldToLower = Util::lowerFirstLetter(temp: field->switchField());
687 output << Util::indent(level: 1) << "data->" << switchfieldToLower << " = " << m_prefix << structuredType->name()
688 << "::" << field->switchField() << "::" << field->name() << ";"
689 << Util::lineBreak();
690 }
691 output << "}"
692 << Util::lineBreak(n: 2);
693}
694
695void DataTypeFileWriter::writeStructuredTypeHeader(const StructuredType *structuredType,
696 QTextStream &output)
697{
698 output << "#pragma once"
699 << Util::lineBreak();
700
701 writeStructuredTypeHeaderIncludes(structuredType, output);
702
703 output << "class " << m_prefix << structuredType->name() << Util::lineBreak();
704 output << "{"
705 << Util::lineBreak();
706 output << "public:"
707 << Util::lineBreak();
708
709 writeStructuredTypeHeaderUnion(structuredType, output);
710
711 output << Util::indent(level: 1) << m_prefix << structuredType->name() << "();"
712 << Util::lineBreak();
713 output << Util::indent(level: 1) << m_prefix << structuredType->name() << "(const " << m_prefix
714 << structuredType->name() << " &);"
715 << Util::lineBreak();
716 output << Util::indent(level: 1) << m_prefix << structuredType->name() << " &operator=(const " << m_prefix
717 << structuredType->name() << " &rhs);"
718 << Util::lineBreak();
719 output << Util::indent(level: 1) << "bool operator==(const " << m_prefix << structuredType->name() << " &rhs) const;"
720 << Util::lineBreak();
721 output << Util::indent(level: 1) << "inline bool operator!=(const " << m_prefix << structuredType->name() << " &rhs) const"
722 << Util::lineBreak(n: 1)
723 << Util::indent(level: 2) << "{ return !(*this == rhs); }"
724 << Util::lineBreak();
725 output << Util::indent(level: 1) << "operator QVariant() const;"
726 << Util::lineBreak();
727 output << Util::indent(level: 1) << "~" << m_prefix << structuredType->name() << "();"
728 << Util::lineBreak(n: 2);
729
730 writeStructuredTypeHeaderGetterSetter(structuredType, output);
731
732 writeStructuredTypeHeaderDebug(structuredType, output);
733
734 output << "private:"
735 << Util::lineBreak();
736 output << Util::indent(level: 1) << "QSharedDataPointer<" << m_prefix << structuredType->name() << "Data> data;"
737 << Util::lineBreak();
738 output << "};"
739 << Util::lineBreak(n: 2);
740 output << "Q_DECLARE_METATYPE(" << m_prefix << structuredType->name() << ")"
741 << Util::lineBreak();
742}
743
744void DataTypeFileWriter::writeStructuredTypeHeaderIncludes(const StructuredType *structuredType,
745 QTextStream &output)
746{
747 QList<QString> qtCoreIncludes{u"#include <QSharedData>\n"_s,
748 u"#include <QVariant>\n"_s};
749 QList<QString> qtOpcUaIncludes{};
750 QList<QString> qtClassIncludes;
751 QList<QString> qtPredifinedClasses;
752
753 const auto typeFields = structuredType->fields();
754
755 for (const auto &field : typeFields) {
756 const auto typeName = field->typeNameSecondPart();
757 QString include;
758 if (field->typeName().startsWith(s: u"%1:"_s.arg(a: StringIdentifier::binaryTypeIdentifier))) {
759 if (typeName == StringIdentifier::datetimeIdentifier) {
760 include = u"#include <QDateTime>\n"_s;
761 } else if (typeName == StringIdentifier::byteStringIdentifier) {
762 include = u"#include <QByteArray>\n"_s;
763 } else if (typeName == StringIdentifier::charArrayIdentifier) {
764 include = u"#include <QString>\n"_s;
765 } else if (typeName == StringIdentifier::guidIdentifier) {
766 include = u"#include <QUuid>\n"_s;
767 }
768
769 if (!qtCoreIncludes.contains(str: include)) {
770 qtCoreIncludes.push_back(t: include);
771 }
772 } else {
773 bool isPrecoded = false;
774 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
775 if (precodedType.contains(name: typeName)) {
776 isPrecoded = true;
777 if (!precodedType.filename().isEmpty()) {
778 include = u"#include <QtOpcUa/%1>\n"_s
779 .arg(a: precodedType.filename());
780 break;
781 }
782 }
783 }
784 if (typeName != structuredType->name()) {
785 if (!isPrecoded) {
786 bool isEnum = false;
787 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
788 if (enumeratedType->name() == typeName)
789 isEnum = true;
790 }
791 if (isEnum) {
792 if (!qtClassIncludes.contains(
793 str: u"#include \"%1enumerations.h\"\n"_s
794 .arg(a: m_prefix.toLower())))
795 qtClassIncludes.push_back(
796 t: u"#include \"%1enumerations.h\"\n"_s
797 .arg(a: m_prefix.toLower()));
798
799 } else {
800 bool isMutualInclude = false;
801 for (const auto &type : std::as_const(t&: m_generateMapping)) {
802 StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(
803 type);
804 if (tempStructuredType) {
805 if (tempStructuredType == structuredType)
806 continue;
807 const auto tempFields = tempStructuredType->fields();
808 for (const auto &tempField : tempFields) {
809 const auto tempTypeName = tempField->typeNameSecondPart();
810 if (typeName == tempStructuredType->name()
811 && tempTypeName == structuredType->name()) {
812 isMutualInclude = true;
813 if (!qtPredifinedClasses.contains(
814 str: u"%1%2"_s.arg(args&: m_prefix, args: tempStructuredType->name())))
815 qtPredifinedClasses.push_back(
816 t: u"%1%2"_s.arg(args&: m_prefix, args: tempStructuredType->name()));
817 }
818 }
819 }
820 }
821
822 if (!qtClassIncludes.contains(
823 str: u"#include \"%1%2.h\"\n"_s
824 .arg(args: m_prefix.toLower(), args: typeName.toLower()))
825 && !isMutualInclude
826 && typeName != StringIdentifier::xmlElementIdentifier)
827 qtClassIncludes.push_back(
828 t: u"#include \"%1%2.h\"\n"_s
829 .arg(args: m_prefix.toLower(), args: typeName.toLower()));
830 }
831 } else {
832 if (!qtOpcUaIncludes.contains(str: include)) {
833 qtOpcUaIncludes.push_back(t: include);
834 }
835 }
836 }
837 }
838 if (field->needContainer()) {
839 include = u"#include <QList>\n"_s;
840 if (!qtCoreIncludes.contains(str: include)) {
841 qtCoreIncludes.push_back(t: include);
842 }
843 }
844 }
845 if (!qtClassIncludes.isEmpty()) {
846 std::sort(first: qtClassIncludes.begin(), last: qtClassIncludes.end());
847 for (const auto &include : qtClassIncludes) {
848 output << include;
849 }
850 output << Util::lineBreak();
851 }
852 std::sort(first: qtOpcUaIncludes.begin(), last: qtOpcUaIncludes.end());
853 for (const auto &include : qtOpcUaIncludes) {
854 output << include;
855 }
856 output << Util::lineBreak();
857 std::sort(first: qtCoreIncludes.begin(), last: qtCoreIncludes.end());
858 for (const auto &include : qtCoreIncludes) {
859 output << include;
860 }
861 output << Util::lineBreak();
862
863 if (!qtPredifinedClasses.isEmpty()) {
864 std::sort(first: qtPredifinedClasses.begin(), last: qtPredifinedClasses.end());
865 for (const auto &predefinedClass : qtPredifinedClasses) {
866 output << "class " << predefinedClass << ";"
867 << Util::lineBreak();
868 }
869 }
870
871 output << "class " << m_prefix << structuredType->name() << "Data;"
872 << Util::lineBreak();
873}
874
875void DataTypeFileWriter::writeStructuredTypeHeaderUnion(const StructuredType *structuredType,
876 QTextStream &output)
877{
878 if (structuredType->hasUnion()) {
879 QList<Field *> unions;
880 QList<Field *> switchFields;
881
882 const auto typeFields = structuredType->fields();
883
884 for (const auto &field : typeFields) {
885 if (field->isUnion())
886 switchFields.push_back(t: field);
887 }
888 for (const auto &switchField : switchFields) {
889 for (const auto &structuredTypeField : typeFields) {
890 if (switchField->switchField() == structuredTypeField->name()) {
891 if (!unions.contains(t: structuredTypeField))
892 unions.push_back(t: structuredTypeField);
893 }
894 }
895 }
896 QList<Field *> arrayLengthfields;
897 for (const auto &possibleArrayLengthField : typeFields) {
898 for (const auto &field : typeFields) {
899 if (possibleArrayLengthField->name() == field->lengthField()
900 && !arrayLengthfields.contains(t: possibleArrayLengthField))
901 arrayLengthfields.push_back(t: possibleArrayLengthField);
902 }
903 }
904 for (const auto &field : unions) {
905 output << Util::indent(level: 1) << "enum class " << field->name() << " {"
906 << Util::lineBreak();
907 output << Util::indent(level: 2) << "None = 0,\n";
908 for (int i = 0; i < switchFields.size(); i++) {
909 const int nextField = i + 1;
910 if (!arrayLengthfields.contains(t: switchFields.at(i))) {
911 if (nextField >= switchFields.size()) {
912 if (switchFields.at(i)->switchField() == field->name()) {
913 output << Util::indent(level: 2) << switchFields.at(i)->name() << " = "
914 << switchFields.at(i)->switchValue() << Util::lineBreak();
915 output << Util::indent(level: 1) << "};"
916 << Util::lineBreak(n: 2);
917 }
918 } else {
919 if (field->name() == switchFields.at(i: nextField)->switchField()) {
920 output << Util::indent(level: 2) << switchFields.at(i)->name() << " = "
921 << switchFields.at(i)->switchValue() << ","
922 << Util::lineBreak();
923 } else {
924 output << Util::indent(level: 2) << switchFields.at(i)->name() << " = "
925 << switchFields.at(i)->switchValue() << Util::lineBreak();
926 output << Util::indent(level: 1) << "};"
927 << Util::lineBreak(n: 2);
928 }
929 }
930 }
931 }
932 }
933 }
934}
935
936void DataTypeFileWriter::writeStructuredTypeHeaderGetterSetter(const StructuredType *structuredType,
937 QTextStream &output)
938{
939 QList<Field *> unionMember;
940 QList<Field *> arrayLengthfields;
941 QList<Field *> switchFields;
942
943 const auto typeFields = structuredType->fields();
944
945 for (const auto &possibleMember : typeFields) {
946 for (const auto &field : typeFields) {
947 if (possibleMember->name() == field->lengthField()
948 && !arrayLengthfields.contains(t: possibleMember))
949 arrayLengthfields.push_back(t: possibleMember);
950 if (field->isUnion() && field->switchField() == possibleMember->name()
951 && !unionMember.contains(t: possibleMember))
952 unionMember.push_back(t: possibleMember);
953 if (!possibleMember->switchField().isEmpty() && field->name() == possibleMember->switchField())
954 switchFields.push_back(t: field);
955 }
956 }
957 for (const auto &field : typeFields) {
958 if (!arrayLengthfields.contains(t: field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(t: field))) {
959 const auto typeName = field->typeNameSecondPart();
960 const auto tmpFunctionName = field->lowerFirstName();
961 if (!unionMember.contains(t: field)) {
962 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName())) {
963 if (field->typeName().contains(s: StringIdentifier::booleanIdentifier)
964 || field->typeName().contains(s: StringIdentifier::integerIdentifier)
965 || field->typeName().contains(s: StringIdentifier::floatIdentifier)
966 || field->typeName().contains(s: StringIdentifier::doubleIdentifier)) {
967 if (!field->needContainer()) {
968 output << Util::indent(level: 1)
969 << StringIdentifier::typeNameDataTypeConverter.value(
970 key: field->typeName())
971 << " " << tmpFunctionName << "() const;"
972 << Util::lineBreak();
973 output << Util::indent(level: 1) << "void set" << field->name() << "("
974 << StringIdentifier::typeNameDataTypeConverter.value(
975 key: field->typeName())
976 << " " << tmpFunctionName << ");"
977 << Util::lineBreak(n: 2);
978 } else {
979 output << Util::indent(level: 1) << "QList<"
980 << StringIdentifier::typeNameDataTypeConverter.value(
981 key: field->typeName())
982 << "> " << tmpFunctionName << "() const;"
983 << Util::lineBreak();
984 output << Util::indent(level: 1) << "void set" << field->name() << "(QList<"
985 << StringIdentifier::typeNameDataTypeConverter.value(
986 key: field->typeName())
987 << "> " << tmpFunctionName << ");"
988 << Util::lineBreak(n: 2);
989 }
990 } else {
991 if (!field->needContainer()) {
992 output << Util::indent(level: 1)
993 << StringIdentifier::typeNameDataTypeConverter.value(
994 key: field->typeName())
995 << " " << tmpFunctionName << "() const;"
996 << Util::lineBreak();
997 output << Util::indent(level: 1) << "void set" << field->name() << "(const "
998 << StringIdentifier::typeNameDataTypeConverter.value(
999 key: field->typeName())
1000 << " &" << tmpFunctionName << ");"
1001 << Util::lineBreak(n: 2);
1002 } else {
1003 output << Util::indent(level: 1) << "QList<"
1004 << StringIdentifier::typeNameDataTypeConverter.value(
1005 key: field->typeName())
1006 << "> " << tmpFunctionName << "() const;"
1007 << Util::lineBreak();
1008 output << Util::indent(level: 1) << "void set" << field->name() << "(const QList<"
1009 << StringIdentifier::typeNameDataTypeConverter.value(
1010 key: field->typeName())
1011 << "> &" << tmpFunctionName << ");"
1012 << Util::lineBreak(n: 2);
1013 }
1014 }
1015 } else {
1016 bool isPreCoded = false;
1017 for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
1018 if (preCodedType.contains(name: typeName)) {
1019 isPreCoded = true;
1020 if (preCodedType.deEncoderName().isEmpty()) {
1021 output << Util::indent(level: 1);
1022 if (field->needContainer())
1023 output << "QList<";
1024 output << preCodedType.className();
1025 if (field->needContainer())
1026 output << ">";
1027 output << " " << tmpFunctionName << "() const;"
1028 << Util::lineBreak();
1029 output << Util::indent(level: 1) << "void set" << field->name() << "(const ";
1030 if (field->needContainer())
1031 output << "QList<";
1032 output << preCodedType.className();
1033 if (field->needContainer())
1034 output << ">";
1035 output << " &" << tmpFunctionName << ");"
1036 << Util::lineBreak(n: 2);
1037 } else {
1038 output << Util::indent(level: 1);
1039 if (field->needContainer())
1040 output << "QList<";
1041 output << preCodedType.deEncoderName();
1042 if (field->needContainer())
1043 output << ">";
1044 output << " " << tmpFunctionName << "() const;"
1045 << Util::lineBreak();
1046 output << Util::indent(level: 1) << "void set" << field->name() << "(const ";
1047 if (field->needContainer())
1048 output << "QList<";
1049 output << preCodedType.deEncoderName();
1050 if (field->needContainer())
1051 output << ">";
1052 output << " &" << tmpFunctionName << ");"
1053 << Util::lineBreak(n: 2);
1054 }
1055 break;
1056 }
1057 }
1058
1059 if (!isPreCoded) {
1060 if (field->needContainer()) {
1061 output << Util::indent(level: 1) << "QList <" << m_prefix << typeName << "> "
1062 << tmpFunctionName << "() const;"
1063 << Util::lineBreak();
1064 output << Util::indent(level: 1) << "void set" << field->name() << "(const QList <"
1065 << m_prefix << typeName << "> &" << tmpFunctionName
1066 << ");"
1067 << Util::lineBreak(n: 2);
1068 } else {
1069 bool isEnum = false;
1070 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
1071 if (enumeratedType->name() == field->typeName().split(sep: ':'_L1).at(i: 1))
1072 isEnum = true;
1073 }
1074 if (isEnum) {
1075 output << Util::indent(level: 1) << m_prefix << "::" << typeName << " "
1076 << tmpFunctionName << "() const;"
1077 << Util::lineBreak();
1078 output << Util::indent(level: 1) << "void set" << field->name() << "(const " << m_prefix
1079 << "::" << typeName << " &" << tmpFunctionName << ");"
1080 << Util::lineBreak(n: 2);
1081 } else {
1082 output << Util::indent(level: 1) << m_prefix << typeName << " "
1083 << tmpFunctionName << "() const;"
1084 << Util::lineBreak();
1085 output << Util::indent(level: 1) << "void set" << field->name() << "(const " << m_prefix
1086 << typeName << " &" << tmpFunctionName << ");"
1087 << Util::lineBreak(n: 2);
1088 }
1089 }
1090 }
1091 }
1092 } else { // is union switchfield, create just getter
1093 output << Util::indent(level: 1) << field->name() << " " << tmpFunctionName << "() const;"
1094 << Util::lineBreak();
1095 output << Util::lineBreak();
1096 }
1097 }
1098 }
1099}
1100
1101void DataTypeFileWriter::writeStructuredTypeHeaderDebug(const StructuredType *structuredType, QTextStream &output)
1102{
1103 output << Util::indent(level: 1) << "friend QDebug operator<<(QDebug debug, const " << m_prefix << structuredType->name() << " &v);";
1104 output << Util::lineBreak(n: 2);
1105}
1106
1107void DataTypeFileWriter::writeStructuredTypeCppDebug(const StructuredType *structuredType, QTextStream &output)
1108{
1109 const auto typeName = u"%1%2"_s.arg(args&: m_prefix, args: structuredType->name());
1110
1111 output << "/*!" << Util::lineBreak();
1112 output << Util::indent(level: 1) << "Prints the field values of object \\a v to \\a debug" << Util::lineBreak();
1113 output << "*/" << Util::lineBreak();
1114 output << "QDebug operator<<(QDebug debug, const " << typeName << " &v)" << Util::lineBreak();
1115 output << "{" << Util::lineBreak();
1116 output << Util::indent(level: 1) << "QDebugStateSaver saver(debug);" << Util::lineBreak();
1117 output << Util::indent(level: 1) << "debug.nospace().noquote();" << Util::lineBreak(n: 2);
1118 output << Util::indent(level: 1) << "debug << \"" << typeName << " (\";" << Util::lineBreak(n: 2);
1119
1120 if (!structuredType->hasUnion() && structuredType->hasSwitchfield())
1121 output << Util::indent(level: 1) << "bool firstFieldPrinted = false;" << Util::lineBreak(n: 2);
1122
1123 bool isFirst = true;
1124
1125 QSet<Field *> lengthFields;
1126
1127 const auto typeFields = structuredType->fields();
1128 for (const auto &field : typeFields) {
1129 for (const auto &innerField : typeFields) {
1130 if (!field->lengthField().isEmpty() && field->lengthField() == innerField->name())
1131 lengthFields.insert(value: innerField);
1132 }
1133 }
1134
1135 bool parameterUsed = false;
1136
1137 for (const auto &field : typeFields) {
1138 if (structuredType->hasUnion() && field == typeFields.constFirst())
1139 continue;
1140
1141 if (field->isInStructuredTypeBitMask())
1142 continue;
1143
1144 if (lengthFields.contains(value: field))
1145 continue;
1146
1147 if (structuredType->hasUnion())
1148 output << Util::indent(level: 1) << "if (static_cast<quint32>(v." << Util::lowerFirstLetter(temp: field->switchField()) << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
1149 else if (!field->switchField().isEmpty())
1150 output << Util::indent(level: 1) << "if (v." << Util::lowerFirstLetter(temp: field->switchField()) << "()) {" << Util::lineBreak();
1151
1152 int indentOffset = 0;
1153 if (structuredType->hasUnion() || !field->switchField().isEmpty())
1154 indentOffset = 1;
1155
1156 if (!structuredType->hasUnion() && !isFirst) {
1157 if (structuredType->hasSwitchfield())
1158 output << Util::indent(level: 1 + indentOffset) << "if (firstFieldPrinted)" << Util::lineBreak();
1159 output << Util::indent(level: 1 + (structuredType->hasSwitchfield() ? indentOffset + 1 : 0)) << "debug << \", \";" << Util::lineBreak();
1160 }
1161
1162 output << Util::indent(level: 1 + indentOffset) << "debug << \"" << field->name() << ": \";" << Util::lineBreak();
1163
1164 // Not all types have an operator<< for QDebug...
1165 const auto isPrecoded = std::find_if(first: StringIdentifier::opcUaPrecodedTypes.constBegin(), last: StringIdentifier::opcUaPrecodedTypes.constEnd(),
1166 pred: [&](const auto &entry) { return entry.name() == field->typeNameSecondPart(); });
1167
1168 if (isPrecoded == StringIdentifier::opcUaPrecodedTypes.constEnd() || StringIdentifier::precodedTypesWithDebugOperator.contains(value: field->typeNameSecondPart())) {
1169 output << Util::indent(level: 1 + indentOffset) << "debug << v." << field->lowerFirstName() << "();" << Util::lineBreak();
1170 parameterUsed = true;
1171 } else {
1172 if (field->needContainer()) {
1173 const auto tempListName = u"%1%2"_s.arg(args: field->lowerFirstName(), args: u"Strings"_s);
1174 output << Util::indent(level: 1 + indentOffset) << "QStringList " << tempListName << ";" << Util::lineBreak();
1175 output << Util::indent(level: 1 + indentOffset) << "using namespace Qt::Literals::StringLiterals;" << Util::lineBreak();
1176 output << Util::indent(level: 1 + indentOffset) << "for (int i = 0; i < v." << field->lowerFirstName() << "().size(); ++i)" << Util::lineBreak();
1177 output << Util::indent(level: 2 + indentOffset) << tempListName << ".push_back(u\"" << isPrecoded->className() << "(...)\"_s" << ");" << Util::lineBreak();
1178 output << Util::indent(level: 1 + indentOffset) << "debug << \"QList(\" << " << tempListName << ".join(u\", \"_s) << \")\";";
1179 parameterUsed = true;
1180 } else {
1181 output << Util::indent(level: 1 + indentOffset) << "debug << \"" << isPrecoded->className() << "(...)\";";
1182 }
1183 output << Util::lineBreak();
1184 }
1185
1186 if (!structuredType->hasUnion() && structuredType->hasSwitchfield() && field != typeFields.constLast())
1187 output << Util::indent(level: 1 + indentOffset) << "firstFieldPrinted = true;" << Util::lineBreak();
1188
1189 if (structuredType->hasUnion() || !field->switchField().isEmpty())
1190 output << Util::indent(level: 1) << "}" << Util::lineBreak();
1191
1192 output << Util::lineBreak();
1193
1194 isFirst = false;
1195 }
1196
1197 if (!parameterUsed)
1198 output << Util::lineBreak() << Util::indent(level: 1) << "Q_UNUSED(v);" << Util::lineBreak(n: 2);
1199
1200 output << Util::indent(level: 1) << "debug << \")\";" << Util::lineBreak();
1201 output << Util::indent(level: 1) << "return debug;" << Util::lineBreak();
1202 output << "}" << Util::lineBreak();
1203}
1204
1205DataTypeFileWriter::GeneratingError DataTypeFileWriter::writeEnumeratedTypes()
1206{
1207 QFile file;
1208 const auto fileName = u"%1enumerations.h"_s.arg(a: m_prefix.toLower());
1209 QDir dir(m_path);
1210 if (!dir.exists(name: m_path))
1211 if (!dir.mkpath(dirPath: m_path))
1212 return UnableToWrite;
1213 file.setFileName(dir.absoluteFilePath(fileName));
1214 if (!file.open(flags: QIODevice::WriteOnly | QIODevice::Text))
1215 return UnableToWrite;
1216 QTextStream output(&file);
1217 writeLicenseHeader(output);
1218
1219 if (!m_generatedEnumeratedTypeFilenames.contains(
1220 str: u"%1enumerations"_s.arg(a: m_prefix.toLower())))
1221 m_generatedEnumeratedTypeFilenames.push_back(
1222 t: u"%1enumerations"_s.arg(a: m_prefix.toLower()));
1223 output << "#pragma once"
1224 << Util::lineBreak();
1225 output << "#include <QMetaType>"
1226 << Util::lineBreak(n: 2);
1227 output << "namespace " << m_prefix << " {"
1228 << Util::lineBreak();
1229 output << "Q_NAMESPACE"
1230 << Util::lineBreak(n: 2);
1231 for (const auto &enumeratedType : std::as_const(t&: m_enumeratedTypes)) {
1232 output << "enum class " << enumeratedType->name() << " {" << Util::lineBreak(n: 1);
1233 const auto tempValues = enumeratedType->values();
1234 for (const auto &enumeratedValue : tempValues) {
1235 output << Util::indent(level: 1) << enumeratedValue->name() << " = " << enumeratedValue->value();
1236 if (!tempValues.endsWith(t: enumeratedValue))
1237 output << ",";
1238 output << Util::lineBreak();
1239 }
1240 output << "};"
1241 << Util::lineBreak();
1242 output << "Q_ENUM_NS(" << enumeratedType->name() << ")"
1243 << Util::lineBreak(n: 2);
1244 }
1245
1246 output << Util::lineBreak();
1247 output << "}"
1248 << Util::lineBreak();
1249
1250 m_generatedHeaderFileNames.push_back(t: fileName);
1251
1252 return GeneratingError::NoError;
1253}
1254
1255DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateFile(const XmlElement *type,
1256 const QString &fileExtension)
1257{
1258 QFile file;
1259 const auto fileName = u"%1%2%3"_s.arg(args: m_prefix.toLower(),
1260 args: type->name().toLower(),
1261 args: fileExtension);
1262 QDir dir(m_path);
1263 if (!dir.exists(name: m_path))
1264 if (!dir.mkpath(dirPath: m_path))
1265 return UnableToWrite;
1266 file.setFileName(dir.absoluteFilePath(fileName));
1267 if (!file.open(flags: QIODevice::WriteOnly | QIODevice::Text))
1268 return UnableToWrite;
1269 QTextStream output(&file);
1270 writeLicenseHeader(output);
1271
1272 auto structuredType = dynamic_cast<const StructuredType *>(type);
1273 if (structuredType) {
1274 if (fileExtension == StringIdentifier::headerIdentifier) {
1275 writeStructuredTypeHeader(structuredType, output);
1276 if (!m_generatedHeaderFileNames.contains(str: fileName))
1277 m_generatedHeaderFileNames.push_back(t: fileName);
1278 } else {
1279 if (fileExtension == StringIdentifier::cppIdentifier) {
1280 writeStructuredTypeCpp(structuredType, output);
1281 if (!m_generatedSourceFileNames.contains(str: fileName))
1282 m_generatedSourceFileNames.push_back(t: fileName);
1283 }
1284 }
1285 }
1286 file.close();
1287 return NoError;
1288}
1289
1290DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateTypes(
1291 const QList<XmlElement *> &types)
1292{
1293 m_generateMapping.append(l: types);
1294 for (const auto &type : std::as_const(t&: m_generateMapping)) {
1295 const auto enumeratedType = dynamic_cast<EnumeratedType *>(type);
1296 if (enumeratedType)
1297 if (!m_enumeratedTypes.contains(t: enumeratedType))
1298 m_enumeratedTypes.append(t: enumeratedType);
1299 }
1300 if (writeEnumeratedTypes() != GeneratingError::NoError) {
1301 return GeneratingError::UnableToWrite;
1302 }
1303
1304 for (const auto &type : std::as_const(t&: m_generateMapping)) {
1305 const auto structuredType = dynamic_cast<StructuredType *>(type);
1306 if (structuredType) {
1307 if (generateFile(type, fileExtension: StringIdentifier::headerIdentifier) != GeneratingError::NoError)
1308 return GeneratingError::UnableToWrite;
1309 if (generateFile(type, fileExtension: StringIdentifier::cppIdentifier) != GeneratingError::NoError)
1310 return GeneratingError::UnableToWrite;
1311 }
1312 }
1313 return NoError;
1314}
1315
1316QList<XmlElement *> DataTypeFileWriter::generateMapping() const
1317{
1318 return m_generateMapping;
1319}
1320
1321void DataTypeFileWriter::setGenerateMapping(const QList<XmlElement *> &generateMapping)
1322{
1323 m_generateMapping = generateMapping;
1324}
1325
1326bool DataTypeFileWriter::writeBundleFiles()
1327{
1328 const auto collectionHeaderName = u"%1datatypes%2"_s
1329 .arg(args: m_prefix.toLower(), args: StringIdentifier::headerIdentifier);
1330 const auto collectionSourceName = u"%1datatypes%2"_s
1331 .arg(args: m_prefix.toLower(), args: StringIdentifier::cppIdentifier);
1332
1333 if (!m_generatedHeaderFileNames.isEmpty()) {
1334 QFile file(QDir(m_path).absoluteFilePath(fileName: collectionHeaderName));
1335 if (!file.open(flags: QFile::WriteOnly))
1336 return false;
1337
1338 QTextStream out(&file);
1339
1340 writeLicenseHeader(output&: out);
1341
1342 out << "#pragma once" << Util::lineBreak(n: 2);
1343
1344 for (const auto &header : std::as_const(t&: m_generatedHeaderFileNames))
1345 out << "#include \"" << header << "\"" << Util::lineBreak();
1346 }
1347
1348 const auto enumHeaderCandidate = u"%1enumerations.h"_s.arg(a: m_prefix.toLower());
1349 const bool hasEnums = m_generatedHeaderFileNames.contains(str: enumHeaderCandidate);
1350
1351 if (!m_generatedSourceFileNames.isEmpty() || hasEnums) {
1352 QFile file(QDir(m_path).absoluteFilePath(fileName: collectionSourceName));
1353 if (!file.open(flags: QFile::WriteOnly))
1354 return false;
1355
1356 QTextStream out(&file);
1357
1358 writeLicenseHeader(output&: out);
1359
1360 if (hasEnums)
1361 out << "#include \"" << u"moc_%1enumerations.cpp"_s.arg(a: m_prefix.toLower()) << "\"" << Util::lineBreak();
1362
1363 for (const auto &source : std::as_const(t&: m_generatedSourceFileNames))
1364 out << "#include \"" << source << "\"" << Util::lineBreak();
1365 }
1366
1367 return true;
1368}
1369

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtopcua/tools/datatypecodegenerator/datatypefilewriter.cpp