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 | |
19 | RecursiveDescentParser::~RecursiveDescentParser() |
20 | { |
21 | qDeleteAll(c: m_mapTypeDictionary); |
22 | } |
23 | |
24 | void RecursiveDescentParser::printInOrder() const |
25 | { |
26 | for (const auto &entry : m_mapTypeDictionary) { |
27 | entry->print(); |
28 | } |
29 | } |
30 | |
31 | RecursiveDescentParser::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 | |
60 | RecursiveDescentParser::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 | |
87 | RecursiveDescentParser::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 | |
150 | RecursiveDescentParser::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 | |
176 | RecursiveDescentParser::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 | |
199 | RecursiveDescentParser::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 | |
241 | RecursiveDescentParser::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 | |
295 | RecursiveDescentParser::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 | |
317 | RecursiveDescentParser::ParsingError RecursiveDescentParser::generateInputFiles( |
318 | const QString &path, |
319 | const QString &prefix, |
320 | const QString &) |
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 | |
365 | bool RecursiveDescentParser::isKnownElement(const QXmlStreamReader &xmlStreamReader) |
366 | { |
367 | return xmlStreamReader.name() == StringIdentifier::documentationIdentifier |
368 | || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier; |
369 | } |
370 | |
371 | QString 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 | |