#ifndef CONFIGBASE #define CONFIGBASE #include #include #include #include #include #include #include #include #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 static Type getValue(const QString& value); template static QString getStringValue(Type val); }; using ConfigValues = QMap; template 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 Float = double; using Port = ushort; using Boolean = bool; #define List(Type) QList #define Map(KeyType, ValueType) QMap #define ConfigVal(Value) {#Value, mkConfigValue(Value)} #define ConfigValName(Name, Value) {#Name, mkConfigValue(Value)} #define ConfigValEx(Value, ...) {#Value, mkConfigValue(Value, __VA_ARGS__)} template<> class ConfigValue : 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 class ConfigValue::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::min() || longLongVal > std::numeric_limits::max()) throw ConfigException((QString::number(longLongVal) + " is out of range (" + QString::number(std::numeric_limits::min()) + " - " + QString::number(std::numeric_limits::max()) + ")").toStdString()); config = static_cast(longLongVal); } virtual QString value() { return QString::number(config); } virtual ConfigValueType::Type type() { return ConfigValueType::Type::Integer; } }; template<> class ConfigValue : public ConfigValueBase { Float& config; public: ConfigValue(Float& config) : config(config) { } virtual void setValue(const QString& value) { bool ok = true; Float temp = value.toDouble(&ok); if(!ok) throw ConfigException("\"" + value.toStdString() + "\" could not be parsed as a Float"); config = temp; } virtual QString value() { return QString::number(config, 'g', 10); } }; template<> class ConfigValue : 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 getValues() = 0; }; template class ConfigValue> : public ConfigValueList { using ListType = QList; 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(entry); if(!deduplicate || !config.contains(val)) { config.append(val); } } virtual void remove(const QString &entry) { config.removeAll(ConfigValueBase::getValue(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(part)); } } QString value() { QStringList parts; foreach(const Type& part, config) { parts.append(ConfigValueBase::getStringValue(part)); } return Util::joinEscape(parts, splitChar); } }; template class ConfigValue> : public ConfigValueMap { using MapType = QMap; 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(key)] = ConfigValueBase::getValue(value); } virtual QMap getValues() { QMap 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(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(parts.first())] = ConfigValueBase::getValue(parts.at(1)); } } }; template class ConfigValue::value>::type> : public ConfigValue::type> { using UndType = typename std::underlying_type::type; public: ConfigValue(Type& config) : ConfigValue::ConfigValue((UndType&)config) {} }; template ConfigValue* mkConfigValue(Type& Value, Types... values) { return new ConfigValue(Value, values...); } template Type ConfigValueBase::getValue(const QString& value) { Type ret; ConfigValue val(ret); val.setValue(value); return ret; } template QString ConfigValueBase::getStringValue(Type value) { ConfigValue val(value); return val.value(); } class ConfigBase { public: explicit ConfigBase(QMap 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 configValues; private: QString curFileName = ""; }; #endif // CONFIGBASE