From 8963d0abc2ef3224edc2618a04d53ca7c7ea714f Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Fri, 29 Jun 2018 23:54:01 +0200 Subject: [PATCH] implemented xml parser --- tools/settingsgenerator/main.cpp | 51 ++++- .../settingsgenerator/qsettingsgenerator.xsd | 16 +- tools/settingsgenerator/settingsgenerator.cpp | 214 ++++++++++++++++++ tools/settingsgenerator/settingsgenerator.h | 102 +++++++++ tools/settingsgenerator/settingsgenerator.pro | 6 +- 5 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 tools/settingsgenerator/settingsgenerator.cpp create mode 100644 tools/settingsgenerator/settingsgenerator.h diff --git a/tools/settingsgenerator/main.cpp b/tools/settingsgenerator/main.cpp index 054cc68..8bfcb6b 100644 --- a/tools/settingsgenerator/main.cpp +++ b/tools/settingsgenerator/main.cpp @@ -1,8 +1,57 @@ #include +#include +#include +#include +#include + +#include "settingsgenerator.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral(TARGET)); + QCoreApplication::setApplicationVersion(QStringLiteral(VERSION)); + QCoreApplication::setOrganizationName(QStringLiteral(COMPANY)); + QCoreApplication::setOrganizationDomain(QStringLiteral(BUNDLE_PREFIX)); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("A tool to...")); //TODO description + parser.addVersionOption(); + parser.addHelpOption(); + + parser.addOption({ + {QStringLiteral("t"), QStringLiteral("translate")}, + QStringLiteral("Translate the given settings file. The output will be" + "a dummy cpp file, writte to --impl.") + }); + parser.addOption({ + QStringLiteral("in"), + QStringLiteral("The input XML containing the settings definition"), + QStringLiteral("file") + }); + parser.addOption({ + QStringLiteral("header"), + QStringLiteral("The of the header file to generate"), + QStringLiteral("name") + }); + parser.addOption({ + QStringLiteral("impl"), + QStringLiteral("The of the implementation file to generate"), + QStringLiteral("name") + }); + + parser.process(a); - return a.exec(); + try { + SettingsGenerator generator { + parser.value(QStringLiteral("in")), + parser.value(QStringLiteral("header")), + parser.value(QStringLiteral("impl")) + }; + generator.process(); + return EXIT_SUCCESS; + } catch (const QString &str) { + qCritical() << qUtf8Printable(str); + return EXIT_FAILURE; + } } diff --git a/tools/settingsgenerator/qsettingsgenerator.xsd b/tools/settingsgenerator/qsettingsgenerator.xsd index f35c58c..e9e51cd 100644 --- a/tools/settingsgenerator/qsettingsgenerator.xsd +++ b/tools/settingsgenerator/qsettingsgenerator.xsd @@ -9,12 +9,12 @@ - + - + @@ -23,7 +23,7 @@ - + @@ -33,14 +33,14 @@ - + - + @@ -51,7 +51,7 @@ - + @@ -62,11 +62,11 @@ - + - + diff --git a/tools/settingsgenerator/settingsgenerator.cpp b/tools/settingsgenerator/settingsgenerator.cpp new file mode 100644 index 0000000..72c2cbf --- /dev/null +++ b/tools/settingsgenerator/settingsgenerator.cpp @@ -0,0 +1,214 @@ +#include "settingsgenerator.h" +#include + +SettingsGenerator::SettingsGenerator(const QString &inPath, const QString &hdrPath, const QString &srcPath) : + _inFile{inPath}, + _hdrFile{hdrPath}, + _srcFile{srcPath}, + _xml{&_inFile}, + _hdr{&_hdrFile}, + _src{&_srcFile} +{} + +void SettingsGenerator::process() +{ + if(!_inFile.open(QIODevice::ReadOnly | QIODevice::Text)) + throwFile(_inFile); + if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text)) + throwFile(_hdrFile); + if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) + throwFile(_srcFile); + + readData(); +} + +void SettingsGenerator::readData() +{ + if(!_xml.readNextStartElement()) + throwReader(); + if(_xml.name() != QStringLiteral("Settings")) + throwChild(); + + _data = readSettings(); + checkError(); + qDebug() << "Completed parsing xml - it is valid"; +} + +SettingsGenerator::Include SettingsGenerator::readInclude() +{ + Include include; + include.local = readAttrib(QStringLiteral("local"), false); + include.include = _xml.readElementText(); + checkError(); + return include; +} + +SettingsGenerator::Param SettingsGenerator::readParam() +{ + 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(); + return param; +} + +QSharedPointer SettingsGenerator::readBackend() +{ + auto backend = QSharedPointer::create(); + backend->className = readAttrib(QStringLiteral("class"), {}, true); + while(_xml.readNextStartElement()) { + if(_xml.name() == QStringLiteral("Param")) + backend->params.append(readParam()); + else + throwChild(); + } + checkError(); + return backend; +} + +QPair SettingsGenerator::readTypeMapping() +{ + QPair mapping; + mapping.first = readAttrib(QStringLiteral("key"), {}, true); + mapping.second = readAttrib(QStringLiteral("type"), {}, true); + checkError(); + return mapping; +} + +SettingsGenerator::Node SettingsGenerator::readNode() +{ + 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()); + else + throwChild(); + } + checkError(); + return node; +} + +SettingsGenerator::Entry SettingsGenerator::readEntry() +{ + 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")) { + if(!entry.defaultValue.isNull()) + throwChild(); + 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()); + else + throwChild(); + } + checkError(); + return entry; +} + +SettingsGenerator::Import SettingsGenerator::readImport() +{ + Import import; + import.required = readAttrib(QStringLiteral("required"), true); + import.rootNode = readAttrib(QStringLiteral("rootNode")); + import.import = _xml.readElementText(); + checkError(); + return import; +} + +SettingsGenerator::Settings SettingsGenerator::readSettings() +{ + 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.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 + throwChild(); + } + checkError(); + return settings; +} + +void SettingsGenerator::checkError() const +{ + if(_xml.hasError()) + throwReader(); +} + +template +T SettingsGenerator::readAttrib(const QString &key, const T &defaultValue, bool required) const +{ + 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)); + else + return defaultValue; +} + +template<> +bool SettingsGenerator::readAttrib(const QString &key, const bool &defaultValue, bool required) const +{ + if(_xml.attributes().hasAttribute(key)) { + if(_xml.attributes().value(key) == QStringLiteral("true")) + return true; + else if(_xml.attributes().value(key) == QStringLiteral("false")) + return false; + else + throwReader(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)); + else + return defaultValue; +} + +void SettingsGenerator::throwFile(const QFileDevice &file) const +{ + throw QStringLiteral("%1: %2").arg(file.fileName(), file.errorString()); +} + +void SettingsGenerator::throwReader(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); +} + +void SettingsGenerator::throwChild() +{ + throwReader(QStringLiteral("Unexpected child element: %1").arg(_xml.name())); +} diff --git a/tools/settingsgenerator/settingsgenerator.h b/tools/settingsgenerator/settingsgenerator.h new file mode 100644 index 0000000..26dae83 --- /dev/null +++ b/tools/settingsgenerator/settingsgenerator.h @@ -0,0 +1,102 @@ +#ifndef SETTINGSGENERATOR_H +#define SETTINGSGENERATOR_H + +#include +#include +#include +#include + +class SettingsGenerator +{ +public: + SettingsGenerator(const QString &inPath, + const QString &hdrPath, + const QString &srcPath); + + void process(); + +private: + QFile _inFile; + QFile _hdrFile; + QFile _srcFile; + + QXmlStreamReader _xml; + QTextStream _hdr; + QTextStream _src; + + struct Include{ + bool local; + QString include; + }; + + struct Param { + QString key; + QString type; + bool asStr; + QString value; + }; + + struct Backend { + QString className; + QList params; + }; + + struct Entry; + struct Import; + struct Node { + QString key; + QList subNodes; + QList subEntries; + QList subImports; + }; + + struct Entry : public Node { + QString type; + QString qmlGroupKey; + QString defaultValue; + bool defaultIsCode = false; + bool tr; + QString trContext; + }; + + struct Import { + bool required; + QString rootNode; + QString import; + }; + + struct Settings : public Node { + QString name; + QString prefix; + + QList includes; + QSharedPointer backend; + QHash typeMapping; + } _data; + + void readData(); + + Include readInclude(); + Param readParam(); + QSharedPointer readBackend(); + QPair readTypeMapping(); + + Node readNode(); + Entry readEntry(); + Import readImport(); + + Settings readSettings(); + + template + T readAttrib(const QString &key, const T &defaultValue = {}, bool required = false) const; + + void checkError() const; + Q_NORETURN void throwFile(const QFileDevice &file) const; + Q_NORETURN void throwReader(const QString &overwriteError = {}) const; + Q_NORETURN void throwChild(); +}; + +template<> +bool SettingsGenerator::readAttrib(const QString &key, const bool &defaultValue, bool required) const; + +#endif // SETTINGSGENERATOR_H diff --git a/tools/settingsgenerator/settingsgenerator.pro b/tools/settingsgenerator/settingsgenerator.pro index 3489a7f..f203896 100644 --- a/tools/settingsgenerator/settingsgenerator.pro +++ b/tools/settingsgenerator/settingsgenerator.pro @@ -13,10 +13,12 @@ DEFINES += "VERSION=\\\"$$VERSION\\\"" DEFINES += "COMPANY=\\\"$$COMPANY\\\"" DEFINES += "BUNDLE_PREFIX=\\\"$$BUNDLE_PREFIX\\\"" -HEADERS += +HEADERS += \ + settingsgenerator.h SOURCES += \ - main.cpp + main.cpp \ + settingsgenerator.cpp load(qt_tool)