From: Krzysztof Dynowski Date: Sat, 6 Dec 2014 11:53:07 +0000 (+0100) Subject: config (dynamic): implemented kvjson visitor and loading methods X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F09%2F31709%2F12;p=archive%2Fplatform%2Fcore%2Fsystem%2FlibConfig.git config (dynamic): implemented kvjson visitor and loading methods [Bug/Feature] load config from kvstore with defaults from json [Cause] N/A [Solution] N/A [Verification] Build, install Change-Id: I58c70c0089102b8c4ec1c659937e43d698cd6197 --- diff --git a/src/config/from-kvjson-visitor.hpp b/src/config/from-kvjson-visitor.hpp new file mode 100644 index 0000000..174bbad --- /dev/null +++ b/src/config/from-kvjson-visitor.hpp @@ -0,0 +1,215 @@ +#ifndef CONFIG_FROM_KVJSON_VISITOR_HPP +#define CONFIG_FROM_KVJSON_VISITOR_HPP + +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Krzysztof Dynowski (k.dynowski@samsumg.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief KVStore visitor with defaults values from json + */ + +#include "config/manager.hpp" + + +namespace config { + +class FromKVJsonVisitor { +public: + FromKVJsonVisitor(const std::string& db, const std::string& json) : + mStorePtr(new KVStore(db)) + { + mObject = json_tokener_parse(json.c_str()); + if (mObject == nullptr) { + throw ConfigException("Json parsing error"); + } + } + + ~FromKVJsonVisitor() { + json_object_put(mObject); + } + + FromKVJsonVisitor(const FromKVJsonVisitor& v) : + mObject(json_object_get(v.mObject)), + mStorePtr(v.mStorePtr), + mKeyPrefix(v.mKeyPrefix) + { + } + FromKVJsonVisitor& operator=(const FromKVJsonVisitor&) = delete; + + template + void visit(const std::string& name, T& value) { + getValue(name, value); + } + +private: + std::shared_ptr mStorePtr; + std::string mKeyPrefix; + json_object* mObject; + + // create visitor for field object (visitable object) + FromKVJsonVisitor(const FromKVJsonVisitor& v, const std::string& name) : + mStorePtr(v.mStorePtr) + { + json_object* object; + if (!json_object_object_get_ex(v.mObject, name.c_str(), &object)) { + throw ConfigException("Missing json field " + key(mKeyPrefix, name)); + } + mObject = json_object_get(object); + mKeyPrefix = key(v.mKeyPrefix, name); + } + + // create visitor for vector i-th element (visitable object) + FromKVJsonVisitor(const FromKVJsonVisitor& v, int i) : + mStorePtr(v.mStorePtr) + { + json_object* object = json_object_array_get_idx(v.mObject, i); + checkType(object, json_type_object); + mObject = json_object_get(object); + mKeyPrefix = key(v.mKeyPrefix, std::to_string(i)); + } + + template::value, int>::type = 0> + void getValue(const std::string& name, T& t) + { + json_object* object; + if (!json_object_object_get_ex(mObject, name.c_str(), &object)) { + throw ConfigException("Missing json field " + key(mKeyPrefix, name)); + } + std::string k = key(mKeyPrefix, name); + if (mStorePtr->exists(k)) { + t = mStorePtr->get(k); + } + else { + fromJsonObject(object, t); + } + } + + template::value, int>::type = 0> + void getValue(const std::string& name, T& t) + { + FromKVJsonVisitor visitor(*this, name); + t.accept(visitor); + } + + int getArraySize(std::string& name, json_object* object) + { + int length = -1, jlength = json_object_array_length(object); + if (mStorePtr->exists(name)) { + length = mStorePtr->get(name); + } + return length != jlength ? jlength : length; + } + + template + void getValue(const std::string& name, std::vector& value) + { + json_object* object; + if (!json_object_object_get_ex(mObject, name.c_str(), &object)) { + throw ConfigException("Missing json field " + key(mKeyPrefix, name)); + } + checkType(object, json_type_array); + + std::string k = key(mKeyPrefix, name); + int length = getArraySize(k, object); + value.resize(static_cast(length)); + FromKVJsonVisitor visitor(*this, name); + for (int i = 0; i < length; ++i) { + visitor.getValue(i, value[i]); + } + } + + template::value, int>::type = 0> + void getValue(int i, T& t) + { + json_object* object = json_object_array_get_idx(mObject, i); + std::string k = key(mKeyPrefix, std::to_string(i)); + if (mStorePtr->exists(k)) { + t = mStorePtr->get(k); + } + else { + fromJsonObject(object, t); + } + } + + template::value, int>::type = 0> + void getValue(int i, T& t) + { + FromKVJsonVisitor visitor(*this, i); + t.accept(visitor); + } + + template + void getValue(int i, std::vector& value) + { + std::string k = key(mKeyPrefix, std::to_string(i)); + int length = getArraySize(k, mObject); + value.resize(static_cast(length)); + FromKVJsonVisitor visitor(*this, i); + for (int i = 0; i < length; ++i) { + visitor.getValue(i, value[i]); + } + } + + static void checkType(json_object* object, json_type type) + { + if (type != json_object_get_type(object)) { + throw ConfigException("Invalid field type " + std::to_string(type)); + } + } + + static void fromJsonObject(json_object* object, int& value) + { + checkType(object, json_type_int); + std::int64_t value64 = json_object_get_int64(object); + if (value64 > INT32_MAX || value64 < INT32_MIN) { + throw ConfigException("Value out of range"); + } + value = static_cast(value64); + } + + static void fromJsonObject(json_object* object, std::int64_t& value) + { + checkType(object, json_type_int); + value = json_object_get_int64(object); + } + + static void fromJsonObject(json_object* object, bool& value) + { + checkType(object, json_type_boolean); + value = json_object_get_boolean(object); + } + + static void fromJsonObject(json_object* object, double& value) + { + checkType(object, json_type_double); + value = json_object_get_double(object); + } + + static void fromJsonObject(json_object* object, std::string& value) + { + checkType(object, json_type_string); + value = json_object_get_string(object); + } +}; + +} // namespace config + +#endif // CONFIG_FROM_KVJSON_VISITOR_HPP diff --git a/src/config/fs-utils.hpp b/src/config/fs-utils.hpp index 924a642..82b85f8 100644 --- a/src/config/fs-utils.hpp +++ b/src/config/fs-utils.hpp @@ -33,6 +33,10 @@ namespace fsutils { bool readFileContent(const std::string& path, std::string& result); bool saveFileContent(const std::string& path, const std::string& content); +inline std::string readFileContent(const std::string& path) { + std::string content; + return readFileContent(path, content) ? content : std::string(); +} } // namespace fsutils } // namespace config diff --git a/src/config/kvstore.cpp b/src/config/kvstore.cpp index 621f7f6..d6223fc 100644 --- a/src/config/kvstore.cpp +++ b/src/config/kvstore.cpp @@ -165,7 +165,9 @@ void KVStore::prepareStatements() mGetValueStmt.reset( new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1")); mGetKeyExistsStmt.reset( - new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1")); + // following line left in comment to have example of any subkey matching + //new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1")); + new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 LIMIT 1")); mGetIsEmptyStmt.reset( new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1")); mSetValueStmt.reset( @@ -280,7 +282,7 @@ std::string KVStore::getInternal(const std::string& key, std::string*) int ret = ::sqlite3_step(mGetValueStmt->get()); if (ret == SQLITE_DONE) { - throw ConfigException("No value corresponding to the key: " + key); + throw ConfigException("No value corresponding to the key: " + key + "@" + mPath); } if (ret != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); diff --git a/src/config/manager.hpp b/src/config/manager.hpp index a2623ea..eb7c740 100644 --- a/src/config/manager.hpp +++ b/src/config/manager.hpp @@ -31,6 +31,7 @@ #include "config/from-json-visitor.hpp" #include "config/from-kvstore-visitor.hpp" #include "config/from-fdstore-visitor.hpp" +#include "config/from-kvjson-visitor.hpp" #include "config/is-visitable.hpp" #include "config/fs-utils.hpp" @@ -136,6 +137,44 @@ void saveToKVStore(const std::string& filename, const Config& config, const std: } /** + * Load the config from KVStore with defaults given in json + * + * @param kvfile path to the KVStore db + * @param jsonfile path to json file with defaults + * @param config visitable structure to save + */ +template +void loadFromKVStoreWithJson(const std::string& kvfile, const std::string& json, Config& config) +{ + static_assert(isVisitable::value, "Use CONFIG_REGISTER macro"); + + FromKVJsonVisitor visitor(kvfile, json); + config.accept(visitor); +} +/** + * Load the config from KVStore with defaults given in json file + * + * @param kvfile path to the KVStore db + * @param jsonfile path to json file with defaults + * @param config visitable structure to save + */ +template +void loadFromKVStoreWithJsonFile(const std::string& kvfile, const std::string& jsonfile, Config& config) +{ + static_assert(isVisitable::value, "Use CONFIG_REGISTER macro"); + + std::string content; + if (!fsutils::readFileContent(jsonfile, content)) { + throw ConfigException("Could not load " + jsonfile); + } + try { + loadFromKVStoreWithJson(kvfile, content, config); + } catch (ConfigException& e) { + throw ConfigException("Error in " + jsonfile + ": " + e.what()); + } +} + +/** * Load binary data from a file/socket/pipe represented by the fd * * @param fd file descriptor