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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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