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 "dependencydatatypevalidator.h"
7#include "enumeratedtype.h"
8#include "enumeratedvalue.h"
9#include "field.h"
10#include "import.h"
11#include "mappingfilegenerator.h"
12#include "recursivedescentparser.h"
13#include "stringidentifier.h"
14#include "structuredtype.h"
15#include "typedictionary.h"
16
17#include <QtCore/qfile.h>
18
19using namespace Qt::Literals::StringLiterals;
20
21RecursiveDescentParser::~RecursiveDescentParser()
22{
23 qDeleteAll(c: m_mapTypeDictionary);
24}
25
26void RecursiveDescentParser::printInOrder() const
27{
28 for (const auto &entry : m_mapTypeDictionary) {
29 entry->print();
30 }
31}
32
33RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedType(
34 QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
35{
36 EnumeratedType *enumeratedType = new EnumeratedType(
37 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::nameIdentifier).toString(),
38 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::lengthInBitsIdentifier).toInt());
39 typeDictionary->addType(type: enumeratedType);
40 xmlStreamReader.readNext();
41 ParsingError error = NoError;
42 while (!xmlStreamReader.atEnd() && error == NoError) {
43 if (xmlStreamReader.isEndElement()) {
44 xmlStreamReader.readNext();
45 return error;
46 }
47 if (xmlStreamReader.isStartElement()) {
48 if (xmlStreamReader.name() == StringIdentifier::enumeratedValueIdentifier)
49 error = parseEnumeratedValue(xmlStreamReader, enumeratedType);
50 else {
51 if (isKnownElement(xmlStreamReader))
52 error = skipKnownElement(xmlStreamReader);
53 else
54 error = InvalidEnumeratedTypeEntry;
55 }
56 } else
57 xmlStreamReader.readNext();
58 }
59 return error;
60}
61
62RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedValue(
63 QXmlStreamReader &xmlStreamReader, EnumeratedType *enumeratedType)
64{
65 const auto name = permittedName(name: xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::nameIdentifier).toString());
66
67 if (name.isEmpty())
68 return ParsingError::InvalidEnumeratedValueEntry;
69
70 EnumeratedValue *enumeratedValue = new EnumeratedValue(
71 name, xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::valueIdentifier).toInt());
72 enumeratedType->addValue(enumeratedValue);
73 ParsingError error = NoError;
74 xmlStreamReader.readNext();
75 while (!xmlStreamReader.atEnd() && error == NoError) {
76 if (xmlStreamReader.isEndElement()) {
77 xmlStreamReader.readNext();
78 return error;
79 } else {
80 if (isKnownElement(xmlStreamReader))
81 error = skipKnownElement(xmlStreamReader);
82 else
83 error = InvalidEnumeratedValueEntry;
84 }
85 }
86 return error;
87}
88
89RecursiveDescentParser::ParsingError RecursiveDescentParser::parseField(
90 QXmlStreamReader &xmlStreamReader, StructuredType *structuredType)
91{
92 const auto name = permittedName(name: xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::nameIdentifier).toString());
93
94 if (name.isEmpty())
95 return ParsingError::InvalidFieldEntry;
96
97 Field *field = new Field(
98 name,
99 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::typeNameIdentifier).toString(),
100 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::lengthFieldIdentifier).toString());
101 if (!field->lengthField().isEmpty())
102 field->setNeedContainer(true);
103
104 if (field->typeName().endsWith(s: u":%1"_s.arg(a: structuredType->name())))
105 field->setRecursive(true);
106
107 if (!xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::lengthIdentifier).toString().isEmpty()) {
108 field->setLength(xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::lengthIdentifier).toUInt());
109 field->setIsInStructuredTypeBitMask(true);
110 }
111 if (!xmlStreamReader.attributes()
112 .value(qualifiedName: StringIdentifier::switchFieldIdentifier)
113 .toString()
114 .isEmpty()) {
115 structuredType->setHasSwitchfield(true);
116 field->setSwitchField(
117 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::switchFieldIdentifier).toString());
118 }
119 if (!xmlStreamReader.attributes()
120 .value(qualifiedName: StringIdentifier::switchValueIdentifier)
121 .toString()
122 .isEmpty()) {
123 structuredType->setHasUnion(true);
124 field->setIsUnion(true);
125 field->setSwitchValue(
126 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::switchValueIdentifier).toInt());
127 }
128 if (field->typeName() == StringIdentifier::opcBitIdentifier)
129 structuredType->setContainsBitMask(true);
130 if (field->typeName().contains(s: StringIdentifier::bitIdentifier)
131 && (field->name().contains(s: StringIdentifier::specifiedIdentifier)
132 || field->name().contains(s: StringIdentifier::reservedIdentifier))
133 && structuredType->containsBitMask())
134 field->setIsInStructuredTypeBitMask(true);
135 structuredType->addField(field);
136 ParsingError error = NoError;
137 xmlStreamReader.readNext();
138 while (!xmlStreamReader.atEnd() && error == NoError) {
139 if (xmlStreamReader.isEndElement()) {
140 xmlStreamReader.readNext();
141 return error;
142 } else {
143 if (isKnownElement(xmlStreamReader))
144 error = skipKnownElement(xmlStreamReader);
145 else
146 error = InvalidFieldEntry;
147 }
148 }
149 return error;
150}
151
152RecursiveDescentParser::ParsingError RecursiveDescentParser::parseFile(
153 const QString &fileName, const bool &dependencyTypeDictionary)
154{
155 ParsingError error = NoError;
156 QFile xmlFile(fileName);
157 if (!xmlFile.open(flags: QIODevice::ReadOnly))
158 return InvalidFileName;
159 QXmlStreamReader xmlStreamReader;
160 xmlStreamReader.setDevice((&xmlFile));
161 while (!xmlStreamReader.atEnd() && error == NoError) {
162 if (xmlStreamReader.isStartElement()) {
163 if (xmlStreamReader.name() == StringIdentifier::typeDictionaryIdentifier)
164 error = parseTypeDictionary(xmlStreamReader, dependencyTypeDictionary);
165 else {
166 if (isKnownElement(xmlStreamReader))
167 error = skipKnownElement(xmlStreamReader);
168 else
169 error = InvalidTypeDictionaryEntry;
170 }
171 } else
172 xmlStreamReader.readNext();
173 }
174 xmlFile.close();
175 return error;
176}
177
178RecursiveDescentParser::ParsingError RecursiveDescentParser::parseImport(
179 QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
180{
181 Import *import = new Import(
182 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::nameSpaceIdentifier).toString(),
183 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::locationIdentifier).toString());
184 typeDictionary->addType(type: import);
185 xmlStreamReader.readNext();
186 ParsingError error = NoError;
187 while (!xmlStreamReader.atEnd() && error == NoError) {
188 if (xmlStreamReader.isEndElement()) {
189 xmlStreamReader.readNext();
190 return error;
191 } else {
192 if (isKnownElement(xmlStreamReader))
193 error = skipKnownElement(xmlStreamReader);
194 else
195 error = InvalidImportEntry;
196 }
197 }
198 return error;
199}
200
201RecursiveDescentParser::ParsingError RecursiveDescentParser::parseStructuredType(
202 QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
203{
204 StructuredType *structuredType = new StructuredType(
205 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::nameIdentifier).toString(),
206 xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::baseTypeIdentifier).toString());
207 if (StringIdentifier::buildInTypesWithBitMask.contains(str: structuredType->name())) {
208 structuredType->setIsBuiltInType(true);
209 structuredType->setContainsBitMask(true);
210 }
211 structuredType->setTargetNamespace(typeDictionary->targetNamespace());
212 typeDictionary->addType(type: structuredType);
213 xmlStreamReader.readNext();
214 ParsingError error = NoError;
215 while (!xmlStreamReader.atEnd() && error == NoError) {
216 if (xmlStreamReader.isEndElement()) {
217 xmlStreamReader.readNext();
218
219 const auto tempFields = structuredType->fields();
220 for (const auto &field : tempFields) {
221 if (field->recursive()) {
222 structuredType->setRecursive(true);
223 break;
224 }
225 }
226 return error;
227 }
228 if (xmlStreamReader.isStartElement()) {
229 if (xmlStreamReader.name() == StringIdentifier::fieldIdentifier)
230 error = parseField(xmlStreamReader, structuredType);
231 else {
232 if (isKnownElement(xmlStreamReader))
233 error = skipKnownElement(xmlStreamReader);
234 else
235 error = InvalidStructuredTypeEntry;
236 }
237 } else
238 xmlStreamReader.readNext();
239 }
240
241 return error;
242}
243
244RecursiveDescentParser::ParsingError RecursiveDescentParser::parseTypeDictionary(
245 QXmlStreamReader &xmlStreamReader, const bool &dependencyTypeDictionary)
246{
247 QMap<QString, QString> namespaces;
248 for (int i = 0; i < xmlStreamReader.namespaceDeclarations().size(); i++)
249 namespaces.insert(key: xmlStreamReader.namespaceDeclarations().at(i).prefix().toString(),
250 value: xmlStreamReader.namespaceDeclarations().at(i).namespaceUri().toString());
251 m_mapTypeDictionary.insert(
252 key: xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::targetNamespaceIdentifier).toString(),
253 value: new TypeDictionary(dependencyTypeDictionary,
254 xmlStreamReader.attributes()
255 .value(qualifiedName: StringIdentifier::defaultByteOrderIdentifier)
256 .toString(),
257 xmlStreamReader.attributes()
258 .value(qualifiedName: StringIdentifier::targetNamespaceIdentifier)
259 .toString(),
260 namespaces));
261 QString targetNamespace
262 = xmlStreamReader.attributes().value(qualifiedName: StringIdentifier::targetNamespaceIdentifier).toString();
263 ParsingError error = NoError;
264 if (!dependencyTypeDictionary && targetNamespace == StringIdentifier::namespaceZeroIdentifier)
265 return CannotFullyGenerateNamespaceZero;
266 xmlStreamReader.readNext();
267 while (!xmlStreamReader.atEnd() && error == NoError) {
268 if (xmlStreamReader.isEndElement()) {
269 xmlStreamReader.readNext();
270 return error;
271 }
272 if (xmlStreamReader.isStartElement()) {
273 if (xmlStreamReader.name() == StringIdentifier::enumeratedTypeIdentifier)
274 error = parseEnumeratedType(xmlStreamReader,
275 typeDictionary: m_mapTypeDictionary.value(key: targetNamespace));
276 else {
277 if (xmlStreamReader.name() == StringIdentifier::structuredTypeIdentifier)
278 error = parseStructuredType(xmlStreamReader,
279 typeDictionary: m_mapTypeDictionary.value(key: targetNamespace));
280 else {
281 if (xmlStreamReader.name() == StringIdentifier::importIdentifier)
282 error = parseImport(xmlStreamReader,
283 typeDictionary: m_mapTypeDictionary.value(key: targetNamespace));
284 else {
285 if (isKnownElement(xmlStreamReader))
286 error = skipKnownElement(xmlStreamReader);
287 else
288 error = InvalidTypeDictionaryEntry;
289 }
290 }
291 }
292 } else
293 xmlStreamReader.readNext();
294 }
295 return error;
296}
297
298RecursiveDescentParser::ParsingError RecursiveDescentParser::skipKnownElement(
299 QXmlStreamReader &xmlStreamReader)
300{
301 ParsingError error = NoError;
302 xmlStreamReader.readNext();
303 while (!xmlStreamReader.atEnd() && error == NoError) {
304 if (xmlStreamReader.isEndElement()) {
305 xmlStreamReader.readNext();
306 return error;
307 }
308 if (xmlStreamReader.isStartElement()) {
309 if (xmlStreamReader.name() == StringIdentifier::documentationIdentifier
310 || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier)
311 skipKnownElement(xmlStreamReader);
312 else
313 error = UnknownEntry;
314 } else
315 xmlStreamReader.readNext();
316 }
317 return error;
318}
319
320RecursiveDescentParser::ParsingError RecursiveDescentParser::generateInputFiles(
321 const QString &path,
322 const QString &prefix,
323 const QString &header,
324 bool generateBundleFiles)
325{
326 DependencyDataTypeValidator dependencyDataTypeValidator;
327 for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
328 if (!it.value()->dependencyTypeDictionary())
329 it.value()->accept(visitor: &dependencyDataTypeValidator);
330 }
331
332 dependencyDataTypeValidator.setReadResolveDependencies(
333 DependencyDataTypeValidator::ResolveDependencies);
334
335 for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
336 it.value()->accept(visitor: &dependencyDataTypeValidator);
337
338 for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
339 it.value()->accept(visitor: &dependencyDataTypeValidator);
340
341 if (dependencyDataTypeValidator.unresolvedDependencyStringList().isEmpty()) {
342 DataTypeFileWriter filewriter(path,
343 prefix,
344 header);
345 for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
346 if (!it.value()->dependencyTypeDictionary())
347 it.value()->accept(visitor: &filewriter);
348 };
349 if (filewriter.generateMapping().isEmpty()) {
350 return UnableToWriteFile;
351 }
352 MappingFileGenerator mappingFileGenerator(filewriter.generateMapping(), path, prefix, header);
353 if (filewriter.generateTypes(types: dependencyDataTypeValidator.resolvedDependencyElementList())
354 != DataTypeFileWriter::GeneratingError::NoError) {
355 return UnableToWriteFile;
356 }
357
358 mappingFileGenerator.addGenerateMapping(generateMapping: dependencyDataTypeValidator.resolvedDependencyElementList());
359
360 if (mappingFileGenerator.generateMapping() != MappingFileGenerator::MappingError::NoError)
361 return UnableToWriteFile;
362
363 if (generateBundleFiles)
364 filewriter.writeBundleFiles();
365 } else {
366 return MissingDependency;
367 }
368
369 return NoError;
370}
371
372bool RecursiveDescentParser::isKnownElement(const QXmlStreamReader &xmlStreamReader)
373{
374 return xmlStreamReader.name() == StringIdentifier::documentationIdentifier
375 || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier;
376}
377
378QString RecursiveDescentParser::permittedName(const QString &name) const
379{
380 if (name.isEmpty())
381 return {};
382
383 auto result = name;
384
385 if (StringIdentifier::illegalNames.contains(str: name)) {
386 result = Util::lowerFirstLetter(temp: name);
387 result = u"_%1"_s.arg(a: name);
388 }
389
390 if (result.contains(c: ' '_L1))
391 result.replace(before: ' '_L1, after: '_'_L1);
392 if (name.contains(c: '-'_L1))
393 result.replace(before: '-'_L1, after: '_'_L1);
394
395 return result;
396}
397

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