Browse Source

WIP settings generator

pull/2/head
Skycoder42 7 years ago
parent
commit
137382b205
No known key found for this signature in database GPG Key ID: 8E01AD9EF0578D2B
  1. 1
      tools/settingsgenerator/qsettingsgenerator.xsd
  2. 336
      tools/settingsgenerator/settingsgenerator.cpp
  3. 45
      tools/settingsgenerator/settingsgenerator.h

1
tools/settingsgenerator/qsettingsgenerator.xsd

@ -80,6 +80,7 @@
</xs:sequence> </xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional"/> <xs:attribute name="name" type="xs:string" use="optional"/>
<xs:attribute name="prefix" type="xs:string" use="optional"/> <xs:attribute name="prefix" type="xs:string" use="optional"/>
<xs:attribute name="baseKey" type="xs:string" use="optional"/>
</xs:complexType> </xs:complexType>
<!-- root elements--> <!-- root elements-->

336
tools/settingsgenerator/settingsgenerator.cpp

@ -1,5 +1,9 @@
#include "settingsgenerator.h" #include "settingsgenerator.h"
#include <QDebug> #include <QDebug>
#include <QFileInfo>
#include <QDir>
#define TABS QString{QLatin1Char('\t')}.repeated(intendent)
SettingsGenerator::SettingsGenerator(const QString &inPath, const QString &hdrPath, const QString &srcPath) : SettingsGenerator::SettingsGenerator(const QString &inPath, const QString &hdrPath, const QString &srcPath) :
_inFile{inPath}, _inFile{inPath},
@ -19,177 +23,293 @@ void SettingsGenerator::process()
if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text))
throwFile(_srcFile); throwFile(_srcFile);
readData();
}
void SettingsGenerator::readData()
{
if(!_xml.readNextStartElement()) if(!_xml.readNextStartElement())
throwReader(); throwReader(_xml);
if(_xml.name() != QStringLiteral("Settings")) if(_xml.name() != QStringLiteral("Settings"))
throwChild(); throwChild(_xml);
auto data = readSettings(_xml);
_data = readSettings(); checkError(_xml);
checkError();
qDebug() << "Completed parsing xml - it is valid"; 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 include;
include.local = readAttrib<bool>(QStringLiteral("local"), false); include.local = readAttrib<bool>(xml, QStringLiteral("local"), false);
include.include = _xml.readElementText(); include.include = xml.readElementText();
checkError(); checkError(xml);
return include; return include;
} }
SettingsGenerator::Param SettingsGenerator::readParam() SettingsGenerator::Param SettingsGenerator::readParam(QXmlStreamReader &xml)
{ {
Param param; Param param;
param.key = readAttrib(QStringLiteral("key"), {}, true); param.key = readAttrib(xml, QStringLiteral("key"), {}, true);
param.type = readAttrib(QStringLiteral("type"), {}, true); param.type = readAttrib(xml, QStringLiteral("type"), {}, true);
param.asStr = readAttrib<bool>(QStringLiteral("asStr"), false); param.asStr = readAttrib<bool>(xml, QStringLiteral("asStr"), false);
param.value = _xml.readElementText(); param.value = xml.readElementText();
checkError(); checkError(xml);
return param; return param;
} }
QSharedPointer<SettingsGenerator::Backend> SettingsGenerator::readBackend() QSharedPointer<SettingsGenerator::Backend> SettingsGenerator::readBackend(QXmlStreamReader &xml)
{ {
auto backend = QSharedPointer<Backend>::create(); auto backend = QSharedPointer<Backend>::create();
backend->className = readAttrib(QStringLiteral("class"), {}, true); backend->className = readAttrib(xml, QStringLiteral("class"), {}, true);
while(_xml.readNextStartElement()) { while(xml.readNextStartElement()) {
if(_xml.name() == QStringLiteral("Param")) if(xml.name() == QStringLiteral("Param"))
backend->params.append(readParam()); backend->params.append(readParam(xml));
else else
throwChild(); throwChild(xml);
} }
checkError(); checkError(xml);
return backend; return backend;
} }
QPair<QString, QString> SettingsGenerator::readTypeMapping() QPair<QString, QString> SettingsGenerator::readTypeMapping(QXmlStreamReader &xml)
{ {
QPair<QString, QString> mapping; QPair<QString, QString> mapping;
mapping.first = readAttrib(QStringLiteral("key"), {}, true); mapping.first = readAttrib(xml, QStringLiteral("key"), {}, true);
mapping.second = readAttrib(QStringLiteral("type"), {}, true); mapping.second = readAttrib(xml, QStringLiteral("type"), {}, true);
checkError(); checkError(xml);
return mapping; return mapping;
} }
SettingsGenerator::Node SettingsGenerator::readNode() SettingsGenerator::Node SettingsGenerator::readNode(QXmlStreamReader &xml)
{ {
Node node; Node node;
node.key = readAttrib(QStringLiteral("key"), {}, true); node.key = readAttrib(xml, QStringLiteral("key"), {}, true);
while(_xml.readNextStartElement()) { while(xml.readNextStartElement()) {
if(_xml.name() == QStringLiteral("Node")) if(xml.name() == QStringLiteral("Node"))
node.subNodes.append(readNode()); node.subNodes.append(readNode(xml));
else if(_xml.name() == QStringLiteral("Entry")) else if(xml.name() == QStringLiteral("Entry"))
node.subEntries.append(readEntry()); node.subEntries.append(readEntry(xml));
else if(_xml.name() == QStringLiteral("Import")) else if(xml.name() == QStringLiteral("Import"))
node.subImports.append(readImport()); node.subImports.append(readImport(xml));
else else
throwChild(); throwChild(xml);
} }
checkError(); checkError(xml);
return node; return node;
} }
SettingsGenerator::Entry SettingsGenerator::readEntry() SettingsGenerator::Entry SettingsGenerator::readEntry(QXmlStreamReader &xml)
{ {
Entry entry; Entry entry;
entry.key = readAttrib(QStringLiteral("key"), {}, true); entry.key = readAttrib(xml, QStringLiteral("key"), {}, true);
entry.type = readAttrib(QStringLiteral("type"), {}, true); entry.type = readAttrib(xml, QStringLiteral("type"), {}, true);
entry.qmlGroupKey = readAttrib(QStringLiteral("qmlGroupKey")); entry.qmlGroupKey = readAttrib(xml, QStringLiteral("qmlGroupKey"));
entry.defaultValue = readAttrib(QStringLiteral("default")); entry.defaultValue = readAttrib(xml, QStringLiteral("default"));
entry.tr = readAttrib<bool>(QStringLiteral("tr"), false); entry.tr = readAttrib<bool>(xml, QStringLiteral("tr"), false);
entry.trContext = readAttrib(QStringLiteral("trContext")); entry.trContext = readAttrib(xml, QStringLiteral("trContext"));
while(_xml.readNextStartElement()) { while(xml.readNextStartElement()) {
if(_xml.name() == QStringLiteral("Code")) { if(xml.name() == QStringLiteral("Code")) {
if(!entry.defaultValue.isNull()) if(!entry.defaultValue.isNull())
throwChild(); throwChild(xml);
entry.defaultValue = _xml.readElementText(); entry.defaultValue = xml.readElementText();
entry.defaultIsCode = true; entry.defaultIsCode = true;
checkError(); checkError(xml);
} else if(_xml.name() == QStringLiteral("Node")) } else if(xml.name() == QStringLiteral("Node"))
entry.subNodes.append(readNode()); entry.subNodes.append(readNode(xml));
else if(_xml.name() == QStringLiteral("Entry")) else if(xml.name() == QStringLiteral("Entry"))
entry.subEntries.append(readEntry()); entry.subEntries.append(readEntry(xml));
else if(_xml.name() == QStringLiteral("Import")) else if(xml.name() == QStringLiteral("Import"))
entry.subImports.append(readImport()); entry.subImports.append(readImport(xml));
else else
throwChild(); throwChild(xml);
} }
checkError(); checkError(xml);
return entry; return entry;
} }
SettingsGenerator::Import SettingsGenerator::readImport() SettingsGenerator::Import SettingsGenerator::readImport(QXmlStreamReader &xml)
{ {
Import import; auto required = readAttrib<bool>(xml, QStringLiteral("required"), true);
import.required = readAttrib<bool>(QStringLiteral("required"), true); auto rootNode = readAttrib(xml, QStringLiteral("rootNode")).split(QLatin1Char('/'), QString::SkipEmptyParts);
import.rootNode = readAttrib(QStringLiteral("rootNode")); auto path = xml.readElementText();
import.import = _xml.readElementText(); checkError(xml);
checkError();
return import; auto pInfo = QFileInfo{static_cast<QFileDevice*>(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('/'))));
} }
SettingsGenerator::Settings SettingsGenerator::readSettings() return std::move(subSettings);
}
SettingsGenerator::Settings SettingsGenerator::readSettings(QXmlStreamReader &xml)
{ {
Settings settings; Settings settings;
settings.name = readAttrib(QStringLiteral("name"), {}, true); settings.name = readAttrib(xml, QStringLiteral("name"), {}, true);
settings.prefix = readAttrib(QStringLiteral("prefix")); settings.prefix = readAttrib(xml, QStringLiteral("prefix"));
while(_xml.readNextStartElement()) { while(xml.readNextStartElement()) {
if(_xml.name() == QStringLiteral("Include")) if(xml.name() == QStringLiteral("Include"))
settings.includes.append(readInclude()); settings.includes.append(readInclude(xml));
else if(_xml.name() == QStringLiteral("Backend")) else if(xml.name() == QStringLiteral("Backend"))
settings.backend = readBackend(); settings.backend = readBackend(xml);
else if(_xml.name() == QStringLiteral("TypeMapping")) { else if(xml.name() == QStringLiteral("TypeMapping")) {
auto mapping = readTypeMapping(); auto mapping = readTypeMapping(xml);
settings.typeMapping.insert(mapping.first, mapping.second); settings.typeMapping.insert(mapping.first, mapping.second);
} else if(_xml.name() == QStringLiteral("Node")) } else if(xml.name() == QStringLiteral("Node"))
settings.subNodes.append(readNode()); settings.subNodes.append(readNode(xml));
else if(_xml.name() == QStringLiteral("Entry")) else if(xml.name() == QStringLiteral("Entry"))
settings.subEntries.append(readEntry()); settings.subEntries.append(readEntry(xml));
else if(_xml.name() == QStringLiteral("Import")) else if(xml.name() == QStringLiteral("Import"))
settings.subImports.append(readImport()); settings.subImports.append(readImport(xml));
else else
throwChild(); throwChild(xml);
} }
checkError(); checkError(xml);
return settings; 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()) if(xml.hasError())
throwReader(); throwReader(xml);
} }
template<typename T> template<typename T>
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)) if(xml.attributes().hasAttribute(key))
return QVariant(_xml.attributes().value(key).toString()).template value<T>(); return QVariant(xml.attributes().value(key).toString()).template value<T>();
else if(required) 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 else
return defaultValue; return defaultValue;
} }
template<> template<>
bool SettingsGenerator::readAttrib<bool>(const QString &key, const bool &defaultValue, bool required) const bool SettingsGenerator::readAttrib<bool>(QXmlStreamReader &xml, const QString &key, const bool &defaultValue, bool required) const
{ {
if(_xml.attributes().hasAttribute(key)) { if(xml.attributes().hasAttribute(key)) {
if(_xml.attributes().value(key) == QStringLiteral("true")) if(xml.attributes().value(key) == QStringLiteral("true"))
return true; return true;
else if(_xml.attributes().value(key) == QStringLiteral("false")) else if(xml.attributes().value(key) == QStringLiteral("false"))
return false; return false;
else 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) } 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 else
return defaultValue; return defaultValue;
} }
@ -199,16 +319,16 @@ void SettingsGenerator::throwFile(const QFileDevice &file) const
throw QStringLiteral("%1: %2").arg(file.fileName(), file.errorString()); 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") throw QStringLiteral("%1:%2:%3: %4")
.arg(dynamic_cast<QFileDevice*>(_xml.device())->fileName()) .arg(dynamic_cast<QFileDevice*>(xml.device())->fileName())
.arg(_xml.lineNumber()) .arg(xml.lineNumber())
.arg(_xml.columnNumber()) .arg(xml.columnNumber())
.arg(overwriteError.isNull() ? _xml.errorString() : overwriteError); .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()));
} }

45
tools/settingsgenerator/settingsgenerator.h

@ -59,44 +59,51 @@ private:
QString trContext; QString trContext;
}; };
struct Import { struct Import : public Node {
bool required; inline Import() = default;
QString rootNode; inline Import(Node &&node) :
QString import; Node(std::move(node))
{}
}; };
struct Settings : public Node { struct Settings : public Node {
QString name; QString name;
QString prefix; QString prefix;
QString baseKey;
QList<Include> includes; QList<Include> includes;
QSharedPointer<Backend> backend; QSharedPointer<Backend> backend;
QHash<QString, QString> typeMapping; QHash<QString, QString> typeMapping;
} _data; };
Include readInclude(QXmlStreamReader &xml);
Param readParam(QXmlStreamReader &xml);
QSharedPointer<Backend> readBackend(QXmlStreamReader &xml);
QPair<QString, QString> readTypeMapping(QXmlStreamReader &xml);
void readData(); Node readNode(QXmlStreamReader &xml);
Entry readEntry(QXmlStreamReader &xml);
Import readImport(QXmlStreamReader &xml);
Include readInclude(); Settings readSettings(QXmlStreamReader &xml);
Param readParam();
QSharedPointer<Backend> readBackend();
QPair<QString, QString> readTypeMapping();
Node readNode(); void writeHeader(const Settings &settings);
Entry readEntry(); void writeNodeElements(const Node &node, int intendent = 1);
Import readImport(); void writeNode(const Node &node, int intendent = 1);
void writeEntry(const Entry &entry, int intendent = 1);
Settings readSettings(); void writeSource(const Settings &settings);
template <typename T = QString> template <typename T = QString>
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 throwFile(const QFileDevice &file) const;
Q_NORETURN void throwReader(const QString &overwriteError = {}) const; Q_NORETURN void throwReader(QXmlStreamReader &xml, const QString &overwriteError = {}) const;
Q_NORETURN void throwChild(); Q_NORETURN void throwChild(QXmlStreamReader &xml);
}; };
template<> template<>
bool SettingsGenerator::readAttrib<bool>(const QString &key, const bool &defaultValue, bool required) const; bool SettingsGenerator::readAttrib<bool>(QXmlStreamReader &xml, const QString &key, const bool &defaultValue, bool required) const;
#endif // SETTINGSGENERATOR_H #endif // SETTINGSGENERATOR_H

Loading…
Cancel
Save