From 137382b205a55f6b76b9d9c4a91116484f8676b6 Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Sat, 30 Jun 2018 19:55:29 +0200 Subject: [PATCH] WIP settings generator --- .../settingsgenerator/qsettingsgenerator.xsd | 1 + tools/settingsgenerator/settingsgenerator.cpp | 336 ++++++++++++------ tools/settingsgenerator/settingsgenerator.h | 45 ++- 3 files changed, 255 insertions(+), 127 deletions(-) diff --git a/tools/settingsgenerator/qsettingsgenerator.xsd b/tools/settingsgenerator/qsettingsgenerator.xsd index e9e51cd..3dc8ea9 100644 --- a/tools/settingsgenerator/qsettingsgenerator.xsd +++ b/tools/settingsgenerator/qsettingsgenerator.xsd @@ -80,6 +80,7 @@ + diff --git a/tools/settingsgenerator/settingsgenerator.cpp b/tools/settingsgenerator/settingsgenerator.cpp index 72c2cbf..014eafe 100644 --- a/tools/settingsgenerator/settingsgenerator.cpp +++ b/tools/settingsgenerator/settingsgenerator.cpp @@ -1,5 +1,9 @@ #include "settingsgenerator.h" #include +#include +#include + +#define TABS QString{QLatin1Char('\t')}.repeated(intendent) SettingsGenerator::SettingsGenerator(const QString &inPath, const QString &hdrPath, const QString &srcPath) : _inFile{inPath}, @@ -19,177 +23,293 @@ void SettingsGenerator::process() if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) throwFile(_srcFile); - readData(); -} - -void SettingsGenerator::readData() -{ if(!_xml.readNextStartElement()) - throwReader(); + throwReader(_xml); if(_xml.name() != QStringLiteral("Settings")) - throwChild(); - - _data = readSettings(); - checkError(); + throwChild(_xml); + auto data = readSettings(_xml); + checkError(_xml); qDebug() << "Completed parsing xml - it is valid"; + + data.includes.append({ + {false, QStringLiteral("QtCore/QObject")}, + {false, QStringLiteral("QtMvvmCore/ISettingsAccessor")}, + {false, QStringLiteral("QtMvvmCore/SettingsEntry")}, + }); + + writeHeader(data); + writeSource(data); } -SettingsGenerator::Include SettingsGenerator::readInclude() +SettingsGenerator::Include SettingsGenerator::readInclude(QXmlStreamReader &xml) { Include include; - include.local = readAttrib(QStringLiteral("local"), false); - include.include = _xml.readElementText(); - checkError(); + include.local = readAttrib(xml, QStringLiteral("local"), false); + include.include = xml.readElementText(); + checkError(xml); return include; } -SettingsGenerator::Param SettingsGenerator::readParam() +SettingsGenerator::Param SettingsGenerator::readParam(QXmlStreamReader &xml) { Param param; - param.key = readAttrib(QStringLiteral("key"), {}, true); - param.type = readAttrib(QStringLiteral("type"), {}, true); - param.asStr = readAttrib(QStringLiteral("asStr"), false); - param.value = _xml.readElementText(); - checkError(); + param.key = readAttrib(xml, QStringLiteral("key"), {}, true); + param.type = readAttrib(xml, QStringLiteral("type"), {}, true); + param.asStr = readAttrib(xml, QStringLiteral("asStr"), false); + param.value = xml.readElementText(); + checkError(xml); return param; } -QSharedPointer SettingsGenerator::readBackend() +QSharedPointer SettingsGenerator::readBackend(QXmlStreamReader &xml) { auto backend = QSharedPointer::create(); - backend->className = readAttrib(QStringLiteral("class"), {}, true); - while(_xml.readNextStartElement()) { - if(_xml.name() == QStringLiteral("Param")) - backend->params.append(readParam()); + backend->className = readAttrib(xml, QStringLiteral("class"), {}, true); + while(xml.readNextStartElement()) { + if(xml.name() == QStringLiteral("Param")) + backend->params.append(readParam(xml)); else - throwChild(); + throwChild(xml); } - checkError(); + checkError(xml); return backend; } -QPair SettingsGenerator::readTypeMapping() +QPair SettingsGenerator::readTypeMapping(QXmlStreamReader &xml) { QPair mapping; - mapping.first = readAttrib(QStringLiteral("key"), {}, true); - mapping.second = readAttrib(QStringLiteral("type"), {}, true); - checkError(); + mapping.first = readAttrib(xml, QStringLiteral("key"), {}, true); + mapping.second = readAttrib(xml, QStringLiteral("type"), {}, true); + checkError(xml); return mapping; } -SettingsGenerator::Node SettingsGenerator::readNode() +SettingsGenerator::Node SettingsGenerator::readNode(QXmlStreamReader &xml) { Node node; - node.key = readAttrib(QStringLiteral("key"), {}, true); - while(_xml.readNextStartElement()) { - if(_xml.name() == QStringLiteral("Node")) - node.subNodes.append(readNode()); - else if(_xml.name() == QStringLiteral("Entry")) - node.subEntries.append(readEntry()); - else if(_xml.name() == QStringLiteral("Import")) - node.subImports.append(readImport()); + node.key = readAttrib(xml, QStringLiteral("key"), {}, true); + while(xml.readNextStartElement()) { + if(xml.name() == QStringLiteral("Node")) + node.subNodes.append(readNode(xml)); + else if(xml.name() == QStringLiteral("Entry")) + node.subEntries.append(readEntry(xml)); + else if(xml.name() == QStringLiteral("Import")) + node.subImports.append(readImport(xml)); else - throwChild(); + throwChild(xml); } - checkError(); + checkError(xml); return node; } -SettingsGenerator::Entry SettingsGenerator::readEntry() +SettingsGenerator::Entry SettingsGenerator::readEntry(QXmlStreamReader &xml) { Entry entry; - entry.key = readAttrib(QStringLiteral("key"), {}, true); - entry.type = readAttrib(QStringLiteral("type"), {}, true); - entry.qmlGroupKey = readAttrib(QStringLiteral("qmlGroupKey")); - entry.defaultValue = readAttrib(QStringLiteral("default")); - entry.tr = readAttrib(QStringLiteral("tr"), false); - entry.trContext = readAttrib(QStringLiteral("trContext")); - while(_xml.readNextStartElement()) { - if(_xml.name() == QStringLiteral("Code")) { + entry.key = readAttrib(xml, QStringLiteral("key"), {}, true); + entry.type = readAttrib(xml, QStringLiteral("type"), {}, true); + entry.qmlGroupKey = readAttrib(xml, QStringLiteral("qmlGroupKey")); + entry.defaultValue = readAttrib(xml, QStringLiteral("default")); + entry.tr = readAttrib(xml, QStringLiteral("tr"), false); + entry.trContext = readAttrib(xml, QStringLiteral("trContext")); + while(xml.readNextStartElement()) { + if(xml.name() == QStringLiteral("Code")) { if(!entry.defaultValue.isNull()) - throwChild(); - entry.defaultValue = _xml.readElementText(); + throwChild(xml); + entry.defaultValue = xml.readElementText(); entry.defaultIsCode = true; - checkError(); - } else if(_xml.name() == QStringLiteral("Node")) - entry.subNodes.append(readNode()); - else if(_xml.name() == QStringLiteral("Entry")) - entry.subEntries.append(readEntry()); - else if(_xml.name() == QStringLiteral("Import")) - entry.subImports.append(readImport()); + checkError(xml); + } else if(xml.name() == QStringLiteral("Node")) + entry.subNodes.append(readNode(xml)); + else if(xml.name() == QStringLiteral("Entry")) + entry.subEntries.append(readEntry(xml)); + else if(xml.name() == QStringLiteral("Import")) + entry.subImports.append(readImport(xml)); else - throwChild(); + throwChild(xml); } - checkError(); + checkError(xml); return entry; } -SettingsGenerator::Import SettingsGenerator::readImport() +SettingsGenerator::Import SettingsGenerator::readImport(QXmlStreamReader &xml) { - Import import; - import.required = readAttrib(QStringLiteral("required"), true); - import.rootNode = readAttrib(QStringLiteral("rootNode")); - import.import = _xml.readElementText(); - checkError(); - return import; + auto required = readAttrib(xml, QStringLiteral("required"), true); + auto rootNode = readAttrib(xml, QStringLiteral("rootNode")).split(QLatin1Char('/'), QString::SkipEmptyParts); + auto path = xml.readElementText(); + checkError(xml); + + auto pInfo = QFileInfo{static_cast(xml.device())->fileName()}.dir(); + QFile subFile{pInfo.absoluteFilePath(path)}; + if(!subFile.exists()) { + if(required) + throwReader(xml, QStringLiteral("Failed to find required import file \"%1\"").arg(subFile.fileName())); + else + return {}; + } + + if(!subFile.open(QIODevice::ReadOnly | QIODevice::Text)) + throwFile(subFile); + + //TODO implement + + QXmlStreamReader reader{&subFile}; + if(!reader.readNextStartElement()) + throwReader(reader); + if(reader.name() != QStringLiteral("Settings")) + throwChild(reader); + Node subSettings = readSettings(reader); + checkError(reader); + for(const auto &key : rootNode) { + auto found = false; + for(const auto &sNode : qAsConst(subSettings.subNodes)) { + if(sNode.key == key) { + found = true; + subSettings = sNode; + break; + } + } + for(const auto &sNode : qAsConst(subSettings.subEntries)) { + if(sNode.key == key) { + found = true; + subSettings = sNode; + break; + } + } + for(const auto &sNode : qAsConst(subSettings.subImports)) { + if(sNode.key == key) { + found = true; + subSettings = sNode; + break; + } + } + if(!found) + throwReader(xml, QStringLiteral("Unable to find rootNode \"%1\" in loaded document").arg(rootNode.join(QLatin1Char('/')))); + } + + return std::move(subSettings); } -SettingsGenerator::Settings SettingsGenerator::readSettings() +SettingsGenerator::Settings SettingsGenerator::readSettings(QXmlStreamReader &xml) { Settings settings; - settings.name = readAttrib(QStringLiteral("name"), {}, true); - settings.prefix = readAttrib(QStringLiteral("prefix")); - - while(_xml.readNextStartElement()) { - if(_xml.name() == QStringLiteral("Include")) - settings.includes.append(readInclude()); - else if(_xml.name() == QStringLiteral("Backend")) - settings.backend = readBackend(); - else if(_xml.name() == QStringLiteral("TypeMapping")) { - auto mapping = readTypeMapping(); + settings.name = readAttrib(xml, QStringLiteral("name"), {}, true); + settings.prefix = readAttrib(xml, QStringLiteral("prefix")); + + while(xml.readNextStartElement()) { + if(xml.name() == QStringLiteral("Include")) + settings.includes.append(readInclude(xml)); + else if(xml.name() == QStringLiteral("Backend")) + settings.backend = readBackend(xml); + else if(xml.name() == QStringLiteral("TypeMapping")) { + auto mapping = readTypeMapping(xml); settings.typeMapping.insert(mapping.first, mapping.second); - } else if(_xml.name() == QStringLiteral("Node")) - settings.subNodes.append(readNode()); - else if(_xml.name() == QStringLiteral("Entry")) - settings.subEntries.append(readEntry()); - else if(_xml.name() == QStringLiteral("Import")) - settings.subImports.append(readImport()); + } else if(xml.name() == QStringLiteral("Node")) + settings.subNodes.append(readNode(xml)); + else if(xml.name() == QStringLiteral("Entry")) + settings.subEntries.append(readEntry(xml)); + else if(xml.name() == QStringLiteral("Import")) + settings.subImports.append(readImport(xml)); else - throwChild(); + throwChild(xml); } - checkError(); + checkError(xml); return settings; } -void SettingsGenerator::checkError() const +void SettingsGenerator::writeHeader(const Settings &settings) +{ + QString incGuard = QFileInfo{_hdrFile.fileName()} + .completeBaseName() + .replace(QLatin1Char('.'), QLatin1Char('_')) + .toUpper() + QStringLiteral("_H"); + _hdr << "#ifndef " << incGuard << '\n' + << "#define " << incGuard << "\n\n"; + + // write the includes + for(const auto &inc : settings.includes) { + if(inc.local) + _hdr << "#include \"" << inc.include << "\"\n"; + else + _hdr << "#include <" << inc.include << ">\n"; + } + _hdr << "\n"; + + // create the class + QString pName = settings.prefix.isEmpty() ? settings.name : (settings.prefix + QLatin1Char(' ') + settings.name); + _hdr << "class " << pName << " : public QObject\n" + << "{\n" + << "\tQ_OBJECT\n\n" + << "\tQ_PROPERTY(ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n" + << "public:\n" + << "\tQ_INVOKABLE explicit " << settings.name << "(QObject *parent = nullptr);\n" + << "\texplicit " << settings.name << "(QObject *parent, ISettingsAccessor *accessor);\n\n" + << "\tstatic " << settings.name << " *instance();\n\n" + << "\tISettingsAccessor *accessor() const;\n\n"; + + writeNodeElements(settings); + + _hdr << "\nprivate:\n" + << "\tISettingsAccessor *_accessor;\n" + << "};\n\n" + << "#endif //" << incGuard << '\n'; +} + +void SettingsGenerator::writeNodeElements(const SettingsGenerator::Node &node, int intendent) +{ + for(const auto &cNode : node.subNodes) + writeNode(cNode, intendent); + for(const auto &cEntry : node.subEntries) + writeEntry(cEntry, intendent); +} + +void SettingsGenerator::writeNode(const SettingsGenerator::Node &node, int intendent) +{ + _hdr << TABS << "struct {\n"; + writeNodeElements(node, intendent + 1); + _hdr << TABS << "} " << node.key << ";\n"; +} + +void SettingsGenerator::writeEntry(const SettingsGenerator::Entry &entry, int intendent) +{ + _hdr << TABS << "struct : QtMvvm::SettingsEntry<" << entry.type << "> {\n"; + writeNodeElements(entry, intendent + 1); + _hdr << TABS << "} " << entry.key << ";\n"; +} + +void SettingsGenerator::writeSource(const Settings &settings) +{ + +} + +void SettingsGenerator::checkError(QXmlStreamReader &xml) const { - if(_xml.hasError()) - throwReader(); + if(xml.hasError()) + throwReader(xml); } template -T SettingsGenerator::readAttrib(const QString &key, const T &defaultValue, bool required) const +T SettingsGenerator::readAttrib(QXmlStreamReader &xml, const QString &key, const T &defaultValue, bool required) const { - if(_xml.attributes().hasAttribute(key)) - return QVariant(_xml.attributes().value(key).toString()).template value(); + if(xml.attributes().hasAttribute(key)) + return QVariant(xml.attributes().value(key).toString()).template value(); else if(required) - throwReader(QStringLiteral("Required attribute \"%1\" but was not set").arg(key)); + throwReader(xml, QStringLiteral("Required attribute \"%1\" but was not set").arg(key)); else return defaultValue; } template<> -bool SettingsGenerator::readAttrib(const QString &key, const bool &defaultValue, bool required) const +bool SettingsGenerator::readAttrib(QXmlStreamReader &xml, const QString &key, const bool &defaultValue, bool required) const { - if(_xml.attributes().hasAttribute(key)) { - if(_xml.attributes().value(key) == QStringLiteral("true")) + if(xml.attributes().hasAttribute(key)) { + if(xml.attributes().value(key) == QStringLiteral("true")) return true; - else if(_xml.attributes().value(key) == QStringLiteral("false")) + else if(xml.attributes().value(key) == QStringLiteral("false")) return false; else - throwReader(QStringLiteral("Value of attribute \"%1\" is not a xs:boolean!").arg(key)); + throwReader(xml, QStringLiteral("Value of attribute \"%1\" is not a xs:boolean!").arg(key)); } else if(required) - throwReader(QStringLiteral("Required attribute \"%1\" but was not set").arg(key)); + throwReader(xml, QStringLiteral("Required attribute \"%1\" but was not set").arg(key)); else return defaultValue; } @@ -199,16 +319,16 @@ void SettingsGenerator::throwFile(const QFileDevice &file) const throw QStringLiteral("%1: %2").arg(file.fileName(), file.errorString()); } -void SettingsGenerator::throwReader(const QString &overwriteError) const +void SettingsGenerator::throwReader(QXmlStreamReader &xml, const QString &overwriteError) const { throw QStringLiteral("%1:%2:%3: %4") - .arg(dynamic_cast(_xml.device())->fileName()) - .arg(_xml.lineNumber()) - .arg(_xml.columnNumber()) - .arg(overwriteError.isNull() ? _xml.errorString() : overwriteError); + .arg(dynamic_cast(xml.device())->fileName()) + .arg(xml.lineNumber()) + .arg(xml.columnNumber()) + .arg(overwriteError.isNull() ? xml.errorString() : overwriteError); } -void SettingsGenerator::throwChild() +void SettingsGenerator::throwChild(QXmlStreamReader &xml) { - throwReader(QStringLiteral("Unexpected child element: %1").arg(_xml.name())); + throwReader(xml, QStringLiteral("Unexpected child element: %1").arg(xml.name())); } diff --git a/tools/settingsgenerator/settingsgenerator.h b/tools/settingsgenerator/settingsgenerator.h index 26dae83..70c7d31 100644 --- a/tools/settingsgenerator/settingsgenerator.h +++ b/tools/settingsgenerator/settingsgenerator.h @@ -59,44 +59,51 @@ private: QString trContext; }; - struct Import { - bool required; - QString rootNode; - QString import; + struct Import : public Node { + inline Import() = default; + inline Import(Node &&node) : + Node(std::move(node)) + {} }; struct Settings : public Node { QString name; QString prefix; + QString baseKey; QList includes; QSharedPointer backend; QHash typeMapping; - } _data; + }; + + Include readInclude(QXmlStreamReader &xml); + Param readParam(QXmlStreamReader &xml); + QSharedPointer readBackend(QXmlStreamReader &xml); + QPair readTypeMapping(QXmlStreamReader &xml); - void readData(); + Node readNode(QXmlStreamReader &xml); + Entry readEntry(QXmlStreamReader &xml); + Import readImport(QXmlStreamReader &xml); - Include readInclude(); - Param readParam(); - QSharedPointer readBackend(); - QPair readTypeMapping(); + Settings readSettings(QXmlStreamReader &xml); - Node readNode(); - Entry readEntry(); - Import readImport(); + void writeHeader(const Settings &settings); + void writeNodeElements(const Node &node, int intendent = 1); + void writeNode(const Node &node, int intendent = 1); + void writeEntry(const Entry &entry, int intendent = 1); - Settings readSettings(); + void writeSource(const Settings &settings); template - T readAttrib(const QString &key, const T &defaultValue = {}, bool required = false) const; + T readAttrib(QXmlStreamReader &xml, const QString &key, const T &defaultValue = {}, bool required = false) const; - void checkError() const; + void checkError(QXmlStreamReader &xml) const; Q_NORETURN void throwFile(const QFileDevice &file) const; - Q_NORETURN void throwReader(const QString &overwriteError = {}) const; - Q_NORETURN void throwChild(); + Q_NORETURN void throwReader(QXmlStreamReader &xml, const QString &overwriteError = {}) const; + Q_NORETURN void throwChild(QXmlStreamReader &xml); }; template<> -bool SettingsGenerator::readAttrib(const QString &key, const bool &defaultValue, bool required) const; +bool SettingsGenerator::readAttrib(QXmlStreamReader &xml, const QString &key, const bool &defaultValue, bool required) const; #endif // SETTINGSGENERATOR_H