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