/*
* Copyright 2012-2016 Canonical Ltd.
*
* This program 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; version 3.
*
* This program 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 program. If not, see .
*
* Authors:
* Renato Araujo Oliveira Filho
* Marco Trevisan
*/
extern "C" {
#include
}
#include "converter.h"
#include
#include
#include
/*! \internal */
QVariant Converter::toQVariant(GVariant *value)
{
QVariant result;
if (value == NULL) {
return result;
}
const GVariantType *type = g_variant_get_type(value);
if (g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) {
result.setValue((bool)g_variant_get_boolean(value));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) {
result.setValue(g_variant_get_byte(value));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) {
result.setValue(qint16(g_variant_get_int16(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) {
result.setValue(quint16(g_variant_get_uint16(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) {
result.setValue(qint32(g_variant_get_int32(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) {
result.setValue(quint32(g_variant_get_uint32(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) {
result.setValue(qint64(g_variant_get_int64(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) {
result.setValue(quint64(g_variant_get_uint64(value)));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_DOUBLE)) {
result.setValue(g_variant_get_double(value));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING)) {
gsize size = 0;
const gchar *v = g_variant_get_string(value, &size);
result.setValue(QString::fromUtf8(v, size));
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING_ARRAY)) {
gsize size = 0;
const gchar **sa = g_variant_get_strv(value, &size);
QStringList list;
for (gsize i = 0; i < size; ++i) {
list << QString::fromUtf8(sa[i]);
}
result.setValue(list);
g_free(sa);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTESTRING)) {
gsize size = 0;
gchar *bs = g_variant_dup_bytestring(value, &size);
result.setValue(QByteArray::fromRawData(bs, size));
g_free(bs);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTESTRING_ARRAY)) {
gsize size = 0;
const gchar **bsa = g_variant_get_bytestring_array(value, &size);
QByteArrayList list;
for (gsize i = 0; i < size; ++i) {
list << bsa[i];
}
result.setValue(list);
g_free(bsa);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_VARIANT)) {
GVariant *var = g_variant_get_variant(value);
result = toQVariant(var);
g_variant_unref(var);
} else if (g_variant_type_equal(type, G_VARIANT_TYPE_VARDICT)) {
GVariantIter iter;
GVariant *vvalue;
gchar *key;
QVariantMap qmap;
g_variant_iter_init (&iter, value);
while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
{
qmap.insert(QString::fromUtf8(key), toQVariant(vvalue));
}
result.setValue(qmap);
} else if (g_variant_type_is_array(type)) {
QVariantList lst;
for (int i = 0, iMax = g_variant_n_children(value); i < iMax; i++) {
GVariant *child = g_variant_get_child_value(value, i);
lst << toQVariant(child);
g_variant_unref(child);
}
result.setValue(lst);
} else if (g_variant_type_is_tuple(type)) {
gsize size = g_variant_n_children(value);
QVariantList vlist;
for (gsize i=0; i < size; i++) {
GVariant *v = g_variant_get_child_value(value, i);
if (v) {
vlist << toQVariant(v);
g_variant_unref(v);
}
}
result.setValue(vlist);
} else {
qWarning() << "Unsupported GVariant value" << (char*) type;
}
/* TODO: implement convertions to others types
* G_VARIANT_TYPE_HANDLE
* G_VARIANT_TYPE_OBJECT_PATH
* G_VARIANT_TYPE_SIGNATURE
* G_VARIANT_TYPE_ANY
* G_VARIANT_TYPE_BASIC
* G_VARIANT_TYPE_MAYBE
* G_VARIANT_TYPE_UNIT
* G_VARIANT_TYPE_DICT_ENTRY
* G_VARIANT_TYPE_DICTIONARY
* G_VARIANT_TYPE_OBJECT_PATH_ARRAY
*/
return result;
}
QVariant Converter::toQVariantFromVariantString(const QString &variantString)
{
GVariant *gvariant;
GError *error = NULL;
if (variantString.isEmpty()) {
return QVariant();
}
gvariant = g_variant_parse (NULL, qUtf8Printable(variantString), NULL, NULL, &error);
if (error) {
qWarning() << "Impossible to parse" << variantString << "as variant string:"<< error->message;
g_error_free (error);
return QVariant();
}
const QVariant& qvariant = Converter::toQVariant(gvariant);
g_variant_unref (gvariant);
return qvariant;
}
GVariant* Converter::toGVariant(const QVariant &value)
{
GVariant *result = NULL;
if (value.isNull() || !value.isValid())
return result;
switch(value.type()) {
case QVariant::Bool:
result = g_variant_new_boolean(value.toBool());
break;
case QVariant::ByteArray:
result = g_variant_new_bytestring(value.toByteArray());
break;
case QVariant::Double:
result = g_variant_new_double(value.toDouble());
break;
case QVariant::Int:
result = g_variant_new_int32(value.toInt());
break;
case QVariant::LongLong:
result = g_variant_new_int64(value.toLongLong());
break;
case QVariant::String:
result = g_variant_new_string(qUtf8Printable(value.toString()));
break;
case QVariant::UInt:
result = g_variant_new_uint32(value.toUInt());
break;
case QVariant::ULongLong:
result = g_variant_new_uint64(value.toULongLong());
break;
case QMetaType::UChar:
result = g_variant_new_byte(value.value());
break;
case QMetaType::Short:
result = g_variant_new_int16(value.value());
break;
case QMetaType::UShort:
result = g_variant_new_uint16(value.value());
break;
case QMetaType::Long:
result = g_variant_new_int64(value.value());
break;
case QMetaType::ULong:
result = g_variant_new_uint64(value.value());
break;
case QVariant::Map:
{
GVariantBuilder *b;
GVariant *dict;
b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
QMapIterator i(value.toMap());
while (i.hasNext()) {
i.next();
g_variant_builder_add(b, "{sv}", qUtf8Printable(i.key()), toGVariant(i.value()));
}
result = g_variant_builder_end(b);
g_variant_builder_unref(b);
break;
}
case QMetaType::QByteArrayList:
{
const QByteArrayList &list = qvariant_cast(value);
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_BYTESTRING_ARRAY);
for (const QByteArray &ba : list) {
g_variant_builder_add_value(b, g_variant_new_bytestring(ba));
}
result = g_variant_builder_end(b);
g_variant_builder_unref(b);
break;
}
case QVariant::List:
{
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_TUPLE);
for (const QVariant &v : value.toList()) {
g_variant_builder_add_value(b, toGVariant(v));
}
result = g_variant_builder_end(b);
g_variant_builder_unref(b);
break;
}
case QVariant::StringList:
{
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
for (const QString &s : value.toStringList()) {
g_variant_builder_add(b, "s", qUtf8Printable(s));
}
result = g_variant_builder_end(b);
g_variant_builder_unref(b);
break;
}
default:
qWarning() << "QVariant type not supported:" << value.type();
}
return result;
}
GVariant* Converter::toGVariantWithSchema(const QVariant &value, const char* schema)
{
if (!g_variant_type_string_is_valid(schema)) {
return Converter::toGVariant(value);
}
GVariant* result = NULL;
const GVariantType* schema_type;
schema_type = g_variant_type_new(schema);
if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_BOOLEAN)) {
if (value.canConvert()) {
result = g_variant_new_boolean (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_BYTE)) {
if (value.canConvert()) {
result = g_variant_new_byte (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT16)) {
if (value.canConvert()) {
result = g_variant_new_int16 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT16)) {
if (value.canConvert()) {
result = g_variant_new_uint16 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT32)) {
if (value.canConvert()) {
result = g_variant_new_int32 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT32)) {
if (value.canConvert()) {
result = g_variant_new_uint32 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT64)) {
if (value.canConvert()) {
result = g_variant_new_int64 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT64)) {
if (value.canConvert()) {
result = g_variant_new_uint64 (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_DOUBLE)) {
if (value.canConvert()) {
result = g_variant_new_double (value.value());
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_STRING)) {
if (value.canConvert()) {
result = g_variant_new_string(qUtf8Printable(value.toString()));
}
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_VARIANT)) {
result = Converter::toGVariant(value);
} else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_VARDICT)) {
if (value.canConvert(QVariant::Map)) {
result = Converter::toGVariant(value.toMap());
}
} else if (g_variant_type_is_array(schema_type)) {
if (value.canConvert(QVariant::List)) {
const GVariantType* entryType = g_variant_type_element(schema_type);
const gchar* entryTypeString = g_variant_type_peek_string(entryType);
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
bool ok = true;
for (const QVariant &v : value.toList()) {
GVariant *data = toGVariantWithSchema(v, entryTypeString);
if (data) {
g_variant_builder_add_value(b, data);
} else {
ok = false;
qWarning() << "Failed to convert list to array with schema:" << schema;
break;
}
}
if (ok) {
result = g_variant_builder_end(b);
}
g_variant_builder_unref(b);
}
} else if (g_variant_type_is_tuple(schema_type)) {
if (value.canConvert(QVariant::List)) {
const GVariantType* entryType = g_variant_type_first(schema_type);
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_TUPLE);
bool ok = true;
for (const QVariant &v : value.toList()) {
gchar* entryTypeString = g_variant_type_dup_string(entryType);
GVariant *data = toGVariantWithSchema(v, entryTypeString);
g_free(entryTypeString);
if (data) {
g_variant_builder_add_value(b, data);
entryType = g_variant_type_next(entryType);
if (!entryType)
break;
} else {
ok = false;
qWarning() << "Failed to convert list to array with schema:" << schema;
break;
}
}
if (ok) {
result = g_variant_builder_end(b);
}
g_variant_builder_unref(b);
}
}
// fallback to straight convert.
if (!result) {
result = Converter::toGVariant(value);
}
return result;
}