+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Mateusz Malicki (m.malicki2@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 Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief GVariant visitor
+ */
+
+#ifndef CONFIG_FROM_GVARIANT_VISITOR_HPP
+#define CONFIG_FROM_GVARIANT_VISITOR_HPP
+
+#include "config/is-visitable.hpp"
+#include "config/exception.hpp"
+#include "config/is-union.hpp"
+
+#include <string>
+#include <vector>
+#include <cassert>
+#include <glib.h>
+
+namespace config {
+
+class FromGVariantVisitor {
+public:
+ explicit FromGVariantVisitor(GVariant* variant)
+ {
+ //Assume that the visited object is not a union
+ checkType(variant, G_VARIANT_TYPE_TUPLE);
+ mIter = g_variant_iter_new(variant);
+ }
+
+ FromGVariantVisitor(const FromGVariantVisitor& visitor)
+ : mIter(g_variant_iter_copy(visitor.mIter))
+ {
+ }
+
+ ~FromGVariantVisitor()
+ {
+ g_variant_iter_free(mIter);
+ }
+
+ FromGVariantVisitor& operator=(const FromGVariantVisitor&) = delete;
+
+ template<typename T>
+ void visit(const std::string& name, T& value)
+ {
+ GVariant* child = g_variant_iter_next_value(mIter);
+ if (child == NULL) {
+ throw config::ConfigException(
+ "GVariant doesn't match with config. Can't set '" + name + "'");
+ }
+ fromGVariant(child, value);
+ g_variant_unref(child);
+ }
+
+private:
+ GVariantIter* mIter;
+
+ explicit FromGVariantVisitor(GVariant* variant, bool isUnion)
+ {
+ if (isUnion) {
+ checkType(variant, G_VARIANT_TYPE_VARIANT);
+ variant = g_variant_get_variant(variant);
+ }
+ checkType(variant, G_VARIANT_TYPE_TUPLE);
+ mIter = g_variant_iter_new(variant);
+ }
+
+ static void checkType(GVariant* object, const GVariantType* type)
+ {
+ if (!g_variant_is_of_type(object, type)) {
+ throw ConfigException("Invalid field type");
+ }
+ }
+
+ static void fromGVariant(GVariant* object, int& value)
+ {
+ checkType(object, G_VARIANT_TYPE_INT32);
+ value = g_variant_get_int32(object);
+ }
+
+ static void fromGVariant(GVariant* object, std::int64_t& value)
+ {
+ checkType(object, G_VARIANT_TYPE_INT64);
+ value = g_variant_get_int64(object);
+ }
+
+ static void fromGVariant(GVariant* object, bool& value)
+ {
+ checkType(object, G_VARIANT_TYPE_BOOLEAN);
+ value = g_variant_get_boolean(object);
+ }
+
+ static void fromGVariant(GVariant* object, double& value)
+ {
+ checkType(object, G_VARIANT_TYPE_DOUBLE);
+ value = g_variant_get_double(object);
+ }
+
+ static void fromGVariant(GVariant* object, std::string& value)
+ {
+ checkType(object, G_VARIANT_TYPE_STRING);
+ value = g_variant_get_string(object, NULL);
+ }
+
+ template<typename T>
+ static void fromGVariant(GVariant* object, std::vector<T>& value)
+ {
+ checkType(object, G_VARIANT_TYPE_ARRAY);
+ GVariantIter iter;
+ g_variant_iter_init(&iter, object);
+ int length = g_variant_iter_n_children(&iter);
+ value.resize(static_cast<size_t>(length));
+ for (int i = 0; i < length; ++i) {
+ GVariant* child = g_variant_iter_next_value(&iter);
+ assert(child != NULL);
+ fromGVariant(child, value[static_cast<size_t>(i)]);
+ g_variant_unref(child);
+ }
+ }
+
+ template<typename T>
+ static typename std::enable_if<isVisitable<T>::value>::type
+ fromGVariant(GVariant* object, T& value)
+ {
+ FromGVariantVisitor visitor(object, isUnion<T>::value);
+ value.accept(visitor);
+ }
+
+};
+
+} // namespace config
+
+#endif // CONFIG_FROM_GVARIANT_VISITOR_HPP