Revision control
++ // Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "util.h"
++ #include "datatypefilewriter.h"
++ #include "dependencydatatypevalidator.h"
++ #include "enumeratedtype.h"
++ #include "enumeratedvalue.h"
++ #include "field.h"
++ #include "import.h"
++ #include "mappingfilegenerator.h"
++ #include "recursivedescentparser.h"
++ #include "stringidentifier.h"
++ #include "structuredtype.h"
++ #include "typedictionary.h"
++
++ #include <QtCore/qfile.h>
++
++ using namespace Qt::Literals::StringLiterals;
++
++ RecursiveDescentParser::~RecursiveDescentParser()
++ {
++ qDeleteAll(m_mapTypeDictionary);
++ }
++
++ void RecursiveDescentParser::printInOrder() const
++ {
++ for (const auto &entry : m_mapTypeDictionary) {
++ entry->print();
++ }
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedType(
++ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
++ {
++ EnumeratedType *enumeratedType = new EnumeratedType(
++ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
++ xmlStreamReader.attributes().value(StringIdentifier::lengthInBitsIdentifier).toInt());
++ typeDictionary->addType(enumeratedType);
++ xmlStreamReader.readNext();
++ ParsingError error = NoError;
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ }
++ if (xmlStreamReader.isStartElement()) {
++ if (xmlStreamReader.name() == StringIdentifier::enumeratedValueIdentifier)
++ error = parseEnumeratedValue(xmlStreamReader, enumeratedType);
++ else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidEnumeratedTypeEntry;
++ }
++ } else
++ xmlStreamReader.readNext();
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedValue(
++ QXmlStreamReader &xmlStreamReader, EnumeratedType *enumeratedType)
++ {
++ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
++
++ if (name.isEmpty())
++ return ParsingError::InvalidEnumeratedValueEntry;
++
++ EnumeratedValue *enumeratedValue = new EnumeratedValue(
++ name, xmlStreamReader.attributes().value(StringIdentifier::valueIdentifier).toInt());
++ enumeratedType->addValue(enumeratedValue);
++ ParsingError error = NoError;
++ xmlStreamReader.readNext();
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ } else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidEnumeratedValueEntry;
++ }
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseField(
++ QXmlStreamReader &xmlStreamReader, StructuredType *structuredType)
++ {
++ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
++
++ if (name.isEmpty())
++ return ParsingError::InvalidFieldEntry;
++
++ Field *field = new Field(
++ name,
++ xmlStreamReader.attributes().value(StringIdentifier::typeNameIdentifier).toString(),
++ xmlStreamReader.attributes().value(StringIdentifier::lengthFieldIdentifier).toString());
++ if (!field->lengthField().isEmpty())
++ field->setNeedContainer(true);
++
++ if (field->typeName().endsWith(u":%1"_s.arg(structuredType->name())))
++ field->setRecursive(true);
++
++ if (!xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toString().isEmpty()) {
++ field->setLength(xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toUInt());
++ field->setIsInStructuredTypeBitMask(true);
++ }
++ if (!xmlStreamReader.attributes()
++ .value(StringIdentifier::switchFieldIdentifier)
++ .toString()
++ .isEmpty()) {
++ structuredType->setHasSwitchfield(true);
++ field->setSwitchField(
++ xmlStreamReader.attributes().value(StringIdentifier::switchFieldIdentifier).toString());
++ }
++ if (!xmlStreamReader.attributes()
++ .value(StringIdentifier::switchValueIdentifier)
++ .toString()
++ .isEmpty()) {
++ structuredType->setHasUnion(true);
++ field->setIsUnion(true);
++ field->setSwitchValue(
++ xmlStreamReader.attributes().value(StringIdentifier::switchValueIdentifier).toInt());
++ }
++ if (field->typeName() == StringIdentifier::opcBitIdentifier)
++ structuredType->setContainsBitMask(true);
++ if (field->typeName().contains(StringIdentifier::bitIdentifier)
++ && (field->name().contains(StringIdentifier::specifiedIdentifier)
++ || field->name().contains(StringIdentifier::reservedIdentifier))
++ && structuredType->containsBitMask())
++ field->setIsInStructuredTypeBitMask(true);
++ structuredType->addField(field);
++ ParsingError error = NoError;
++ xmlStreamReader.readNext();
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ } else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidFieldEntry;
++ }
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseFile(
++ const QString &fileName, const bool &dependencyTypeDictionary)
++ {
++ ParsingError error = NoError;
++ QFile xmlFile(fileName);
++ if (!xmlFile.open(QIODevice::ReadOnly))
++ return InvalidFileName;
++ QXmlStreamReader xmlStreamReader;
++ xmlStreamReader.setDevice((&xmlFile));
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isStartElement()) {
++ if (xmlStreamReader.name() == StringIdentifier::typeDictionaryIdentifier)
++ error = parseTypeDictionary(xmlStreamReader, dependencyTypeDictionary);
++ else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidTypeDictionaryEntry;
++ }
++ } else
++ xmlStreamReader.readNext();
++ }
++ xmlFile.close();
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseImport(
++ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
++ {
++ Import *import = new Import(
++ xmlStreamReader.attributes().value(StringIdentifier::nameSpaceIdentifier).toString(),
++ xmlStreamReader.attributes().value(StringIdentifier::locationIdentifier).toString());
++ typeDictionary->addType(import);
++ xmlStreamReader.readNext();
++ ParsingError error = NoError;
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ } else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidImportEntry;
++ }
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseStructuredType(
++ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
++ {
++ StructuredType *structuredType = new StructuredType(
++ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
++ xmlStreamReader.attributes().value(StringIdentifier::baseTypeIdentifier).toString());
++ if (StringIdentifier::buildInTypesWithBitMask.contains(structuredType->name())) {
++ structuredType->setIsBuiltInType(true);
++ structuredType->setContainsBitMask(true);
++ }
++ structuredType->setTargetNamespace(typeDictionary->targetNamespace());
++ typeDictionary->addType(structuredType);
++ xmlStreamReader.readNext();
++ ParsingError error = NoError;
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++
++ const auto tempFields = structuredType->fields();
++ for (const auto &field : tempFields) {
++ if (field->recursive()) {
++ structuredType->setRecursive(true);
++ break;
++ }
++ }
++ return error;
++ }
++ if (xmlStreamReader.isStartElement()) {
++ if (xmlStreamReader.name() == StringIdentifier::fieldIdentifier)
++ error = parseField(xmlStreamReader, structuredType);
++ else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidStructuredTypeEntry;
++ }
++ } else
++ xmlStreamReader.readNext();
++ }
++
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::parseTypeDictionary(
++ QXmlStreamReader &xmlStreamReader, const bool &dependencyTypeDictionary)
++ {
++ QMap<QString, QString> namespaces;
++ for (int i = 0; i < xmlStreamReader.namespaceDeclarations().size(); i++)
++ namespaces.insert(xmlStreamReader.namespaceDeclarations().at(i).prefix().toString(),
++ xmlStreamReader.namespaceDeclarations().at(i).namespaceUri().toString());
++ m_mapTypeDictionary.insert(
++ xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString(),
++ new TypeDictionary(dependencyTypeDictionary,
++ xmlStreamReader.attributes()
++ .value(StringIdentifier::defaultByteOrderIdentifier)
++ .toString(),
++ xmlStreamReader.attributes()
++ .value(StringIdentifier::targetNamespaceIdentifier)
++ .toString(),
++ namespaces));
++ QString targetNamespace
++ = xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString();
++ ParsingError error = NoError;
++ if (!dependencyTypeDictionary && targetNamespace == StringIdentifier::namespaceZeroIdentifier)
++ return CannotFullyGenerateNamespaceZero;
++ xmlStreamReader.readNext();
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ }
++ if (xmlStreamReader.isStartElement()) {
++ if (xmlStreamReader.name() == StringIdentifier::enumeratedTypeIdentifier)
++ error = parseEnumeratedType(xmlStreamReader,
++ m_mapTypeDictionary.value(targetNamespace));
++ else {
++ if (xmlStreamReader.name() == StringIdentifier::structuredTypeIdentifier)
++ error = parseStructuredType(xmlStreamReader,
++ m_mapTypeDictionary.value(targetNamespace));
++ else {
++ if (xmlStreamReader.name() == StringIdentifier::importIdentifier)
++ error = parseImport(xmlStreamReader,
++ m_mapTypeDictionary.value(targetNamespace));
++ else {
++ if (isKnownElement(xmlStreamReader))
++ error = skipKnownElement(xmlStreamReader);
++ else
++ error = InvalidTypeDictionaryEntry;
++ }
++ }
++ }
++ } else
++ xmlStreamReader.readNext();
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::skipKnownElement(
++ QXmlStreamReader &xmlStreamReader)
++ {
++ ParsingError error = NoError;
++ xmlStreamReader.readNext();
++ while (!xmlStreamReader.atEnd() && error == NoError) {
++ if (xmlStreamReader.isEndElement()) {
++ xmlStreamReader.readNext();
++ return error;
++ }
++ if (xmlStreamReader.isStartElement()) {
++ if (xmlStreamReader.name() == StringIdentifier::documentationIdentifier
++ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier)
++ skipKnownElement(xmlStreamReader);
++ else
++ error = UnknownEntry;
++ } else
++ xmlStreamReader.readNext();
++ }
++ return error;
++ }
++
++ RecursiveDescentParser::ParsingError RecursiveDescentParser::generateInputFiles(
++ const QString &path,
++ const QString &prefix,
++ const QString &header,
++ bool generateBundleFiles)
++ {
++ DependencyDataTypeValidator dependencyDataTypeValidator;
++ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
++ if (!it.value()->dependencyTypeDictionary())
++ it.value()->accept(&dependencyDataTypeValidator);
++ }
++
++ dependencyDataTypeValidator.setReadResolveDependencies(
++ DependencyDataTypeValidator::ResolveDependencies);
++
++ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
++ it.value()->accept(&dependencyDataTypeValidator);
++
++ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
++ it.value()->accept(&dependencyDataTypeValidator);
++
++ if (dependencyDataTypeValidator.unresolvedDependencyStringList().isEmpty()) {
++ DataTypeFileWriter filewriter(path,
++ prefix,
++ header);
++ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
++ if (!it.value()->dependencyTypeDictionary())
++ it.value()->accept(&filewriter);
++ };
++ if (filewriter.generateMapping().isEmpty()) {
++ return UnableToWriteFile;
++ }
++ MappingFileGenerator mappingFileGenerator(filewriter.generateMapping(), path, prefix, header);
++ if (filewriter.generateTypes(dependencyDataTypeValidator.resolvedDependencyElementList())
++ != DataTypeFileWriter::GeneratingError::NoError) {
++ return UnableToWriteFile;
++ }
++
++ mappingFileGenerator.addGenerateMapping(dependencyDataTypeValidator.resolvedDependencyElementList());
++
++ if (mappingFileGenerator.generateMapping() != MappingFileGenerator::MappingError::NoError)
++ return UnableToWriteFile;
++
++ if (generateBundleFiles)
++ filewriter.writeBundleFiles();
++ } else {
++ return MissingDependency;
++ }
++
++ return NoError;
++ }
++
++ bool RecursiveDescentParser::isKnownElement(const QXmlStreamReader &xmlStreamReader)
++ {
++ return xmlStreamReader.name() == StringIdentifier::documentationIdentifier
++ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier;
++ }
++
++ QString RecursiveDescentParser::permittedName(const QString &name) const
++ {
++ if (name.isEmpty())
++ return {};
++
++ auto result = name;
++
++ if (StringIdentifier::illegalNames.contains(name)) {
++ result = Util::lowerFirstLetter(name);
++ result = u"_%1"_s.arg(name);
++ }
++
++ if (result.contains(' '_L1))
++ result.replace(' '_L1, '_'_L1);
++ if (name.contains('-'_L1))
++ result.replace('-'_L1, '_'_L1);
++
++ return result;
++ }