config: kvstore visitors refactoring 58/49458/21
authorPawel Kubik <p.kubik@samsung.com>
Fri, 9 Oct 2015 11:18:13 +0000 (13:18 +0200)
committerPawel Kubik <p.kubik@samsung.com>
Fri, 30 Oct 2015 13:38:23 +0000 (14:38 +0100)
[Feature]       kvstore and kvjson visitors refactoring
[Cause]         N/A
[Solution]      1. All KVStore methods accepting non-string arguments has
                been moved to the FromKVStoreVisitors.
                2. FromKVStoreIgnoringVisitor has been added for loading
                incomplete config from KVStore.
                3. FromKVJsonVisitor has been removed. As replacement
                FromKVStoreIgnoringVisitor is now called right after
                FromJsonVisitor.
[Verification]  Build, install, run tests

Change-Id: I9d77388830c6e2914b1e1d91dcad211c6ae7059e

14 files changed:
libs/config/exception.hpp
libs/config/from-kvjson-visitor.hpp [deleted file]
libs/config/from-kvstore-ignoring-visitor.hpp [new file with mode: 0644]
libs/config/from-kvstore-visitor-base.hpp [new file with mode: 0644]
libs/config/from-kvstore-visitor.hpp
libs/config/kvstore-visitor-utils.hpp [new file with mode: 0644]
libs/config/kvstore.cpp
libs/config/kvstore.hpp
libs/config/manager.hpp
libs/config/to-kvstore-visitor.hpp
tests/unit_tests/config/testconfig-example.hpp
tests/unit_tests/config/ut-configuration.cpp
tests/unit_tests/config/ut-dynvisit.cpp
tests/unit_tests/config/ut-kvstore.cpp

index 977ec32..390dbec 100644 (file)
@@ -39,6 +39,22 @@ struct ConfigException: public std::runtime_error {
     ConfigException(const std::string& error) : std::runtime_error(error) {}
 };
 
+/**
+ * No such key in the config error.
+ */
+struct NoKeyException: public ConfigException {
+
+    NoKeyException(const std::string& error) : ConfigException(error) {}
+};
+
+/**
+ * Container size does not match the config.
+ */
+struct ContainerSizeException: public ConfigException {
+
+    ContainerSizeException(const std::string& error): ConfigException(error) {}
+};
+
 } // namespace config
 
 #endif // COMMON_CONFIG_EXCEPTION_HPP
diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp
deleted file mode 100644 (file)
index e66b90d..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- *  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
- */
-
-#ifndef COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP
-#define COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP
-
-#include "config/from-kvstore-visitor.hpp"
-#include "config/is-union.hpp"
-#include "config/visit-fields.hpp"
-
-#include <utility>
-#include <json.h>
-
-namespace config {
-
-class FromKVJsonVisitor {
-public:
-    FromKVJsonVisitor(KVStore& store, const std::string& json, const std::string& prefix)
-        : mStore(store)
-        , mKeyPrefix(prefix)
-        , mIsUnion(false)
-    {
-        mObject = json_tokener_parse(json.c_str());
-        if (mObject == nullptr) {
-            throw ConfigException("Json parsing error");
-        }
-    }
-
-
-    ~FromKVJsonVisitor() {
-        json_object_put(mObject);
-    }
-
-    FromKVJsonVisitor(const FromKVJsonVisitor& v) :
-        mStore(v.mStore),
-        mKeyPrefix(v.mKeyPrefix),
-        mObject(v.mObject ? json_object_get(v.mObject) : nullptr),
-        mIsUnion(v.mIsUnion)
-    {
-    }
-    FromKVJsonVisitor& operator=(const FromKVJsonVisitor&) = delete;
-
-    template<typename T>
-    void visit(const std::string& name, T& value) {
-        getValue(name, value);
-    }
-
-    template<typename T>
-    void visit(std::size_t& idx, T& value) {
-        getValue(idx, value);
-        idx += 1;
-    }
-
-private:
-    KVStore& mStore;
-    std::string mKeyPrefix;
-    json_object* mObject;
-    bool mIsUnion;
-
-    // create visitor for field object (visitable object)
-    FromKVJsonVisitor(const FromKVJsonVisitor& v, const std::string& name, const bool isUnion) :
-        mStore(v.mStore),
-        mKeyPrefix(key(v.mKeyPrefix, name)),
-        mIsUnion(isUnion || v.mIsUnion)
-    {
-        json_object* object = nullptr;
-        if (v.mObject && !json_object_object_get_ex(v.mObject, name.c_str(), &object)) {
-            if (!mIsUnion)
-                throw ConfigException("Missing json field " + mKeyPrefix);
-        }
-        mObject = object ? json_object_get(object) : nullptr;
-    }
-
-    // create visitor for vector i-th element (visitable object)
-    FromKVJsonVisitor(const FromKVJsonVisitor& v, int i, const bool isUnion) :
-        mStore(v.mStore),
-        mKeyPrefix(key(v.mKeyPrefix, std::to_string(i))),
-        mIsUnion(isUnion || v.mIsUnion)
-    {
-        json_object* object = nullptr;
-        if (v.mObject) {
-            object = json_object_array_get_idx(v.mObject, i);
-            checkType(object, json_type_object);
-        }
-        mObject = object ? json_object_get(object) : nullptr;
-    }
-
-    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
-    void getValue(const std::string& name, T& t)
-    {
-        getValue(name,
-                 *reinterpret_cast<typename std::underlying_type<T>::type*>(&t));
-    }
-
-    template<typename T,
-             typename std::enable_if<!isVisitable<T>::value &&
-                                     !std::is_enum<T>::value, int>::type = 0>
-    void getValue(const std::string& name, T& t)
-    {
-        std::string k = key(mKeyPrefix, name);
-        if (mStore.exists(k)) {
-            t = mStore.get<T>(k);
-        } else {
-            json_object* object = nullptr;
-            if (mObject) {
-                json_object_object_get_ex(mObject, name.c_str(), &object);
-            }
-            if (!object) {
-                throw ConfigException("Missing json field " + k);
-            }
-            fromJsonObject(object, t);
-        }
-    }
-
-    template<typename T, typename std::enable_if<isUnion<T>::value, int>::type = 0>
-    void getValue(const std::string& name, T& t)
-    {
-        FromKVJsonVisitor visitor(*this, name, true);
-        t.accept(visitor);
-    }
-
-    template<typename T, typename std::enable_if<isVisitable<T>::value && !isUnion<T>::value, int>::type = 0>
-    void getValue(const std::string& name, T& t)
-    {
-        FromKVJsonVisitor visitor(*this, name, false);
-        t.accept(visitor);
-    }
-
-    int getArraySize(std::string& name, json_object* object)
-    {
-        if (mStore.exists(name)) {
-            return mStore.get<int>(name);
-        }
-        if (object) {
-            return json_object_array_length(object);
-        }
-        return -1;
-    }
-
-    template<typename T>
-    void getValue(const std::string& name, std::vector<T>& value)
-    {
-        json_object* object = nullptr;
-        if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) {
-            checkType(object, json_type_array);
-        }
-
-        std::string k = key(mKeyPrefix, name);
-        int length = getArraySize(k, object);
-        if (length < 0) {
-            throw ConfigException("Missing array length " + k);
-        }
-        value.resize(static_cast<size_t>(length));
-        FromKVJsonVisitor visitor(*this, name, false);
-        if (mStore.exists(k)) {
-            json_object_put(visitor.mObject);
-            visitor.mObject = nullptr;
-        }
-        for (int i = 0; i < length; ++i) {
-            visitor.getValue(i, value[i]);
-        }
-    }
-
-    template<typename T, std::size_t N>
-    void getValue(const std::string& name, std::array<T, N>& values)
-    {
-        json_object* object = nullptr;
-        if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) {
-            checkType(object, json_type_array);
-        }
-
-        std::string k = key(mKeyPrefix, name);
-        FromKVJsonVisitor visitor(*this, name, false);
-        if (mStore.exists(k)) {
-            json_object_put(visitor.mObject);
-            visitor.mObject = nullptr;
-        }
-        for (std::size_t i = 0; i < N; ++i) {
-            visitor.getValue(i, values[i]);
-        }
-    }
-
-    template<typename ... T>
-    void getValue(const std::string& name, std::pair<T...>& values)
-    {
-        json_object* object = nullptr;
-        if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) {
-            checkType(object, json_type_array);
-        }
-
-        std::string k = key(mKeyPrefix, name);
-        FromKVJsonVisitor visitor(*this, name, false);
-        if (mStore.exists(k)) {
-            json_object_put(visitor.mObject);
-            visitor.mObject = nullptr;
-        }
-
-        std::size_t idx = 0;
-        visitFields(values, &visitor, idx);
-    }
-
-    template<typename T, typename std::enable_if<!isVisitable<T>::value, int>::type = 0>
-    void getValue(int i, T& t)
-    {
-        std::string k = key(mKeyPrefix, std::to_string(i));
-        if (mStore.exists(k)) {
-            t = mStore.get<T>(k);
-        }
-        else {
-            json_object* object = mObject ? json_object_array_get_idx(mObject, i) : nullptr;
-            if (!object) {
-                throw ConfigException("Missing json array elem " + k);
-            }
-            fromJsonObject(object, t);
-        }
-    }
-
-    template<typename T, typename std::enable_if<isUnion<T>::value, int>::type = 0>
-    void getValue(int i, T& t)
-    {
-        FromKVJsonVisitor visitor(*this, i, true);
-        t.accept(visitor);
-    }
-
-    template<typename T, typename std::enable_if<isVisitable<T>::value && !isUnion<T>::value, int>::type = 0>
-    void getValue(int i, T& t)
-    {
-        FromKVJsonVisitor visitor(*this, i, false);
-        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, false);
-        if (mStore.exists(k)) {
-            json_object_put(visitor.mObject);
-            visitor.mObject = nullptr;
-        }
-        for (int idx = 0; idx < length; ++idx) {
-            visitor.getValue(idx, value[idx]);
-        }
-    }
-
-    template<typename T, std::size_t N>
-    void getValue(int i, std::array<T, N>& values)
-    {
-        std::string k = key(mKeyPrefix, std::to_string(i));
-
-        FromKVJsonVisitor visitor(*this, i, false);
-        if (mStore.exists(k)) {
-            json_object_put(visitor.mObject);
-            visitor.mObject = nullptr;
-        }
-        for (std::size_t idx = 0; idx < N; ++idx) {
-            visitor.getValue(idx, values[idx]);
-        }
-    }
-
-    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, std::uint32_t& value)
-    {
-        checkType(object, json_type_int);
-        std::int64_t value64 = json_object_get_int64(object);
-        if (value64 > UINT32_MAX || value64 < 0) {
-            throw ConfigException("Value out of range");
-        }
-        value = static_cast<std::uint32_t>(value64);
-    }
-
-    static void fromJsonObject(json_object* object, std::uint64_t& value)
-    {
-        checkType(object, json_type_int);
-        std::int64_t value64 = json_object_get_int64(object);
-        if (value64 < 0) {
-            throw ConfigException("Value out of range");
-        }
-        value = static_cast<std::uint64_t>(value64);
-    }
-
-    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);
-    }
-
-    static void fromJsonObject(json_object* object, char* &value)
-    {
-        checkType(object, json_type_string);
-
-        int len = json_object_get_string_len(object);
-        value = new char[len + 1];
-        std::strncpy(value, json_object_get_string(object), len);
-        value[len] = '\0';
-    }
-};
-
-} // namespace config
-
-#endif // COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP
diff --git a/libs/config/from-kvstore-ignoring-visitor.hpp b/libs/config/from-kvstore-ignoring-visitor.hpp
new file mode 100644 (file)
index 0000000..094db50
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.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  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Visitor for loading from KVStore that doesn't fail on missing values
+ */
+
+#ifndef COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP
+#define COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP
+
+#include "config/from-kvstore-visitor.hpp"
+
+
+namespace config {
+
+/**
+ * A variant of KVStoreVisitor that ignore non-existing fields.
+ *
+ * Allows to partially update visited structures with fields that exist in the KVStore.
+ */
+class FromKVStoreIgnoringVisitor : public FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor> {
+public:
+    FromKVStoreIgnoringVisitor(KVStore& store, const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(store, prefix)
+    {
+    }
+
+    FromKVStoreIgnoringVisitor(const FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>& visitor,
+                               const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(visitor, prefix)
+    {
+    }
+
+    FromKVStoreVisitor& operator=(const FromKVStoreVisitor&) = delete;
+
+protected:
+    template<typename T>
+    void visitImpl(const std::string& name, T& value)
+    {
+        try {
+            getInternal(name, value);
+        } catch (const NoKeyException& e) {
+        } catch (const ContainerSizeException& e) {
+        }
+    }
+
+private:
+    FromKVStoreIgnoringVisitor(const FromKVStoreIgnoringVisitor& visitor,
+                               const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(visitor.mStore, prefix)
+    {
+    }
+
+    template<typename T,
+             typename std::enable_if<isUnion<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        std::string type;
+        getInternal(key(name, "type"), type);
+        if (type.empty()) {
+            return;
+        }
+
+        FromKVStoreIgnoringVisitor visitor(*this, name);
+        value.accept(visitor);
+    }
+
+    template<typename T,
+             typename std::enable_if<!isUnion<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>::visitImpl(name, value);
+    }
+
+    friend class FromKVStoreVisitorBase;
+};
+
+} // namespace config
+
+#endif // COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP
diff --git a/libs/config/from-kvstore-visitor-base.hpp b/libs/config/from-kvstore-visitor-base.hpp
new file mode 100644 (file)
index 0000000..a6e209c
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.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  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Base of visitors for loading from KVStore
+ */
+
+#ifndef COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP
+#define COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP
+
+#include "config/is-visitable.hpp"
+#include "config/kvstore.hpp"
+#include "config/kvstore-visitor-utils.hpp"
+#include "config/visit-fields.hpp"
+
+
+namespace config {
+
+/**
+ * Base class for KVStore visitors.
+ *
+ * A curiously recurring template pattern example. This
+ * base-class provides a set of recursively called function templates. A child class can
+ * modify the base class behaviour in certain cases by defining:
+ *  - a set of functions that match those cases
+ *  - a template function that match any other case and calls the base class function
+ */
+template<typename RecursiveVisitor>
+class FromKVStoreVisitorBase {
+public:
+    FromKVStoreVisitorBase& operator=(const FromKVStoreVisitorBase&) = delete;
+
+    template<typename T>
+    void visit(const std::string& name, T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(key(mKeyPrefix, name), value);
+    }
+
+protected:
+    KVStore& mStore;
+    std::string mKeyPrefix;
+
+    template<typename T>
+    void visitImpl(const std::string& name, T& value)
+    {
+        getInternal(name, value);
+    }
+
+    FromKVStoreVisitorBase(KVStore& store, const std::string& prefix)
+        : mStore(store),
+          mKeyPrefix(prefix)
+    {
+    }
+
+    FromKVStoreVisitorBase(const FromKVStoreVisitorBase& visitor,
+                           const std::string& prefix)
+        : mStore(visitor.mStore),
+          mKeyPrefix(prefix)
+    {
+    }
+
+private:
+    template<typename T,
+             typename std::enable_if<!isVisitable<T>::value
+                                  && !std::is_enum<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        value = fromString<T>(mStore.get(name));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        RecursiveVisitor visitor(*this, name);
+        value.accept(visitor);
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        auto rawValue = static_cast<typename std::underlying_type<T>::type>(value);
+        static_cast<RecursiveVisitor*>(this)->visitImpl(name, rawValue);
+        value = static_cast<T>(rawValue);
+    }
+
+    template<typename T>
+    void getInternal(const std::string& name, std::vector<T>& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        if (storedSize == 0) {
+            return;
+        }
+
+        values.resize(storedSize);
+        for (size_t i = 0; i < storedSize; ++i) {
+            const std::string k = key(name, std::to_string(i));
+            if (!mStore.prefixExists(k)) {
+                throw ConfigException("Corrupted list serialization.");
+            }
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k, values[i]);
+        }
+    }
+
+    template<typename T, size_t N>
+    void getInternal(const std::string& name, std::array<T, N>& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        if (storedSize != values.size()) {
+            throw ContainerSizeException("Size of stored array doesn't match provided one.");
+        }
+
+        for (size_t i = 0; i < storedSize; ++i) {
+            const std::string k = key(name, std::to_string(i));
+            if (!mStore.prefixExists(k)) {
+                throw ConfigException("Corrupted list serialization.");
+            }
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k, values[i]);
+        }
+    }
+
+    template<typename ... T>
+    void getInternal(const std::string& key, std::pair<T...>& values)
+    {
+        std::vector<std::string> strValues;
+
+        getInternal(key, strValues);
+        if (strValues.empty()) {
+            return;
+        }
+
+        if (strValues.size() != sizeof...(T)) {
+            throw ContainerSizeException("Size of stored tuple doesn't match provided one.");
+        }
+
+        GetTupleVisitor visitor;
+        visitFields(values, &visitor, strValues.begin());
+    }
+};
+
+} // namespace config
+
+#endif // COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP
index 56bcd14..8cb561f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
  *
- *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 /**
  * @file
- * @author  Jan Olszak (j.olszak@samsung.com)
- * @brief   Visitor for loading from KVStore
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Default visitor for loading from KVStore
  */
 
 #ifndef COMMON_CONFIG_FROM_KVSTORE_VISITOR_HPP
 #define COMMON_CONFIG_FROM_KVSTORE_VISITOR_HPP
 
-#include "config/is-visitable.hpp"
-#include "config/kvstore.hpp"
+#include "config/from-kvstore-visitor-base.hpp"
+
 
 namespace config {
 
-class FromKVStoreVisitor {
+/**
+ * Default KVStore visitor.
+ */
+class FromKVStoreVisitor : public FromKVStoreVisitorBase<FromKVStoreVisitor> {
 public:
     FromKVStoreVisitor(KVStore& store, const std::string& prefix)
-        : mStore(store),
-          mKeyPrefix(prefix)
-    {
-    }
-
-    FromKVStoreVisitor& operator=(const FromKVStoreVisitor&) = delete;
-
-    template<typename T>
-    void visit(const std::string& name, T& value)
+        : FromKVStoreVisitorBase<FromKVStoreVisitor>(store, prefix)
     {
-        getInternal(key(mKeyPrefix, name), value);
     }
 
-private:
-    KVStore& mStore;
-    std::string mKeyPrefix;
-
-    FromKVStoreVisitor(const FromKVStoreVisitor& visitor, const std::string& prefix)
-        : mStore(visitor.mStore),
-          mKeyPrefix(prefix)
+    FromKVStoreVisitor(const FromKVStoreVisitorBase<FromKVStoreVisitor>& visitor,
+                       const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreVisitor>(visitor, prefix)
     {
     }
-
-    template<typename T, typename std::enable_if<!isVisitable<T>::value, int>::type = 0>
-    void getInternal(const std::string& name, T& value)
-    {
-        value = mStore.get<T>(name);
-    }
-
-    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
-    void getInternal(const std::string& name, T& value)
-    {
-        FromKVStoreVisitor visitor(*this, name);
-        value.accept(visitor);
-    }
-
-    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
-    void getInternal(const std::string& name, std::vector<T>& values)
-    {
-        values.clear();
-
-        size_t vectorSize = mStore.get<size_t>(name);
-        if (vectorSize == 0) {
-            return;
-        }
-
-        values.resize(vectorSize);
-        for (size_t i = 0; i < vectorSize; ++i) {
-            const std::string k = key(name, std::to_string(i));
-            getInternal(k, values[i]);
-        }
-    }
 };
 
 } // namespace config
diff --git a/libs/config/kvstore-visitor-utils.hpp b/libs/config/kvstore-visitor-utils.hpp
new file mode 100644 (file)
index 0000000..5368af4
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.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  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   KVStore visitors utilities
+ */
+
+#ifndef COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP
+#define COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP
+
+namespace config {
+
+template<typename T>
+T fromString(const std::string& strValue)
+{
+    std::istringstream iss(strValue);
+    T value;
+    iss >> value;
+    return value;
+}
+
+template<typename T>
+std::string toString(const T& value)
+{
+    std::ostringstream oss;
+    oss << value;
+    return oss.str();
+}
+
+/**
+ * Concatenates all parameters into one std::string.
+ * Uses '.' to connect the terms.
+ * @param args components of the string
+ * @tparam delim optional delimiter
+ * @tparam Args any type implementing str
+ * @return string created from he args
+ */
+template<char delim = '.', typename Arg1, typename ... Args>
+std::string key(const Arg1& a1, const Args& ... args)
+{
+    std::string ret = toString(a1);
+    std::initializer_list<std::string> strings {toString(args)...};
+    for (const std::string& s : strings) {
+        ret += delim + s;
+    }
+
+    return ret;
+}
+
+/**
+ * Function added for key function completeness.
+ *
+ * @tparam delim = '.' parameter not used, added for consistency
+ * @return empty string
+ */
+template<char delim = '.'>
+std::string key()
+{
+    return std::string();
+}
+
+struct GetTupleVisitor
+{
+    template<typename T>
+    static void visit(std::vector<std::string>::iterator& it, T& value)
+    {
+        if (!it->empty()) {
+            value = fromString<T>(*it);
+        }
+        ++it;
+    }
+};
+
+struct SetTupleVisitor
+{
+    template<typename T>
+    static void visit(std::vector<std::string>::iterator& it, const T& value)
+    {
+        *it = toString<T>(value);
+        ++it;
+    }
+};
+
+} // namespace config
+
+#endif // COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP
index 25febf6..2726a8d 100644 (file)
@@ -150,6 +150,43 @@ KVStore::~KVStore()
     assert(mTransactionDepth == 0);
 }
 
+void KVStore::set(const std::string& key, const std::string& value)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mSetValueStmt);
+
+    ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
+    ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
+
+
+    if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
+        throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
+    }
+    transaction.commit();
+}
+
+std::string KVStore::get(const std::string& key)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetValueStmt);
+
+    ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+
+    int ret = ::sqlite3_step(mGetValueStmt->get());
+    if (ret == SQLITE_DONE) {
+        throw NoKeyException("No value corresponding to the key: " + key + "@" + mPath);
+    }
+    if (ret != SQLITE_ROW) {
+        throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
+    }
+
+    std::string value = reinterpret_cast<const char*>(
+            sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
+
+    transaction.commit();
+    return value;
+}
+
 void KVStore::setupDb()
 {
     // called only from ctor, transaction is not needed
@@ -161,9 +198,9 @@ void KVStore::prepareStatements()
     mGetValueStmt.reset(
         new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1"));
     mGetKeyExistsStmt.reset(
-        // 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"));
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 LIMIT 1"));
+    mGetKeyPrefixExistsStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1"));
     mGetIsEmptyStmt.reset(
         new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1"));
     mSetValueStmt.reset(
@@ -219,133 +256,33 @@ bool KVStore::exists(const std::string& key)
     return ret == SQLITE_ROW;
 }
 
-void KVStore::remove(const std::string& key)
-{
-    Transaction transaction(*this);
-    ScopedReset scopedReset(mRemoveValuesStmt);
-
-    ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
-
-    if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
-        throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
-    }
-    transaction.commit();
-}
-
-void KVStore::setInternal(const std::string& key, const char* value)
-{
-    Transaction transaction(*this);
-    ScopedReset scopedReset(mSetValueStmt);
-
-    ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
-    ::sqlite3_bind_text(mSetValueStmt->get(), 2, value, AUTO_DETERM_SIZE, SQLITE_STATIC);
-
-
-    if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
-        throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
-    }
-    transaction.commit();
-}
-
-void KVStore::setInternal(const std::string& key, const std::string& value)
-{
-    setInternal(key, value.c_str());
-}
-
-void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
-{
-    setInternal(key, std::vector<std::string>(values));
-}
-
-void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
+bool KVStore::prefixExists(const std::string& key)
 {
-    if (values.size() > std::numeric_limits<unsigned int>::max()) {
-        throw ConfigException("Too many values to insert");
-    }
-
     Transaction transaction(*this);
+    ScopedReset scopedReset(mGetKeyPrefixExistsStmt);
 
-    remove(key);
-
-    // Save vector's capacity
-    setInternal(key, values.size());
-
-    // Save vector's elements
-    for (unsigned int i = 0; i < values.size(); ++i) {
-        setInternal(config::key(key, std::to_string(i)),
-                    values[i]);
-    }
-    transaction.commit();
-}
+    ::sqlite3_bind_text(mGetKeyPrefixExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
 
-std::string KVStore::getInternal(const std::string& key, std::string*)
-{
-    Transaction transaction(*this);
-    ScopedReset scopedReset(mGetValueStmt);
-
-    ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
-
-    int ret = ::sqlite3_step(mGetValueStmt->get());
-    if (ret == SQLITE_DONE) {
-        throw ConfigException("No value corresponding to the key: " + key + "@" + mPath);
-    }
-    if (ret != SQLITE_ROW) {
+    int ret = ::sqlite3_step(mGetKeyPrefixExistsStmt->get());
+    if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
     }
 
-    std::string value = reinterpret_cast<const char*>(
-            sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
-
     transaction.commit();
-    return value;
+    return ret == SQLITE_ROW;
 }
 
-char* KVStore::getInternal(const std::string& key, char**)
+void KVStore::remove(const std::string& key)
 {
     Transaction transaction(*this);
-    ScopedReset scopedReset(mGetValueStmt);
+    ScopedReset scopedReset(mRemoveValuesStmt);
 
-    ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+    ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
 
-    int ret = ::sqlite3_step(mGetValueStmt->get());
-    if (ret == SQLITE_DONE) {
-        throw ConfigException("No value corresponding to the key: " + key + "@" + mPath);
-    }
-    if (ret != SQLITE_ROW) {
+    if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
     }
-
-    const char* source = reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
-
-    size_t length = std::strlen(source);
-    char* value = new char[length + 1];
-
-    std::strncpy(value, source, length);
-    value[length] = '\0';
-
-    transaction.commit();
-    return value;
-}
-
-std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
-{
-    Transaction transaction(*this);
-
-    unsigned int valuesSize = get<unsigned int>(key);
-    std::vector<std::string> values(valuesSize);
-    if (valuesSize == 0) {
-        transaction.commit();
-        return values;
-    }
-
-    for (unsigned int i = 0; i < values.size(); ++i) {
-        values[i] = getInternal(config::key(key, std::to_string(i)),
-                                static_cast<std::string*>(nullptr));
-
-    }
-
     transaction.commit();
-    return values;
 }
 
 std::vector<std::string> KVStore::getKeys()
index 40aa199..52b5328 100644 (file)
@@ -27,7 +27,6 @@
 
 #include "config/sqlite3/connection.hpp"
 #include "config/sqlite3/statement.hpp"
-#include "config/visit-fields.hpp"
 
 #include <algorithm>
 #include <initializer_list>
@@ -82,13 +81,20 @@ public:
     bool isEmpty();
 
     /**
-     * @param key string regexp of the stored values
+     * @param key string of the stored value
      *
      * @return Does this key exist in the database
      */
     bool exists(const std::string& key);
 
     /**
+     * @param key string of the stored value
+     *
+     * @return Does a key starting with an argument exist in the database
+     */
+    bool prefixExists(const std::string& key);
+
+    /**
      * Removes values corresponding to the passed key.
      * Many values may correspond to one key, so many values may
      * need to be deleted
@@ -103,25 +109,15 @@ public:
      * @param key string key of the value
      * @param value value corresponding to the key
      */
-    template<typename T>
-    void set(const std::string& key, const T& value)
-    {
-        return setInternal(key, value);
-    }
+    void set(const std::string& key, const std::string& value);
 
     /**
      * Gets the value corresponding to the key.
-     * Uses stringstreams to parse.
      *
      * @param key string key of the value
-     * @tparam T = std::string desired type of the return value
      * @return value corresponding to the key
      */
-    template<typename T = std::string>
-    T get(const std::string& key)
-    {
-        return getInternal(key, static_cast<T*>(nullptr));
-    }
+    std::string get(const std::string& key);
 
     /**
      * Returns all stored keys.
@@ -135,39 +131,11 @@ private:
     size_t mTransactionDepth;
     bool mIsTransactionCommited;
 
-    void setInternal(const std::string& key, const std::string& value);
-    void setInternal(const std::string& key, const char* value);
-    void setInternal(const std::string& key, const std::initializer_list<std::string>& values);
-    void setInternal(const std::string& key, const std::vector<std::string>& values);
-    template<typename T, typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
-    void setInternal(const std::string& key, const T& value);
-    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
-    void setInternal(const std::string& key, const T& value);
-    template<typename T>
-    void setInternal(const std::string& key, const std::vector<T>& values);
-    template<typename T, std::size_t N>
-    void setInternal(const std::string& key, const std::array<T, N>& values);
-    template<typename ... T>
-    void setInternal(const std::string& key, const std::pair<T...>& values);
-
-    std::string getInternal(const std::string& key, std::string*);
-    char* getInternal(const std::string& key, char**);
-    std::vector<std::string> getInternal(const std::string& key, std::vector<std::string>*);
-    template<typename T, typename std::enable_if<!std::is_enum<T>::value, int>::type = 0>
-    T getInternal(const std::string& key, T*);
-    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
-    T getInternal(const std::string& key, T*);
-    template<typename T>
-    std::vector<T> getInternal(const std::string& key, std::vector<T>*);
-    template<typename T, std::size_t N>
-    std::array<T, N> getInternal(const std::string& key, std::array<T, N>*);
-    template<typename ... T>
-    std::pair<T...> getInternal(const std::string& key, std::pair<T...>*);
-
     std::string mPath;
     sqlite3::Connection mConn;
     std::unique_ptr<sqlite3::Statement> mGetValueStmt;
     std::unique_ptr<sqlite3::Statement> mGetKeyExistsStmt;
+    std::unique_ptr<sqlite3::Statement> mGetKeyPrefixExistsStmt;
     std::unique_ptr<sqlite3::Statement> mGetIsEmptyStmt;
     std::unique_ptr<sqlite3::Statement> mGetValueListStmt;
     std::unique_ptr<sqlite3::Statement> mSetValueStmt;
@@ -179,180 +147,6 @@ private:
     void createFunctions();
 };
 
-namespace {
-template<typename T>
-std::string toString(const T& value)
-{
-    std::ostringstream oss;
-    oss << value;
-    return oss.str();
-}
-
-template<typename T>
-T fromString(const std::string& strValue)
-{
-    std::istringstream iss(strValue);
-    T value;
-    iss >> value;
-    return value;
-}
-
-} // namespace
-
-template<typename T, typename std::enable_if<!std::is_enum<T>::value, int>::type>
-void KVStore::setInternal(const std::string& key, const T& value)
-{
-    setInternal(key, toString(value));
-}
-
-template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type>
-void KVStore::setInternal(const std::string& key, const T& value)
-{
-    setInternal(key,
-                static_cast<const typename std::underlying_type<T>::type>(value));
-}
-
-template<typename T>
-void KVStore::setInternal(const std::string& key, const std::vector<T>& values)
-{
-    std::vector<std::string> strValues(values.size());
-
-    std::transform(values.begin(),
-                   values.end(),
-                   strValues.begin(),
-                   toString<T>);
-
-    setInternal(key, strValues);
-}
-
-template<typename T, std::size_t N>
-void KVStore::setInternal(const std::string& key, const std::array<T, N>& values)
-{
-    std::vector<std::string> strValues(N);
-
-    std::transform(values.begin(),
-                   values.end(),
-                   strValues.begin(),
-                   toString<T>);
-
-    setInternal(key, strValues);
-}
-
-struct SetTupleVisitor
-{
-    template<typename T>
-    static void visit(std::vector<std::string>::iterator& it, const T& value)
-    {
-        *it = toString<T>(value);
-        ++it;
-    }
-};
-
-template<typename ... T>
-void KVStore::setInternal(const std::string& key, const std::pair<T...>& values)
-{
-    std::vector<std::string> strValues(std::tuple_size<std::pair<T...>>::value);
-
-    SetTupleVisitor visitor;
-    visitFields(values, &visitor, strValues.begin());
-    setInternal(key, strValues);
-}
-
-template<typename T, typename std::enable_if<!std::is_enum<T>::value, int>::type>
-T KVStore::getInternal(const std::string& key, T*)
-{
-    return fromString<T>(getInternal(key, static_cast<std::string*>(nullptr)));
-}
-
-template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type>
-T KVStore::getInternal(const std::string& key, T*)
-{
-    return static_cast<T>(getInternal(key,
-                                      static_cast<typename std::underlying_type<T>::type*>(nullptr)));
-}
-
-template<typename T, std::size_t N>
-std::array<T, N> KVStore::getInternal(const std::string& key, std::array<T, N>*)
-{
-    std::vector<std::string> strValues = getInternal(key, static_cast<std::vector<std::string>*>(nullptr));
-    std::array<T, N> values;
-
-    std::transform(strValues.begin(),
-                   strValues.end(),
-                   values.begin(),
-                   fromString<T>);
-
-    return values;
-}
-
-template<typename T>
-std::vector<T> KVStore::getInternal(const std::string& key, std::vector<T>*)
-{
-    std::vector<std::string> strValues = getInternal(key, static_cast<std::vector<std::string>*>(nullptr));
-    std::vector<T> values(strValues.size());
-
-    std::transform(strValues.begin(),
-                   strValues.end(),
-                   values.begin(),
-                   fromString<T>);
-
-    return values;
-}
-
-struct GetTupleVisitor
-{
-    template<typename T>
-    static void visit(std::vector<std::string>::iterator& it, T& value)
-    {
-        value = fromString<T>(*it);
-        ++it;
-    }
-};
-
-template<typename ... T>
-std::pair<T...> KVStore::getInternal(const std::string& key, std::pair<T...>*)
-{
-    std::vector<std::string> strValues = getInternal(key, static_cast<std::vector<std::string>*>(nullptr));
-    std::pair<T...> values;
-
-    GetTupleVisitor visitor;
-    visitFields(values, &visitor, strValues.begin());
-
-    return values;
-}
-
-/**
- * Concatenates all parameters into one std::string.
- * Uses '.' to connect the terms.
- * @param args components of the string
- * @tparam delim optional delimiter
- * @tparam Args any type implementing str
- * @return string created from he args
- */
-template<char delim = '.', typename Arg1, typename ... Args>
-std::string key(const Arg1& a1, const Args& ... args)
-{
-    std::string ret = toString(a1);
-    std::initializer_list<std::string> strings {toString(args)...};
-    for (const std::string& s : strings) {
-        ret += delim + s;
-    }
-
-    return ret;
-}
-
-/**
- * Function added for key function completeness.
- *
- * @tparam delim = '.' parameter not used, added for consistency
- * @return empty string
- */
-template<char delim = '.'>
-std::string key()
-{
-    return std::string();
-}
-
 } // namespace config
 
 #endif // COMMON_CONFIG_KVSTORE_HPP
index e2877df..d1df907 100644 (file)
@@ -98,8 +98,8 @@
 #include "config/from-gvariant-visitor.hpp"
 #include "config/from-json-visitor.hpp"
 #include "config/from-kvstore-visitor.hpp"
+#include "config/from-kvstore-ignoring-visitor.hpp"
 #include "config/from-fdstore-visitor.hpp"
-#include "config/from-kvjson-visitor.hpp"
 #include "config/fs-utils.hpp"
 #include "config/is-union.hpp"
 
@@ -260,8 +260,10 @@ void loadFromKVStoreWithJson(const std::string& kvfile,
 
     KVStore store(kvfile);
     KVStore::Transaction transaction(store);
-    FromKVJsonVisitor visitor(store, json, kvConfigName);
-    config.accept(visitor);
+    FromJsonVisitor fromJsonVisitor(json);
+    FromKVStoreIgnoringVisitor fromKVStoreVisitor(store, kvConfigName);
+    config.accept(fromJsonVisitor);
+    config.accept(fromKVStoreVisitor);
     transaction.commit();
 }
 
index 14944dc..6992262 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "config/is-visitable.hpp"
 #include "config/kvstore.hpp"
+#include "config/kvstore-visitor-utils.hpp"
 
 namespace config {
 
@@ -57,10 +58,12 @@ private:
     {
     }
 
-    template<typename T, typename std::enable_if<!isVisitable<T>::value, int>::type = 0>
+    template<typename T,
+             typename std::enable_if<!isVisitable<T>::value
+                                  && !std::is_enum<T>::value, int>::type = 0>
     void setInternal(const std::string& name, const T& value)
     {
-        mStore.set(name, value);
+        mStore.set(name, toString(value));
     }
 
     template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
@@ -70,14 +73,59 @@ private:
         value.accept(visitor);
     }
 
-    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
-    void setInternal(const std::string& name, const std::vector<T>& values)
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void setInternal(const std::string& name, const T& value)
     {
+        setInternal(name, static_cast<const typename std::underlying_type<T>::type>(value));
+    }
+
+    template <typename I>
+    void setRangeInternal(const std::string& name,
+                          const I& begin,
+                          const I& end,
+                          const size_t size) {
+        if (size > std::numeric_limits<unsigned int>::max()) {
+            throw ConfigException("Too many values to insert");
+        }
+
+        KVStore::Transaction transaction(mStore);
+
         mStore.remove(name);
-        mStore.set(name, values.size());
-        for (size_t i = 0; i < values.size(); ++i) {
-            setInternal(key(name, std::to_string(i)), values[i]);
+        setInternal(name, size);
+        size_t i = 0;
+        for (auto it = begin; it != end; ++it) {
+            const std::string k = key(name, std::to_string(i));
+            setInternal(k, *it);
+            ++i;
         }
+        transaction.commit();
+    }
+
+    template<typename T>
+    void setInternal(const std::string& name, const std::vector<T>& values)
+    {
+        setRangeInternal(name, values.begin(), values.end(), values.size());
+    }
+
+    template<typename T, std::size_t N>
+    void setInternal(const std::string& name, const std::array<T, N>& values) {
+        setRangeInternal(name, values.begin(), values.end(), N);
+    }
+
+    template<typename T>
+    void setInternal(const std::string& name, const std::initializer_list<T>& values)
+    {
+        setRangeInternal(name, values.begin(), values.end(), values.size());
+    }
+
+    template<typename ... T>
+    void setInternal(const std::string& key, const std::pair<T...>& values)
+    {
+        std::vector<std::string> strValues(std::tuple_size<std::pair<T...>>::value);
+
+        SetTupleVisitor visitor;
+        visitFields(values, &visitor, strValues.begin());
+        setInternal(key, strValues);
     }
 };
 
index 0ddd975..42620db 100644 (file)
@@ -93,7 +93,6 @@ struct TestConfig {
     std::uint32_t uint32Val;
     std::uint64_t uint64Val;
     std::string stringVal;
-    char* cstringVal;
     double doubleVal;
     bool boolVal;
     TestEnum enumVal;
@@ -121,7 +120,6 @@ struct TestConfig {
         uint32Val,
         uint64Val,
         stringVal,
-        cstringVal,
         doubleVal,
         boolVal,
         enumVal,
@@ -167,7 +165,6 @@ const std::string jsonTestString =
     "\"uint32Val\": 123456, "
     "\"uint64Val\": 1234567890123456789, "
     "\"stringVal\": \"blah\", "
-    "\"cstringVal\": \"blah\", "
     "\"doubleVal\": -1.234000, "
     "\"boolVal\": true, "
     "\"enumVal\": 12, "
@@ -194,15 +191,15 @@ const std::string jsonEmptyTestString =
     "\"uint32Val\": 0, "
     "\"uint64Val\": 0, "
     "\"stringVal\": \"\", "
-    "\"cstringVal\": \"\", "
+    "\"doubleVal\": 0.0, "
     "\"boolVal\": false, "
     "\"enumVal\": 0, "
     "\"emptyIntVector\": [ ], "
     "\"intVector\": [ ], "
     "\"stringVector\": [ ], "
     "\"doubleVector\": [ ], "
-    "\"intArray\": [ ], "
-    "\"intIntPair\": [ ], "
+    "\"intArray\": [ 0, 0 ], "
+    "\"intIntPair\": [ 0, 0 ], "
     "\"subObj\": { \"intVal\": 0, \"intVector\": [ ], \"subSubObj\": { \"intVal\": 0 } }, "
     "\"subVector\": [ ], "
     "\"union1\": { \"type\": \"int\", \"value\": 0 }, "
index ab1a200..1546abc 100644 (file)
@@ -64,7 +64,6 @@ BOOST_AUTO_TEST_CASE(FromJsonString)
     BOOST_CHECK_EQUAL(123456, testConfig.uint32Val);
     BOOST_CHECK_EQUAL(1234567890123456789ll, testConfig.uint64Val);
     BOOST_CHECK_EQUAL("blah", testConfig.stringVal);
-    BOOST_CHECK_EQUAL("blah", testConfig.cstringVal);
     BOOST_CHECK_CLOSE(-1.234, testConfig.doubleVal, TOLERANCE);
     BOOST_CHECK_EQUAL(true, testConfig.boolVal);
     BOOST_CHECK(TestEnum::SECOND == testConfig.enumVal);
index b2a0372..d059b01 100644 (file)
@@ -29,8 +29,6 @@
 #include "utils/scoped-dir.hpp"
 #include "config/manager.hpp"
 
-#include "config/from-kvjson-visitor.hpp"
-
 namespace {
 
 using namespace utils;
@@ -70,12 +68,12 @@ void checkJsonConfig(const TestConfig& cfg, const std::string& json)
 void checkKVConfig(const TestConfig& cfg, const std::string& db)
 {
     KVStore store(db);
-    BOOST_CHECK_EQUAL(store.get<int>("conf.intVal"), cfg.intVal);
-    BOOST_CHECK_EQUAL(store.get<int64_t>("conf.int64Val"), cfg.int64Val);
-    BOOST_CHECK_EQUAL(store.get<bool>("conf.boolVal"), cfg.boolVal);
-    BOOST_CHECK_EQUAL(store.get<std::string>("conf.stringVal"), cfg.stringVal);
-    BOOST_CHECK_EQUAL(store.get<int>("conf.intVector"), cfg.intVector.size());
-    BOOST_CHECK_EQUAL(store.get<int>("conf.subObj.intVal"), cfg.subObj.intVal);
+    BOOST_CHECK_EQUAL(store.get("conf.intVal"), std::to_string(cfg.intVal));
+    BOOST_CHECK_EQUAL(store.get("conf.int64Val"), std::to_string(cfg.int64Val));
+    BOOST_CHECK_EQUAL(store.get("conf.boolVal"), std::to_string(cfg.boolVal));
+    BOOST_CHECK_EQUAL(store.get("conf.stringVal"), cfg.stringVal);
+    BOOST_CHECK_EQUAL(store.get("conf.intVector"), std::to_string(cfg.intVector.size()));
+    BOOST_CHECK_EQUAL(store.get("conf.subObj.intVal"), std::to_string(cfg.subObj.intVal));
 }
 
 BOOST_AUTO_TEST_CASE(ReadConfigDefaults)
index 13691c7..06fc701 100644 (file)
@@ -57,41 +57,6 @@ struct Fixture {
     }
 };
 
-class TestClass {
-public:
-    TestClass(int v): value(v) {}
-    TestClass(): value(0) {}
-    friend std::ostream& operator<< (std::ostream& out, const TestClass& cPoint);
-    friend std::istream& operator>> (std::istream& in, TestClass& cPoint);
-    friend bool operator== (const TestClass& lhs, const TestClass& rhs);
-    friend bool operator!= (const TestClass& lhs, const TestClass& rhs);
-
-private:
-    int value ;
-};
-
-bool operator==(const TestClass& lhs, const TestClass& rhs)
-{
-    return lhs.value == rhs.value;
-}
-
-bool operator!=(const TestClass& lhs, const TestClass& rhs)
-{
-    return lhs.value != rhs.value;
-}
-
-std::ostream& operator<< (std::ostream& out, const TestClass& tc)
-{
-    out << tc.value;;
-    return out;
-}
-
-std::istream& operator>> (std::istream& in, TestClass& tc)
-{
-    in >> tc.value;
-    return in;
-}
-
 } // namespace
 
 BOOST_FIXTURE_TEST_SUITE(KVStoreSuite, Fixture)
@@ -113,114 +78,77 @@ BOOST_AUTO_TEST_CASE(EscapedCharacters)
 {
     // '*' ?' '[' ']' are escaped
     // They shouldn't influence the internal implementation
-    std::string HARD_KEY = "[" + KEY;
-    BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
-    BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
-    BOOST_CHECK(c.exists(HARD_KEY));
-    BOOST_CHECK(c.exists(KEY));
-    BOOST_CHECK_NO_THROW(c.clear());
+    for (char sc: {'[', ']', '?', '*'}) {
+        std::string HARD_KEY = sc + KEY;
 
-    HARD_KEY = "]" + KEY;
-    BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
-    BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
-    BOOST_CHECK(c.exists(HARD_KEY));
-    BOOST_CHECK(c.exists(KEY));
-    BOOST_CHECK_NO_THROW(c.clear());
+        BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
+        BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
 
-    HARD_KEY = "?" + KEY;
-    BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
-    BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
-    BOOST_CHECK(c.exists(HARD_KEY));
-    BOOST_CHECK(c.exists(KEY));
-    BOOST_CHECK_NO_THROW(c.clear());
+        BOOST_CHECK(c.exists(HARD_KEY));
+        BOOST_CHECK(c.exists(KEY));
 
-    HARD_KEY = "*" + KEY;
-    BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
-    BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
-    BOOST_CHECK(c.exists(HARD_KEY));
-    BOOST_CHECK(c.exists(KEY));
+        BOOST_CHECK_NO_THROW(c.clear());
+    }
 }
 
-namespace {
-template<typename A, typename B>
-void testSingleValue(Fixture& f, const A& a, const B& b)
+BOOST_AUTO_TEST_CASE(PrefixExists)
 {
-    // Set
-    BOOST_CHECK_NO_THROW(f.c.set(KEY, a));
-    BOOST_CHECK_EQUAL(f.c.get<A>(KEY), a);
+    // '*' ?' '[' ']' are escaped
+    // They shouldn't influence the internal implementation
+    for (char sc: {'[', ']', '?', '*'}) {
+        std::string HARD_KEY = sc + KEY;
+        std::string FIELD_HARD_KEY = HARD_KEY + ".field";
 
-    // Update
-    BOOST_CHECK_NO_THROW(f.c.set(KEY, b));
-    BOOST_CHECK_EQUAL(f.c.get<B>(KEY), b);
-    BOOST_CHECK(f.c.exists(KEY));
+        BOOST_CHECK_NO_THROW(c.set(FIELD_HARD_KEY, "C"));
 
-    // Remove
-    BOOST_CHECK_NO_THROW(f.c.remove(KEY));
-    BOOST_CHECK(!f.c.exists(KEY));
-    BOOST_CHECK_THROW(f.c.get<B>(KEY), ConfigException);
-}
-} // namespace
+        BOOST_CHECK(!c.exists(KEY));
+        BOOST_CHECK(!c.exists(HARD_KEY));
+        BOOST_CHECK(c.exists(FIELD_HARD_KEY));
 
+        BOOST_CHECK(!c.prefixExists(KEY));
+        BOOST_CHECK(c.prefixExists(HARD_KEY));
+        BOOST_CHECK(c.prefixExists(FIELD_HARD_KEY));
 
-BOOST_AUTO_TEST_CASE(SingleValue)
-{
-    testSingleValue<std::string, std::string>(*this, "A", "B");
-    testSingleValue<int, int>(*this, 1, 2);
-    testSingleValue<double, double>(*this, 1.1, 2.2);
-    testSingleValue<int, std::string>(*this, 2, "A");
-    testSingleValue<int64_t, int64_t>(*this, INT64_MAX, INT64_MAX - 2);
-    testSingleValue<TestClass, int>(*this, 11, 22);
+        BOOST_CHECK_NO_THROW(c.clear());
+    }
 }
 
 namespace {
-template<typename T>
-void setVector(Fixture& f, const std::vector<T>& vec)
-{
-    std::vector<T> storedVec;
-    BOOST_CHECK_NO_THROW(f.c.set(KEY, vec));
-    BOOST_CHECK_NO_THROW(storedVec = f.c.get<std::vector<T>>(KEY))
-    BOOST_CHECK_EQUAL_COLLECTIONS(storedVec.begin(), storedVec.end(), vec.begin(), vec.end());
-}
-
-template<typename T>
-void testVectorOfValues(Fixture& f,
-                        const std::vector<T>& a,
-                        const std::vector<T>& b,
-                        const std::vector<T>& c)
+void testSingleValue(Fixture& f, const std::string& a, const std::string& b)
 {
     // Set
-    setVector(f, a);
-    setVector(f, b);
-    setVector(f, c);
+    BOOST_CHECK_NO_THROW(f.c.set(KEY, a));
+    BOOST_CHECK_EQUAL(f.c.get(KEY), a);
+
+    // Update
+    BOOST_CHECK_NO_THROW(f.c.set(KEY, b));
+    BOOST_CHECK_EQUAL(f.c.get(KEY), b);
+    BOOST_CHECK(f.c.exists(KEY));
 
     // Remove
     BOOST_CHECK_NO_THROW(f.c.remove(KEY));
     BOOST_CHECK(!f.c.exists(KEY));
-    BOOST_CHECK(f.c.isEmpty());
-    BOOST_CHECK_THROW(f.c.get<std::vector<T> >(KEY), ConfigException);
     BOOST_CHECK_THROW(f.c.get(KEY), ConfigException);
 }
 } // namespace
 
-BOOST_AUTO_TEST_CASE(VectorOfValues)
+
+BOOST_AUTO_TEST_CASE(SingleValue)
 {
-    testVectorOfValues<std::string>(*this, {"A", "B"}, {"A", "C"}, {"A", "B", "C"});
-    testVectorOfValues<int>(*this, {1, 2}, {1, 3}, {1, 2, 3});
-    testVectorOfValues<int64_t>(*this, {INT64_MAX, 2}, {1, 3}, {INT64_MAX, 2, INT64_MAX});
-    testVectorOfValues<double>(*this, {1.1, 2.2}, {1.1, 3.3}, {1.1, 2.2, 3.3});
-    testVectorOfValues<TestClass>(*this, {1, 2}, {1, 3}, {1, 2, 3});
+    testSingleValue(*this, "A", "B");
 }
 
 BOOST_AUTO_TEST_CASE(Clear)
 {
     BOOST_CHECK_NO_THROW(c.clear());
-    std::vector<std::string> vec = {"A", "B"};
-    BOOST_CHECK_NO_THROW(c.set(KEY, vec));
+    BOOST_CHECK_NO_THROW(c.set(KEY, "2"));
+    BOOST_CHECK_NO_THROW(c.set(KEY + ".0", "A"));
+    BOOST_CHECK_NO_THROW(c.set(KEY + ".1", "B"));
     BOOST_CHECK_NO_THROW(c.clear());
     BOOST_CHECK(c.isEmpty());
 
     BOOST_CHECK_NO_THROW(c.remove(KEY));
-    BOOST_CHECK_THROW(c.get<std::vector<std::string>>(KEY), ConfigException);
+    BOOST_CHECK_THROW(c.get(KEY), ConfigException);
     BOOST_CHECK_THROW(c.get(KEY), ConfigException);
 }
 
@@ -228,17 +156,17 @@ BOOST_AUTO_TEST_CASE(Transaction)
 {
     {
         KVStore::Transaction trans(c);
-        c.set<int>(KEY, 1);
+        c.set(KEY, "a");
         trans.commit();
     }
-    BOOST_CHECK_EQUAL(c.get<int>(KEY), 1);
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
 
     {
         KVStore::Transaction trans(c);
-        c.set<int>(KEY, 2);
+        c.set(KEY, "b");
         // no commit
     }
-    BOOST_CHECK_EQUAL(c.get<int>(KEY), 1);
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
 
     {
         KVStore::Transaction trans(c);
@@ -259,23 +187,23 @@ BOOST_AUTO_TEST_CASE(TransactionStacked)
         KVStore::Transaction transOuter(c);
         {
             KVStore::Transaction transInner(c);
-            c.set<int>(KEY, 1);
+            c.set(KEY, "a");
             // no inner commit
         }
         transOuter.commit();
     }
-    BOOST_CHECK_EQUAL(c.get<int>(KEY), 1);
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
 
     {
         KVStore::Transaction transOuter(c);
         {
             KVStore::Transaction transInner(c);
-            c.set<int>(KEY, 2);
+            c.set(KEY, "b");
             transInner.commit();
         }
         // no outer commit
     }
-    BOOST_CHECK_EQUAL(c.get<int>(KEY), 1);
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
 
     {
         KVStore::Transaction transOuter(c);
@@ -306,17 +234,4 @@ BOOST_AUTO_TEST_CASE(TransactionThreads)
     thread2.join();
 }
 
-BOOST_AUTO_TEST_CASE(Key)
-{
-    BOOST_CHECK_EQUAL(key(), "");
-    BOOST_CHECK_EQUAL(key<>(), "");
-    BOOST_CHECK_EQUAL(key(""), "");
-    BOOST_CHECK_EQUAL(key("KEY"), "KEY");
-    BOOST_CHECK_EQUAL(key<>("KEY"), "KEY");
-    BOOST_CHECK_EQUAL(key("KEY", "A"), "KEY.A");
-    BOOST_CHECK_EQUAL(key("KEY", 1, 2.2), "KEY.1.2.2");
-    BOOST_CHECK_EQUAL(key("KEY", 1, "B"), "KEY.1.B");
-    BOOST_CHECK_EQUAL(key<'_'>("KEY", 1, 2.2), "KEY_1_2.2");
-}
-
 BOOST_AUTO_TEST_SUITE_END()