/* Copyright 2010 The glibmm Development Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include namespace Glib { /****************** VariantBase ***********************************/ VariantBase::VariantBase(GVariant* castitem, bool make_a_copy /* = false */) { if (castitem) { if (g_variant_is_floating(castitem)) g_variant_ref_sink(castitem); if (make_a_copy) g_variant_ref(castitem); } gobject_ = castitem; } VariantBase::operator bool() const { return gobj(); } void VariantBase::init(const GVariant* cobject, bool take_a_reference) { if (gobject_) g_variant_unref(gobject_); gobject_ = const_cast(cobject); if (take_a_reference) g_variant_ref(gobject_); } bool VariantBase::operator==(const VariantBase& other) const { return equal(other); } bool VariantBase::operator!=(const VariantBase& other) const { return !equal(other); } void VariantBase::get_normal_form(VariantBase& result) const { GVariant* const g_value = g_variant_get_normal_form(const_cast(gobj())); // The C function never returns NULL, according to its documenation, // so we don't need a bool return value. result.init(g_value); // g_value is already referenced. } void VariantBase::byteswap(VariantBase& result) const { GVariant* const g_value = g_variant_byteswap(const_cast(gobj())); result.init(g_value); // g_value is already referenced. } bool VariantBase::is_castable_to(const VariantType& supertype) const { const std::string subtype_string = get_type_string(); const std::string supertype_string = supertype.get_string(); // The following code is similar to g_variant_type_is_subtype_of(), with // these differences: // - Both types are assumed to be definite types (no indefinite types, // no 'r', '?' or '*'). // - VARIANT_TYPE_OBJECT_PATH (o) and VARIANT_TYPE_SIGNATURE (g) can be cast to // VARIANT_TYPE_STRING (s), (Variant::variant_type()). // - VARIANT_TYPE_STRING (s), VARIANT_TYPE_OBJECT_PATH (o) and // VARIANT_TYPE_SIGNATURE (g) can be cast to VARIANT_TYPE_BYTESTRING (ay), // (Variant::variant_type()). // - VARIANT_TYPE_HANDLE (h) can be cast to VARIANT_TYPE_INT32 (i), // (Variant::variant_type()). std::size_t subtype_index = 0; std::size_t supertype_index = 0; const std::size_t supertype_size = supertype_string.size(); while (supertype_index < supertype_size) { const char supertype_char = supertype_string[supertype_index++]; const char subtype_char = subtype_string[subtype_index++]; if (supertype_char == subtype_char) continue; switch (supertype_char) { case 's': if (!(subtype_char == 'o' || subtype_char == 'g')) return false; break; case 'a': if (!(supertype_string[supertype_index] == 'y' && (subtype_char == 's' || subtype_char == 'o' || subtype_char == 'g'))) return false; supertype_index++; break; case 'i': if (!(subtype_char == 'h')) return false; break; default: return false; } } return true; } /****************** VariantStringBase ***********************************/ VariantStringBase::VariantStringBase() : VariantBase() { } VariantStringBase::VariantStringBase(GVariant* castitem, bool take_a_reference) : VariantBase(castitem, take_a_reference) { } // static void VariantStringBase::create_object_path(VariantStringBase& output, const std::string& object_path) { GVariant* result = nullptr; result = g_variant_new_object_path(object_path.c_str()); g_variant_ref_sink(result); output.init(result); } // static void VariantStringBase::create_signature(VariantStringBase& output, const std::string& signature) { GVariant* result = nullptr; result = g_variant_new_signature(signature.c_str()); g_variant_ref_sink(result); output.init(result); } /****************** VariantContainerBase ***********************************/ VariantContainerBase::VariantContainerBase() : VariantBase() { } VariantContainerBase::VariantContainerBase(GVariant* castitem, bool take_a_reference) : VariantBase(castitem, take_a_reference) { } // static VariantContainerBase VariantContainerBase::create_tuple(const std::vector& children) { using var_ptr = GVariant*; var_ptr* const var_array = new var_ptr[children.size()]; for (std::vector::size_type i = 0; i < children.size(); i++) var_array[i] = const_cast(children[i].gobj()); VariantContainerBase result = VariantContainerBase(g_variant_new_tuple(var_array, children.size())); delete[] var_array; return result; } // static VariantContainerBase VariantContainerBase::create_tuple(const VariantBase& child) { std::vector vec; vec.emplace_back(child); return create_tuple(vec); } // static VariantContainerBase VariantContainerBase::create_maybe(const VariantType& child_type, const VariantBase& child) { GVariant* g_variant = g_variant_new_maybe(child_type.gobj(), const_cast(child.gobj())); VariantContainerBase result = VariantContainerBase(g_variant); return result; } void VariantContainerBase::get_child(VariantBase& child, gsize index) const { if (index >= get_n_children()) throw std::out_of_range("VariantContainerBase::get_child(): Index out of bounds."); GVariant* const gvariant = g_variant_get_child_value(gobject_, index); child.init(gvariant); } // VariantContainerBase has no method variant_type() template <> VariantContainerBase VariantBase::cast_dynamic(const VariantBase& v) { if (!v.gobj()) return VariantContainerBase(); if (v.get_type().is_container()) { return VariantContainerBase(const_cast(v.gobj()), true); } else { // std::cerr << "vtype=" << v.get_type_string() << std::endl; throw std::bad_cast(); } } bool VariantContainerBase::get_maybe(VariantBase& maybe) const { GVariant* const g_value = g_variant_get_maybe(const_cast(gobj())); if (g_value) { maybe.init(g_value); // g_value is already referenced. return true; } else { return false; } } VariantIter VariantContainerBase::get_iter(const VariantType& container_variant_type) const { // Get the GVariantIter. // If the underlying GVariant can be cast to the type of the container, // use the actual type (which may differ from container_variant_type, if // the GVariant contains strings, object paths or DBus type signatures), // otherwise let g_variant_get() report what's wrong with the type. GVariantIter* g_iter = nullptr; g_variant_get(const_cast(gobj()), is_castable_to(container_variant_type) ? get_type_string().c_str() : container_variant_type.get_string().c_str(), &g_iter); return VariantIter(g_iter); } /****************** Specializations ***********************************/ /*--------------------Variant---------------------*/ Variant::Variant() : VariantContainerBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantContainerBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_VARIANT; } Variant Variant::create(const VariantBase& data) { auto result = Variant(g_variant_new_variant(const_cast(data.gobj()))); return result; } void Variant::get(VariantBase& variant) const { GVariant* const gvariant = g_variant_get_variant(gobject_); variant.init(gvariant); } /*--------------------Variant---------------------*/ Variant::Variant() : VariantStringBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantStringBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_STRING; } Variant Variant::create(const Glib::ustring& data) { auto result = Variant(g_variant_new_string(data.c_str())); return result; } Glib::ustring Variant::get() const { return convert_const_gchar_ptr_to_ustring(g_variant_get_string(gobject_, nullptr)); } /*--------------------Variant---------------------*/ Variant::Variant() : VariantStringBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantStringBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_OBJECT_PATH; } Variant Variant::create(const Glib::DBusObjectPathString& data) { auto result = Variant(g_variant_new_object_path(data.c_str())); return result; } Glib::DBusObjectPathString Variant::get() const { const char* s = g_variant_get_string(gobject_, nullptr); return s ? CppType(s) : CppType(); } /*--------------------Variant---------------------*/ Variant::Variant() : VariantStringBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantStringBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_SIGNATURE; } Variant Variant::create(const Glib::DBusSignatureString& data) { auto result = Variant(g_variant_new_signature(data.c_str())); return result; } Glib::DBusSignatureString Variant::get() const { const char* s = g_variant_get_string(gobject_, nullptr); return s ? CppType(s) : CppType(); } /*--------------------Variant---------------------*/ Variant::Variant() : VariantStringBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantStringBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_BYTESTRING; } Variant Variant::create(const std::string& data) { auto result = Variant(g_variant_new_bytestring(data.c_str())); return result; } std::string Variant::get() const { const VariantType vtype = get_type(); const char* pch = nullptr; if (vtype.equal(VARIANT_TYPE_BYTESTRING)) pch = g_variant_get_bytestring(gobject_); else // g_variant_get_string() can handle strings, object paths, and signatures. pch = g_variant_get_string(gobject_, nullptr); return convert_const_gchar_ptr_to_stdstring(pch); } /*--------------------Variant< std::vector >---------------------*/ using type_vec_ustring = std::vector; Variant::Variant() : VariantContainerBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantContainerBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_STRING_ARRAY; } Variant Variant::create(const type_vec_ustring& data) { // Get the variant type of the elements. VariantType element_variant_type = Variant::variant_type(); // Get the variant type of the array. VariantType array_variant_type = Variant::variant_type(); // Create a GVariantBuilder to build the array. GVariantBuilder* builder = g_variant_builder_new(array_variant_type.gobj()); // Add the elements of the vector into the builder. for (const auto& str : data) g_variant_builder_add(builder, element_variant_type.get_string().c_str(), str.c_str()); // Create the variant using the builder. auto result = Variant(g_variant_new(array_variant_type.get_string().c_str(), builder)); g_variant_builder_unref(builder); return result; } Glib::ustring Variant::get_child(gsize index) const { if (index >= get_n_children()) throw std::out_of_range( "Variant< std::vector >::get_child(): Index out of bounds."); GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index); return Glib::Variant(gvariant).get(); } type_vec_ustring Variant::get() const { // g_variant_get_strv() can be used only if the type is VARIANT_TYPE_STRING_ARRAY, // but the Variant can alternatively hold an array of object paths or DBus type signatures. type_vec_ustring result; for (gsize i = 0, n_children = get_n_children(); i < n_children; ++i) { GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), i); result.emplace_back(Glib::Variant(gvariant).get()); } return result; } VariantIter Variant::get_iter() const { return VariantContainerBase::get_iter(variant_type()); } /*--------------------Variant>---------------------*/ using type_vec_opstring = std::vector; Variant::Variant() : VariantContainerBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantContainerBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_OBJECT_PATH_ARRAY; } // static Variant Variant::create(const type_vec_opstring& data) { // Get the variant type of the elements. VariantType element_variant_type = Variant::variant_type(); // Get the variant type of the array. VariantType array_variant_type = Variant::variant_type(); // Create a GVariantBuilder to build the array. GVariantBuilder* builder = g_variant_builder_new(array_variant_type.gobj()); // Add the elements of the vector into the builder. for (const auto& str : data) g_variant_builder_add(builder, element_variant_type.get_string().c_str(), str.c_str()); // Create the variant using the builder. auto result = Variant(g_variant_new(array_variant_type.get_string().c_str(), builder)); g_variant_builder_unref(builder); return result; } Glib::DBusObjectPathString Variant::get_child(gsize index) const { if (index >= get_n_children()) throw std::out_of_range( "Variant< std::vector >::get_child(): Index out of bounds."); GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index); return Glib::Variant(gvariant).get(); } type_vec_opstring Variant::get() const { gsize n_children = 0; const gchar** children = g_variant_get_objv(const_cast(gobj()), &n_children); type_vec_opstring result = type_vec_opstring(children, children+n_children); g_free(children); return result; } VariantIter Variant::get_iter() const { return VariantContainerBase::get_iter(variant_type()); } /*--------------------Variant< std::vector >---------------------*/ using type_vec_string = std::vector; Variant::Variant() : VariantContainerBase() { } Variant::Variant(GVariant* castitem, bool take_a_reference) : VariantContainerBase(castitem, take_a_reference) { } // static const VariantType& Variant::variant_type() { return VARIANT_TYPE_BYTESTRING_ARRAY; } Variant Variant::create(const type_vec_string& data) { // Create a string array to add the strings of the vector to. char** str_array = g_new(char*, data.size() + 1); // Add the elements of the vector into the string array. for (type_vec_string::size_type i = 0; i < data.size(); i++) str_array[i] = g_strdup(data[i].c_str()); // Terminate the string array. str_array[data.size()] = nullptr; // Create the variant using g_variant_new_bytestring_array() (passing the // newly constructed array. auto result = Variant(g_variant_new_bytestring_array(str_array, data.size())); g_strfreev(str_array); return result; } Variant Variant::create_from_object_paths(const type_vec_string& data) { // Create a string array to add the strings of the vector to. char** str_array = g_new(char*, data.size() + 1); // Add the elements of the vector into the string array. for (type_vec_string::size_type i = 0; i < data.size(); i++) str_array[i] = g_strdup(data[i].c_str()); // Terminate the string array. str_array[data.size()] = nullptr; // Create the variant using g_variant_new_objv() (passing the // newly constructed array. auto result = Variant(g_variant_new_objv(str_array, data.size())); g_strfreev(str_array); return result; } std::string Variant::get_child(gsize index) const { if (index >= get_n_children()) throw std::out_of_range( "Variant< std::vector >::get_child(): Index out of bounds."); GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index); return Glib::Variant(gvariant).get(); } type_vec_string Variant::get() const { // g_variant_get_bytestring_array() can be used only if the type is VARIANT_TYPE_BYTESTRING_ARRAY, // but the Variant can alternatively hold an array of strings, object paths or DBus type // signatures. type_vec_string result; for (gsize i = 0, n_children = get_n_children(); i < n_children; ++i) { GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), i); result.emplace_back(Glib::Variant(gvariant).get()); } return result; } VariantIter Variant::get_iter() const { return VariantContainerBase::get_iter(variant_type()); } /*---------------------Value---------------------*/ void Value::set(CppType data) { set_variant(data.gobj()); } Value::CppType Value::get() const { return CppType(get_variant(), true); } } // namespace Glib