#include "settingsgenerator.h" #include #include #include #define TABS QString{QLatin1Char('\t')}.repeated(intendent) SettingsGenerator::SettingsGenerator(const QString &hdrPath, const QString &srcPath) : _hdrFile{hdrPath}, _srcFile{srcPath}, _hdr{&_hdrFile}, _src{&_srcFile} {} void SettingsGenerator::process(const QString &inPath) { // read settings and adjust defaults auto settings = readDocument(inPath); if(!settings.name) settings.name = QFileInfo{_hdrFile.fileName()}.baseName(); if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text)) throw FileException{_hdrFile}; writeHeader(settings); _hdrFile.close(); if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) throw FileException{_srcFile}; writeSource(settings); _srcFile.close(); } bool SettingsGenerator::read_type_mapping(QXmlStreamReader &reader, QHash &data, bool hasNext) { TypeMappingGroup grp; hasNext = read_TypeMappingGroup(reader, grp, hasNext); for(const auto &mapping : qAsConst(grp.typeMapping)) data.insert(mapping.key, mapping.type); return hasNext; } void SettingsGenerator::read_included_file(QXmlStreamReader &reader, NodeContentGroup &data) { ImportType import; read_ImportType(reader, import); //make the path relative if possbile if(dynamic_cast(reader.device())) { QFileInfo docInfo{static_cast(reader.device())->fileName()}; import.importPath = docInfo.dir().absoluteFilePath(import.importPath); } // read the document try { 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)) { cGrp = findContentGroup(cGrp, key); if(!cGrp) return; } } data = std::move(*cGrp); } catch(FileException &e) { if(import.required) throw; else { qWarning() << e.what(); return; } } } 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 = &(nonstd::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 = &(nonstd::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) { if(isEntry) *isEntry = false; for(auto &cNode : cGrp->contentNodes) { if(nonstd::holds_alternative(cNode)) { if(nonstd::get(cNode).key == key) return &(nonstd::get(cNode)); } else if(nonstd::holds_alternative(cNode)) { if(nonstd::get(cNode).key == key) { if(isEntry) *isEntry = true; return &(nonstd::get(cNode)); } } else if(nonstd::holds_alternative(cNode)) { auto res = findContentGroup(&(nonstd::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(&nonstd::get(cNode) == node) { cNode = std::move(entry); return &(nonstd::get(cNode)); } } else if(nonstd::holds_alternative(cNode)) { auto res = replaceNodeByEntry(&(nonstd::get(cNode)), node, std::move(entry)); if(res) return res; } } return nullptr; } void SettingsGenerator::writeHeader(const SettingsType &settings) { QString incGuard = QFileInfo{_hdrFile.fileName()} .completeBaseName() .replace(QLatin1Char('.'), QLatin1Char('_')) .toUpper() + QStringLiteral("_H"); _hdr << "#ifndef " << incGuard << '\n' << "#define " << incGuard << "\n\n"; // write the includes auto includes = QList { {false, QStringLiteral("QtCore/QObject")}, {false, QStringLiteral("QtMvvmCore/ISettingsAccessor")}, {false, QStringLiteral("QtMvvmCore/SettingsEntry")} } + settings.includes; for(const auto &inc : includes) { if(inc.local) _hdr << "#include \"" << inc.includePath << "\"\n"; else _hdr << "#include <" << inc.includePath << ">\n"; } _hdr << "\n"; // create the class _hdr << "class " << QString{settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + settings.name.value() : settings.name.value()} << " : public QObject\n" << "{\n" << "\tQ_OBJECT\n\n" << "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n" << "public:\n" << "\tQ_INVOKABLE explicit " << settings.name.value() << "(QObject *parent = nullptr);\n" << "\texplicit " << settings.name.value() << "(QtMvvm::ISettingsAccessor *accessor, QObject *parent);\n\n" << "\tstatic " << settings.name.value() << " *instance();\n\n" << "\tQtMvvm::ISettingsAccessor *accessor() const;\n\n"; writeNodeElements(settings); _hdr << "\nprivate:\n" << "\tQtMvvm::ISettingsAccessor *_accessor;\n" << "};\n\n" << "#endif //" << incGuard << '\n'; } void SettingsGenerator::writeNodeElements(const NodeContentGroup &node, int intendent) { for(const auto &cNode : node.contentNodes) { if(nonstd::holds_alternative(cNode)) writeNode(nonstd::get(cNode), intendent); else if(nonstd::holds_alternative(cNode)) writeEntry(nonstd::get(cNode), intendent); else if(nonstd::holds_alternative(cNode)) writeNodeElements(nonstd::get(cNode), intendent); } } void SettingsGenerator::writeNode(const NodeType &node, int intendent) { _hdr << TABS << "struct {\n"; writeNodeElements(node, intendent + 1); _hdr << TABS << "} " << node.key << ";\n"; } void SettingsGenerator::writeEntry(const EntryType &entry, int intendent) { _hdr << TABS << "struct : QtMvvm::SettingsEntry<" << entry.type << "> {\n"; writeNodeElements(entry, intendent + 1); _hdr << TABS << "} " << entry.key << ";\n"; } void SettingsGenerator::writeSource(const SettingsType &settings) { _src << "#include \"" << _hdrFile.fileName() << "\"\n\n"; _src << settings.name.value() << "::" << settings.name.value() << "(QObject *parent) : \n" << "\t" << settings.name.value() << "{nullptr, parent}\n" << "{}\n\n"; _src << settings.name.value() << "::" << settings.name.value() << "(QtMvvm::ISettingsAccessor *accessor, QObject *parent) : \n" << "\tQObject{parent},\n" << "\t_accessor{accessor}\n" << "{}\n\n"; _src << settings.name.value() << " *" << settings.name.value() << "::instance()\n" << "{\n" << "\treturn nullptr;\n" << "}\n\n"; _src << "QtMvvm::ISettingsAccessor *" << settings.name.value() << "::accessor() const\n" << "{\n" << "\treturn _accessor;\n" << "}\n\n"; }