summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2016-02-16 15:18:14 +0100
committerMarkus Mittendrein <git@maxmitti.tk>2016-02-16 15:18:14 +0100
commit681f2be56c45e3945cd6cbc56ac14317a97ec9c1 (patch)
tree8c0a78a97ab82a22df6802f5c49e3e1077fb9161
downloadqt-config-681f2be56c45e3945cd6cbc56ac14317a97ec9c1.tar.gz
qt-config-681f2be56c45e3945cd6cbc56ac14317a97ec9c1.zip
Initial commit
-rw-r--r--ConfigBase.cpp207
-rw-r--r--ConfigBase.hpp330
2 files changed, 537 insertions, 0 deletions
diff --git a/ConfigBase.cpp b/ConfigBase.cpp
new file mode 100644
index 0000000..ce4bcc0
--- /dev/null
+++ b/ConfigBase.cpp
@@ -0,0 +1,207 @@
+#include "CRSMConfig.hpp"
+
+#include <QFile>
+
+void ConfigBase::addConfigValue(QString name, ConfigValueBase* value)
+{
+ configValues[name] = value;
+}
+
+void ConfigBase::setConfigValue(const QString &name, const QString &value)
+{
+ ConfigValueBase& configValue = getConfigValue(name);
+ configValue.setValue(Util::unescape(value));
+}
+
+QString ConfigBase::getConfigValueLine(const QString &name)
+{
+ if(configValues.contains(name))
+ {
+ ConfigValueBase& value = *configValues[name];
+ switch(value.type())
+ {
+ case ConfigValueType::Map:
+ {
+ QString ret;
+ const QMap<QString, QString>& values = (dynamic_cast<ConfigValueMap*>(&value))->getValues();
+ foreach(const QString& mapKey, values.keys())
+ {
+ ret += name + "[" + Util::escape(mapKey, '\\', "]") + "]" + " = " + Util::escape(values.value(mapKey)) + "\n";
+ }
+ return ret;
+ break;
+ }
+ case ConfigValueType::List:
+ {
+ QString ret;
+ foreach(const QString& val, (dynamic_cast<ConfigValueList*>(&value))->getValues())
+ {
+ ret += name + " += " + Util::escape(val) + "\n";
+ }
+ return ret;
+ break;
+ }
+ default:
+ return name + " = " + Util::escape(value.value()) + "\n";
+ }
+ }
+ else
+ {
+ throw ConfigException("Unknown config value \"" + name.toStdString() + "\"");
+ }
+}
+
+void ConfigBase::addConfigListEntry(const QString &name, const QString &value)
+{
+ ConfigValueBase& configValue = getConfigValue(name);
+ if(configValue.type() != ConfigValueType::List)
+ {
+ throw ConfigException("Cannot add a list entry to a non-list-config value \"" + name.toStdString() + "\"");
+ }
+ ((ConfigValueList*)&configValue)->append(Util::unescape(value));
+}
+
+void ConfigBase::removeConfigMapListEntry(const QString &name, const QString &value)
+{
+ ConfigValueBase& configValue = getConfigValue(name);
+ if(configValue.type() == ConfigValueType::List)
+ {
+ ((ConfigValueList*)&configValue)->remove(Util::unescape(value));
+ }
+ else if(configValue.type() == ConfigValueType::Map)
+ {
+ ((ConfigValueMap*)&configValue)->remove(Util::unescape(value));
+ }
+ else
+ {
+ throw ConfigException("Cannot remove a list entry from a value which is neither a list nor a map \"" + name.toStdString() + "\"");
+ }
+}
+
+void ConfigBase::setConfigMapValue(const QString &name, const QString &key, const QString &value)
+{
+ ConfigValueBase& configValue = getConfigValue(name);
+ if(configValue.type() != ConfigValueType::Map)
+ {
+ throw ConfigException("Cannot set a map value on a non-map-config value \"" + name.toStdString() + "\"");
+ }
+ ((ConfigValueMap*)&configValue)->setKeyValue(Util::unescape(key), Util::unescape(value));
+}
+
+ConfigValueBase& ConfigBase::getConfigValue(const QString& name)
+{
+ if(!configValues.contains(name))
+ {
+ throw ConfigException("Unknown config value \"" + name.toStdString() + "\"");
+ }
+ else
+ {
+ return *configValues[name];
+ }
+}
+
+QString ConfigBase::read(const QString &fileName, bool writeDefault)
+{
+ curFileName = fileName;
+ QString ret = "";
+
+ QFile config(fileName);
+ if(!config.exists())
+ {
+ if(writeDefault)
+ {
+ if(write(fileName))
+ {
+ return fileName + ": The config-file did not exist, a new one with default values has been created.\n";
+ }
+ else
+ {
+ return fileName + ": The config-file did not exist, a new one could not be created.\n";
+ }
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ else if(config.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ QString line;
+ for(size_t lineNr = 1; !config.atEnd(); ++lineNr)
+ {
+ try
+ {
+ line = config.readLine().trimmed();
+ if(!line.isEmpty())
+ {
+ setConfigLine(line);
+ }
+ }
+ catch(ConfigException e)
+ {
+ ret += fileName + ":" + QString::number(lineNr) + ": " + e.what() + "\n";
+ }
+ }
+ }
+ else
+ {
+ return fileName + ": The config-file could not be read.\n";
+ }
+
+ return ret;
+}
+
+bool ConfigBase::write(QString fileName)
+{
+ if(fileName.isEmpty())
+ {
+ fileName = curFileName;
+ }
+ if(fileName.isEmpty())
+ {
+ return false;
+ }
+ QFile config(fileName);
+ if(config.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
+ {
+ foreach(const QString& key, configValues.keys())
+ {
+ config.write(getConfigValueLine(key).toUtf8());
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void ConfigBase::setConfigLine(const QString &line)
+{
+ static QRegExp valueExp(R"(^([^=]+)=(.*)$)");
+ static QRegExp listExp(R"(^([^=]+)\+=(.*)$)");
+ static QRegExp listMapRemoveExp(R"(^([^=]+)\-=(.*)$)");
+ static QRegExp mapExp(R"(^([^\[]+)\[((?:[^\]\\]|\\\]|\\\\)+)\]\s*=\s*(.*)$)");
+
+ if(listExp.exactMatch(line))
+ {
+ addConfigListEntry(listExp.cap(1).trimmed(), listExp.cap(2).trimmed());
+ }
+ else if(listMapRemoveExp.exactMatch(line))
+ {
+ removeConfigMapListEntry(listMapRemoveExp.cap(1).trimmed(), listMapRemoveExp.cap(2).trimmed());
+ }
+ else if(mapExp.exactMatch(line))
+ {
+ setConfigMapValue(mapExp.cap(1).trimmed(), mapExp.cap(2).trimmed(), mapExp.cap(3).trimmed());
+ }
+ else if(valueExp.exactMatch(line))
+ {
+ setConfigValue(valueExp.cap(1).trimmed(), valueExp.cap(2).trimmed());
+ }
+ else
+ {
+ throw ConfigException("Syntax error: unkown syntax");
+ }
+}
diff --git a/ConfigBase.hpp b/ConfigBase.hpp
new file mode 100644
index 0000000..ade454a
--- /dev/null
+++ b/ConfigBase.hpp
@@ -0,0 +1,330 @@
+#ifndef CONFIGBASE
+#define CONFIGBASE
+
+#include <QString>
+#include <QStringList>
+#include <QList>
+#include <QMap>
+#include <QRegularExpression>
+
+#include <exception>
+#include <type_traits>
+#include <limits>
+
+#include "Util.hpp"
+
+using ConfigException = std::logic_error;
+
+namespace ConfigValueType {
+enum Type {
+ None,
+ String,
+ Integer,
+ Boolean,
+ List,
+ Map
+};
+}
+
+class ConfigValueBase {
+public:
+
+ virtual void setValue(const QString&) { }
+ virtual QString value() { return ""; }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::None; }
+
+
+
+ template<typename Type>
+ static Type getValue(const QString& value);
+
+ template<typename Type>
+ static QString getStringValue(Type val);
+};
+
+template<typename Type, typename = void>
+class ConfigValue : public ConfigValueBase {
+public:
+ virtual void setValue(const QString&) { throw ConfigException("Cannot assign"); }
+ virtual QString value() { throw ConfigException("Cannot get the value"); }
+};
+
+using String = QString;
+using Integer = int;
+using Port = ushort;
+using Boolean = bool;
+#define List(Type) QList<Type>
+#define Map(KeyType, ValueType) QMap<KeyType, ValueType>
+
+
+#define ConfigVal(Value) {#Value, mkConfigValue(Value)}
+#define ConfigValEx(Value, ...) {#Value, mkConfigValue(Value, __VA_ARGS__)}
+
+template<>
+class ConfigValue<String> : public ConfigValueBase {
+ String& config;
+ bool trimmedQuotes = false;
+
+public:
+ ConfigValue(String& config) : config(config) { }
+
+ virtual void setValue(const QString& value) { config = Util::trimQuotes(value, trimmedQuotes); }
+ virtual QString value()
+ {
+ static QRegularExpression quoteExp(R"(^(\s.*|.*\s)$)");
+ if(trimmedQuotes || quoteExp.match(config).hasMatch())
+ {
+ return "\"" + config + "\"";
+ }
+ else
+ {
+ return config;
+ }
+ }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::String; }
+};
+
+template<typename Type>
+class ConfigValue<Type, typename std::enable_if<std::is_integral<Type>::value>::type> : public ConfigValueBase {
+ Type& config;
+
+public:
+ ConfigValue(Type& config) : config(config) { }
+
+ virtual void setValue(const QString& value)
+ {
+ bool ok = true;
+ long long longLongVal = value.toLongLong(&ok, 0);
+ if(!ok) throw ConfigException("\"" + value.toStdString() + "\" could not be parsed as an Integer");
+ if(longLongVal < std::numeric_limits<Type>::min() || longLongVal > std::numeric_limits<Type>::max()) throw ConfigException((QString::number(longLongVal) + " is out of range (" + QString::number(std::numeric_limits<Type>::min()) + " - " + QString::number(std::numeric_limits<Type>::max()) + ")").toStdString());
+ config = static_cast<Type>(longLongVal);
+ }
+ virtual QString value() { return QString::number(config); }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::Integer; }
+};
+
+template<>
+class ConfigValue<Boolean> : public ConfigValueBase {
+ Boolean& config;
+
+public:
+ ConfigValue(Boolean& config) : config(config) { }
+
+ virtual void setValue(const QString& value)
+ {
+ if(value == "true")
+ {
+ config = true;
+ }
+ else if(value == "false")
+ {
+ config = false;
+ }
+ else
+ {
+ throw ConfigException("\"" + value.toStdString() + "\" could not be parsed as a Boolean");
+ }
+ }
+ virtual QString value() { return config ? "true" : "false"; }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::Boolean; }
+};
+
+class ConfigValueList : public ConfigValueBase {
+public:
+ virtual void append(const QString& entry) = 0;
+ virtual void remove(const QString& entry) = 0;
+ virtual QStringList getValues() = 0;
+};
+
+class ConfigValueMap : public ConfigValueBase {
+public:
+ virtual void setKeyValue(const QString& key, const QString& value) = 0;
+ virtual void remove(const QString& key) = 0;
+ virtual QMap<QString, QString> getValues() = 0;
+};
+
+template<typename Type>
+class ConfigValue<QList<Type>> : public ConfigValueList {
+ using ListType = QList<Type>;
+
+ ListType& config;
+ const bool deduplicate;
+ const QChar splitChar;
+
+public:
+ ConfigValue(ListType& config, bool deduplicate = true, const QChar splitChar = ';') : config(config), deduplicate(deduplicate), splitChar(splitChar) {}
+
+ virtual ListType& list() { return config; }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::List; }
+
+ virtual void append(const QString &entry)
+ {
+ Type val = ConfigValueBase::getValue<Type>(entry);
+ if(!deduplicate || !config.contains(val))
+ {
+ config.append(val);
+ }
+ }
+
+ virtual void remove(const QString &entry) { config.removeAll(ConfigValueBase::getValue<Type>(entry)); }
+
+ virtual QStringList getValues()
+ {
+ QStringList ret;
+
+ foreach(Type val, config)
+ {
+ ret.append(ConfigValueBase::getStringValue(val));
+ }
+
+ return ret;
+ }
+
+ void setValue(const QString& value)
+ {
+ config.clear();
+ if(value.isEmpty())
+ {
+ return;
+ }
+ QStringList parts = Util::splitEscaped(value, splitChar);
+ foreach(const QString& part, parts)
+ {
+ config.append(ConfigValueBase::getValue<Type>(part));
+ }
+ }
+
+ QString value()
+ {
+ QStringList parts;
+ foreach(const Type& part, config)
+ {
+ parts.append(ConfigValueBase::getStringValue(part));
+ }
+ return Util::joinEscape(parts, splitChar);
+ }
+};
+
+template<typename KeyType, typename ValueType>
+class ConfigValue<QMap<KeyType, ValueType>> : public ConfigValueMap {
+ using MapType = QMap<KeyType, ValueType>;
+
+ MapType& config;
+ const QChar assignChar;
+ const QChar splitChar;
+
+public:
+ ConfigValue(MapType& config, const QChar assignChar = ':', const QChar splitChar = ';') : config(config), assignChar(assignChar), splitChar(splitChar) {}
+
+ virtual MapType& map() { return config; }
+ virtual ConfigValueType::Type type() { return ConfigValueType::Type::Map; }
+
+ virtual void setKeyValue(const QString& key, const QString& value)
+ {
+ config[ConfigValueBase::getValue<KeyType>(key)] = ConfigValueBase::getValue<ValueType>(value);
+ }
+
+ virtual QMap<QString, QString> getValues()
+ {
+ QMap<QString, QString> ret;
+
+ foreach(KeyType key, config.keys())
+ {
+ ret[ConfigValueBase::getStringValue(key)] = ConfigValueBase::getStringValue(config[key]);
+ }
+
+ return ret;
+ }
+
+ virtual void remove(const QString &key)
+ {
+ config.remove(ConfigValueBase::getValue<KeyType>(key));
+ }
+
+ QString value()
+ {
+ QStringList ret;
+ foreach(const KeyType& key, config.keys())
+ {
+ ret.append(Util::joinEscape({ConfigValueBase::getStringValue(key), ConfigValueBase::getStringValue(config.value(key))}, assignChar));
+ }
+ return Util::joinEscape(ret, splitChar);
+ }
+
+ void setValue(const QString& value)
+ {
+ config.clear();
+ if(value.isEmpty())
+ {
+ return;
+ }
+ QStringList parts = Util::splitEscaped(value, splitChar);
+ foreach(const QString& part, parts)
+ {
+ QStringList parts = Util::splitEscaped(part, assignChar);
+ if(parts.length() != 2)
+ {
+ throw ConfigException("Cannot parse corrupt map key-value-pair");
+ }
+ config[ConfigValueBase::getValue<KeyType>(parts.first())] = ConfigValueBase::getValue<ValueType>(parts.at(1));
+ }
+ }
+};
+
+template<typename Type>
+class ConfigValue<Type, typename std::enable_if<std::is_enum<Type>::value>::type> : public ConfigValue<typename std::underlying_type<Type>::type> {
+ using UndType = typename std::underlying_type<Type>::type;
+public:
+ ConfigValue(Type& config) : ConfigValue<UndType>::ConfigValue((UndType&)config) {}
+};
+
+template<typename Type, typename... Types>
+ConfigValue<Type>* mkConfigValue(Type& Value, Types... values)
+{
+ return new ConfigValue<Type>(Value, values...);
+}
+
+template<typename Type>
+Type ConfigValueBase::getValue(const QString& value)
+{
+ Type ret;
+ ConfigValue<Type> val(ret);
+ val.setValue(value);
+ return ret;
+}
+
+template<typename Type>
+QString ConfigValueBase::getStringValue(Type value)
+{
+ ConfigValue<Type> val(value);
+ return val.value();
+}
+
+class ConfigBase
+{
+public:
+ explicit ConfigBase(QMap<QString, ConfigValueBase*> configValues) : configValues(configValues) {}
+
+ void setConfigValue(const QString& name, const QString& value);
+ QString getConfigValueLine(const QString& name);
+ void addConfigListEntry(const QString& name, const QString& value);
+ void removeConfigMapListEntry(const QString &name, const QString &value);
+ void setConfigMapValue(const QString& name, const QString& key, const QString& value);
+
+ QString read(const QString& fileName, bool writeDefault = true);
+ bool write(QString fileName = "");
+
+ void setConfigLine(const QString& line);
+
+protected:
+ void addConfigValue(QString name, ConfigValueBase *value);
+ ConfigValueBase& getConfigValue(const QString &name);
+
+ QMap<QString, ConfigValueBase*> configValues;
+
+private:
+ QString curFileName = "";
+};
+
+#endif // CONFIGBASE
+