config (dynamic): implemented kvjson visitor and loading methods 09/31709/12
authorKrzysztof Dynowski <k.dynowski@samsung.com>
Sat, 6 Dec 2014 11:53:07 +0000 (12:53 +0100)
committerJan Olszak <j.olszak@samsung.com>
Thu, 18 Dec 2014 14:29:11 +0000 (06:29 -0800)
[Bug/Feature]   load config from kvstore with defaults from json
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install

Change-Id: I58c70c0089102b8c4ec1c659937e43d698cd6197

src/config/from-kvjson-visitor.hpp [new file with mode: 0644]
src/config/fs-utils.hpp
src/config/kvstore.cpp
src/config/manager.hpp

diff --git a/src/config/from-kvjson-visitor.hpp b/src/config/from-kvjson-visitor.hpp
new file mode 100644 (file)
index 0000000..174bbad
--- /dev/null
@@ -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<typename T>
+    void visit(const std::string& name, T& value) {
+        getValue(name, value);
+    }
+
+private:
+    std::shared_ptr<KVStore> 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<typename T, typename std::enable_if<!isVisitable<T>::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<T>(k);
+        }
+        else {
+            fromJsonObject(object, t);
+        }
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::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<int>(name);
+        }
+        return length != jlength ? jlength : length;
+    }
+
+    template<typename T>
+    void getValue(const std::string& name, std::vector<T>& 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<size_t>(length));
+        FromKVJsonVisitor visitor(*this, name);
+        for (int i = 0; i < length; ++i) {
+            visitor.getValue(i, value[i]);
+        }
+    }
+
+    template<typename T, typename std::enable_if<!isVisitable<T>::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<T>(k);
+        }
+        else {
+            fromJsonObject(object, t);
+        }
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void getValue(int i, T& t)
+    {
+        FromKVJsonVisitor visitor(*this, i);
+        t.accept(visitor);
+    }
+
+    template<typename T>
+    void getValue(int i, std::vector<T>& value)
+    {
+        std::string k = key(mKeyPrefix, std::to_string(i));
+        int length = getArraySize(k, mObject);
+        value.resize(static_cast<size_t>(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<int>(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
index 924a642..82b85f8 100644 (file)
@@ -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
 
index 621f7f6..d6223fc 100644 (file)
@@ -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());
index a2623ea..eb7c740 100644 (file)
@@ -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 <class Config>
+void loadFromKVStoreWithJson(const std::string& kvfile, const std::string& json, Config& config)
+{
+    static_assert(isVisitable<Config>::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 <class Config>
+void loadFromKVStoreWithJsonFile(const std::string& kvfile, const std::string& jsonfile, Config& config)
+{
+    static_assert(isVisitable<Config>::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