From 2596f8280fbf247525d2758c99cd9e9ee078778d Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Sat, 14 Jul 2018 18:54:58 +0200 Subject: [PATCH] implemented xml imports, ready for first tests --- src/mvvmcore/settings.xsd | 145 ----------------- tools/settingsgenerator/qpmx.json | 2 +- .../settingsgenerator/qsettingsgenerator.qrc | 1 + tools/settingsgenerator/settingsconfig.xsd | 144 +++++++++++++++++ tools/settingsgenerator/settingsgenerator.cpp | 150 ++++++++++++++++-- tools/settingsgenerator/settingsgenerator.h | 10 +- tools/settingsgenerator/settingsgenerator.pro | 9 +- .../settingsgenerator/settingstranslator.cpp | 108 +++++++++++++ tools/settingsgenerator/settingstranslator.h | 21 +++ 9 files changed, 431 insertions(+), 159 deletions(-) delete mode 100644 src/mvvmcore/settings.xsd create mode 100644 tools/settingsgenerator/settingsconfig.xsd create mode 100644 tools/settingsgenerator/settingstranslator.cpp create mode 100644 tools/settingsgenerator/settingstranslator.h diff --git a/src/mvvmcore/settings.xsd b/src/mvvmcore/settings.xsd deleted file mode 100644 index 6e3bb0e..0000000 --- a/src/mvvmcore/settings.xsd +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/settingsgenerator/qpmx.json b/tools/settingsgenerator/qpmx.json index 5f052db..8de0c3d 100644 --- a/tools/settingsgenerator/qpmx.json +++ b/tools/settingsgenerator/qpmx.json @@ -3,7 +3,7 @@ { "package": "de.skycoder42.qxmlcodegen", "provider": "qpm", - "version": "1.0.0" + "version": "1.1.0" } ], "license": { diff --git a/tools/settingsgenerator/qsettingsgenerator.qrc b/tools/settingsgenerator/qsettingsgenerator.qrc index c1b6869..b2a73cd 100644 --- a/tools/settingsgenerator/qsettingsgenerator.qrc +++ b/tools/settingsgenerator/qsettingsgenerator.qrc @@ -1,5 +1,6 @@ qsettingsgenerator.xsd + settingsconfig.xsd diff --git a/tools/settingsgenerator/settingsconfig.xsd b/tools/settingsgenerator/settingsconfig.xsd new file mode 100644 index 0000000..e65e245 --- /dev/null +++ b/tools/settingsgenerator/settingsconfig.xsd @@ -0,0 +1,144 @@ + + + + + QtCore/QHash + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/settingsgenerator/settingsgenerator.cpp b/tools/settingsgenerator/settingsgenerator.cpp index 9a1946e..f1c418f 100644 --- a/tools/settingsgenerator/settingsgenerator.cpp +++ b/tools/settingsgenerator/settingsgenerator.cpp @@ -50,7 +50,25 @@ void SettingsGenerator::read_included_file(QXmlStreamReader &reader, NodeContent // read the document try { - auto settings = readDocument(import.importPath); + QFile xmlFile{import.importPath}; + if(!xmlFile.open(QIODevice::ReadOnly | QIODevice::Text)) + throw FileException{xmlFile}; + + QXmlStreamReader subReader{&xmlFile}; + if(!subReader.readNextStartElement()) + throw XmlException{subReader}; + + SettingsType settings; + if(subReader.name() == QStringLiteral("Settings")) { + read_SettingsType(subReader, settings); + } else if(subReader.name() == QStringLiteral("SettingsConfig")) { + SettingsTranslator confReader; + SettingsConfigBase::SettingsConfigType settingsConf; + confReader.read_SettingsConfigType(subReader, settingsConf); + convertFromConf(reader, settingsConf, settings); + } else + throwChild(subReader); + NodeContentGroup *cGrp = &settings; if(import.rootNode) { for(const auto &key : import.rootNode.value().split(QLatin1Char('/'), QString::SkipEmptyParts)) { @@ -59,8 +77,8 @@ void SettingsGenerator::read_included_file(QXmlStreamReader &reader, NodeContent return; } } - data = *cGrp; - } catch(QException &e) { + data = std::move(*cGrp); + } catch(FileException &e) { if(import.required) throw; else { @@ -70,17 +88,131 @@ void SettingsGenerator::read_included_file(QXmlStreamReader &reader, NodeContent } } -SettingsGeneratorBase::NodeContentGroup *SettingsGenerator::findContentGroup(SettingsGeneratorBase::NodeContentGroup *cGrp, const QString &key) +void SettingsGenerator::convertFromConf(QXmlStreamReader &reader, SettingsConfigBase::SettingsConfigType &conf, SettingsType &data) +{ + for(auto &element : conf.content) { + if(nonstd::holds_alternative(element)) + readCategory(reader, nonstd::get(element), data); + else if(nonstd::holds_alternative(element)) + readSection(reader, nonstd::get(element), data); + else if(nonstd::holds_alternative(element)) + readGroup(reader, nonstd::get(element), data); + else if(nonstd::holds_alternative(element)) + readEntry(reader, nonstd::get(element), data); + else + throw XmlException{reader, QStringLiteral("Unexpected child element in included SettingsConf")}; + } +} + +void SettingsGenerator::readCategory(QXmlStreamReader &reader, SettingsConfigBase::CategoryContentGroup &content, NodeContentGroup &targetRootNode) +{ + for(auto &element : content.content) { + if(nonstd::holds_alternative(element)) + readSection(reader, nonstd::get(element), targetRootNode); + else if(nonstd::holds_alternative(element)) + readGroup(reader, nonstd::get(element), targetRootNode); + else if(nonstd::holds_alternative(element)) + readEntry(reader, nonstd::get(element), targetRootNode); + else + throw XmlException{reader, QStringLiteral("Unexpected child element in included SettingsConf Categoy")}; + } +} + +void SettingsGenerator::readSection(QXmlStreamReader &reader, SettingsConfigBase::SectionContentGroup &content, NodeContentGroup &targetRootNode) +{ + for(auto &element : content.content) { + if(nonstd::holds_alternative(element)) + readGroup(reader, nonstd::get(element), targetRootNode); + else if(nonstd::holds_alternative(element)) + readEntry(reader, nonstd::get(element), targetRootNode); + else + throw XmlException{reader, QStringLiteral("Unexpected child element in included SettingsConf Section")}; + } +} + +void SettingsGenerator::readGroup(QXmlStreamReader &reader, SettingsConfigBase::GroupContentGroup &content, NodeContentGroup &targetRootNode) +{ + for(auto &element : content.content) { + if(nonstd::holds_alternative(element)) + readEntry(reader, nonstd::get(element), targetRootNode); + else + throw XmlException{reader, QStringLiteral("Unexpected child element in included SettingsConf Group")}; + } +} + +void SettingsGenerator::readEntry(QXmlStreamReader &reader, SettingsConfigBase::EntryType &entry, NodeContentGroup &targetRootNode) +{ + auto keyChain = entry.key.split(QLatin1Char('/'), QString::SkipEmptyParts); + auto entryKey = keyChain.takeLast(); + + auto cGrp = &targetRootNode; + for(const auto &key : keyChain) { + auto nGrp = findContentGroup(cGrp, key); + if(!nGrp) { + NodeType nNode; + nNode.key = key; + cGrp->contentNodes.append(std::move(nNode)); + nGrp = &(std::get(cGrp->contentNodes.last())); + } + cGrp = nGrp; + } + + auto isEntry = false; + auto eGrp = findContentGroup(cGrp, entryKey, &isEntry); + if(!eGrp) { + EntryType nEntry; + nEntry.key = entryKey; + cGrp->contentNodes.append(std::move(nEntry)); + eGrp = &(std::get(cGrp->contentNodes.last())); + } else if(!isEntry) { + EntryType nEntry; + nEntry.key = entryKey; + nEntry.contentNodes = std::move(eGrp->contentNodes); + eGrp = replaceNodeByEntry(cGrp, eGrp, std::move(nEntry)); + Q_ASSERT(eGrp); + } else + throw XmlException{reader, QStringLiteral("Found duplicated entry with key: %1").arg(entry.key)}; + + auto nEntry = static_cast(eGrp); + nEntry->type = std::move(entry.type); + nEntry->defaultValue = std::move(entry.defaultValue); + nEntry->tr = std::move(entry.trdefault); +} + +SettingsGeneratorBase::NodeContentGroup *SettingsGenerator::findContentGroup(SettingsGeneratorBase::NodeContentGroup *cGrp, const QString &key, bool *isEntry) { - for(const auto &cNode : cGrp->contentNodes) { + if(isEntry) + *isEntry = false; + for(auto &cNode : cGrp->contentNodes) { if(nonstd::holds_alternative(cNode)) { if(std::get(cNode).key == key) - return const_cast(&(std::get(cNode))); + return &(std::get(cNode)); } else if(nonstd::holds_alternative(cNode)) { - if(std::get(cNode).key == key) - return const_cast(&(std::get(cNode))); + if(std::get(cNode).key == key) { + if(isEntry) + *isEntry = true; + return &(std::get(cNode)); + } + } else if(nonstd::holds_alternative(cNode)) { + auto res = findContentGroup(&(std::get(cNode)), key); + if(res) + return res; + } + } + + return nullptr; +} + +SettingsGeneratorBase::NodeContentGroup *SettingsGenerator::replaceNodeByEntry(SettingsGeneratorBase::NodeContentGroup *cGrp, NodeContentGroup *node, SettingsGeneratorBase::EntryType &&entry) +{ + for(auto &cNode : cGrp->contentNodes) { + if(nonstd::holds_alternative(cNode)) { + if(&std::get(cNode) == node) { + cNode = std::move(entry); + return &(std::get(cNode)); + } } else if(nonstd::holds_alternative(cNode)) { - auto res = findContentGroup(const_cast(&(std::get(cNode))), key); + auto res = replaceNodeByEntry(&(std::get(cNode)), node, std::move(entry)); if(res) return res; } diff --git a/tools/settingsgenerator/settingsgenerator.h b/tools/settingsgenerator/settingsgenerator.h index 9d897b5..6a83354 100644 --- a/tools/settingsgenerator/settingsgenerator.h +++ b/tools/settingsgenerator/settingsgenerator.h @@ -5,6 +5,7 @@ #include #include "qsettingsgenerator.h" +#include "settingstranslator.h" class SettingsGenerator : public SettingsGeneratorBase { @@ -25,7 +26,14 @@ private: QTextStream _hdr; QTextStream _src; - NodeContentGroup *findContentGroup(NodeContentGroup *cGrp, const QString &key); + void convertFromConf(QXmlStreamReader &reader, SettingsConfigBase::SettingsConfigType &conf, SettingsType &data); + void readCategory(QXmlStreamReader &reader, SettingsConfigBase::CategoryContentGroup &content, NodeContentGroup &targetRootNode); + void readSection(QXmlStreamReader &reader, SettingsConfigBase::SectionContentGroup &content, NodeContentGroup &targetRootNode); + void readGroup(QXmlStreamReader &reader, SettingsConfigBase::GroupContentGroup &content, NodeContentGroup &targetRootNode); + void readEntry(QXmlStreamReader &reader, SettingsConfigBase::EntryType &entry, NodeContentGroup &targetRootNode); + + NodeContentGroup *findContentGroup(NodeContentGroup *cGrp, const QString &key, bool *isEntry = nullptr); + NodeContentGroup *replaceNodeByEntry(NodeContentGroup *cGrp, NodeContentGroup *node, EntryType &&entry); void writeHeader(const SettingsType &settings); void writeNodeElements(const NodeContentGroup &node, int intendent = 1); diff --git a/tools/settingsgenerator/settingsgenerator.pro b/tools/settingsgenerator/settingsgenerator.pro index 15f6e77..513f000 100644 --- a/tools/settingsgenerator/settingsgenerator.pro +++ b/tools/settingsgenerator/settingsgenerator.pro @@ -15,14 +15,17 @@ DEFINES += "COMPANY=\\\"$$COMPANY\\\"" DEFINES += "BUNDLE_PREFIX=\\\"$$BUNDLE_PREFIX\\\"" HEADERS += \ - settingsgenerator.h + settingsgenerator.h \ + settingstranslator.h SOURCES += \ main.cpp \ - settingsgenerator.cpp + settingsgenerator.cpp \ + settingstranslator.cpp XML_SCHEMA_DEFINITIONS += \ - qsettingsgenerator.xsd + qsettingsgenerator.xsd \ + settingsconfig.xsd contains(QT, xmlpatterns):RESOURCES += qsettingsgenerator.qrc diff --git a/tools/settingsgenerator/settingstranslator.cpp b/tools/settingsgenerator/settingstranslator.cpp new file mode 100644 index 0000000..df63f4e --- /dev/null +++ b/tools/settingsgenerator/settingstranslator.cpp @@ -0,0 +1,108 @@ +#include "settingstranslator.h" + +#include +#include +#include + +namespace { + +using SourceType = SettingsConfigBase::variant; + +template +void convertElement(QXmlStreamReader &reader, TVariantTarget &, SourceType &&) +{ + throw SettingsConfigBase::XmlException{reader, QStringLiteral("Unexpected root element in included file")}; +} + +template +void convertElement(QXmlStreamReader &reader, TVariantTarget &target, SourceType &&source) +{ + if(nonstd::holds_alternative(source)) + target = nonstd::get(std::move(source)); + else + convertElement(reader, target, std::move(source)); +} + +template +struct VariantInfo {}; + +template +struct VariantInfo>> +{ + using TargetType = SettingsConfigBase::variant; + static void convert(QXmlStreamReader &reader, TargetType &target, SourceType &&source) { + convertElement(reader, target, std::move(source)); + } +}; + +} + +bool SettingsTranslator::finish_group_content(QXmlStreamReader &reader, GroupContentGroup &data, bool hasNext) +{ + hasNext = read_GroupContentGroup(reader, data, hasNext); + finishContents(reader, data.content); + return hasNext; +} + +bool SettingsTranslator::finish_section_content(QXmlStreamReader &reader, SectionContentGroup &data, bool hasNext) +{ + hasNext = read_SectionContentGroup(reader, data, hasNext); + finishContents(reader, data.content); + return hasNext; +} + +bool SettingsTranslator::finish_category_content(QXmlStreamReader &reader, CategoryContentGroup &data, bool hasNext) +{ + hasNext = read_CategoryContentGroup(reader, data, hasNext); + finishContents(reader, data.content); + return hasNext; +} + +bool SettingsTranslator::finish_settings_config_content(QXmlStreamReader &reader, SettingsConfigContentGroup &data, bool hasNext) +{ + hasNext = read_SettingsConfigContentGroup(reader, data, hasNext); + finishContents(reader, data.content); + return hasNext; +} + +template +void SettingsTranslator::finishContents(QXmlStreamReader &reader, TGroup &group) +{ + optional index; + for(auto it = group.begin(); it != group.end();) { + // convert includes to the actual data + if(nonstd::holds_alternative(*it)) { + if(!readGeneralInclude(reader, nonstd::get(*it), it, group)) + continue; + } + // verify that the contents are all of the same type + if(index) { + if(index.value() != it->index()) + throw XmlException{reader, QStringLiteral("Detected mixture of different child elements. Only includes and a single other type are allowed")}; + } else + index = it->index(); + ++it; + } +} + +template +bool SettingsTranslator::readGeneralInclude(QXmlStreamReader &reader, IncludeType include, TIter &it, TList &list) +{ + try { + //make the path relative if possbile + if(dynamic_cast(reader.device())) { + QFileInfo docInfo{static_cast(reader.device())->fileName()}; + include.includePath = docInfo.dir().absoluteFilePath(include.includePath); + } + // read the document + VariantInfo::convert(reader, *it, readDocument(include.includePath)); + return true; + } catch(FileException &e) { + if(include.optional) { + qWarning() << e.what(); + it = list.erase(it); + return false; + } else + throw; + } +} diff --git a/tools/settingsgenerator/settingstranslator.h b/tools/settingsgenerator/settingstranslator.h new file mode 100644 index 0000000..dd832ea --- /dev/null +++ b/tools/settingsgenerator/settingstranslator.h @@ -0,0 +1,21 @@ +#ifndef SETTINGSTRANSLATOR_H +#define SETTINGSTRANSLATOR_H + +#include "settingsconfig.h" + +class SettingsTranslator : public SettingsConfigBase +{ +protected: + bool finish_group_content(QXmlStreamReader &reader, GroupContentGroup &data, bool hasNext) override; + bool finish_section_content(QXmlStreamReader &reader, SectionContentGroup &data, bool hasNext) override; + bool finish_category_content(QXmlStreamReader &reader, CategoryContentGroup &data, bool hasNext) override; + bool finish_settings_config_content(QXmlStreamReader &reader, SettingsConfigContentGroup &data, bool hasNext) override; + +private: + template + void finishContents(QXmlStreamReader &reader, TGroup &group); + template + bool readGeneralInclude(QXmlStreamReader &reader, IncludeType include, TIter &it, TList &list); +}; + +#endif // SETTINGSTRANSLATOR_H