diff options
Diffstat (limited to 'qt/qdbusmarshall.cpp')
-rw-r--r-- | qt/qdbusmarshall.cpp | 444 |
1 files changed, 303 insertions, 141 deletions
diff --git a/qt/qdbusmarshall.cpp b/qt/qdbusmarshall.cpp index 31c35896..19de0d91 100644 --- a/qt/qdbusmarshall.cpp +++ b/qt/qdbusmarshall.cpp @@ -1,6 +1,8 @@ /* qdbusmarshall.cpp * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,21 +17,22 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "qdbusmarshall.h" +#include "qdbustype.h" #include "qdbusvariant.h" -#include <QtCore/qdebug.h> -#include <QtCore/qvariant.h> -#include <QtCore/qlist.h> -#include <QtCore/qmap.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qvector.h> +#include <qdebug.h> +#include <qvariant.h> +#include <qlist.h> +#include <qmap.h> +#include <qstringlist.h> +#include <qvarlengtharray.h> +#include <qvector.h> #include <dbus/dbus.h> @@ -60,6 +63,10 @@ static QVariant qFetchParameter(DBusMessageIter *it) switch (dbus_message_iter_get_arg_type(it)) { case DBUS_TYPE_BYTE: return qIterGet<unsigned char>(it); + case DBUS_TYPE_INT16: + return qIterGet<dbus_int16_t>(it); + case DBUS_TYPE_UINT16: + return qIterGet<dbus_uint16_t>(it); case DBUS_TYPE_INT32: return qIterGet<dbus_int32_t>(it); case DBUS_TYPE_UINT32: @@ -78,15 +85,26 @@ static QVariant qFetchParameter(DBusMessageIter *it) return QString::fromUtf8(qIterGet<char *>(it)); case DBUS_TYPE_ARRAY: { int arrayType = dbus_message_iter_get_element_type(it); - if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH) { + if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH || + arrayType == DBUS_TYPE_SIGNATURE) { return qFetchStringList(it); + } else if (arrayType == DBUS_TYPE_BYTE) { + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + int len = dbus_message_iter_get_array_len(&sub); + char* data; + dbus_message_iter_get_fixed_array(&sub,&data,&len); + return QByteArray(data,len); } else if (arrayType == DBUS_TYPE_DICT_ENTRY) { // ### support other types of maps? QMap<QString, QVariant> map; DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); if (!dbus_message_iter_has_next(&sub)) + // empty map return map; + do { DBusMessageIter itemIter; dbus_message_iter_recurse(&sub, &itemIter); @@ -96,43 +114,30 @@ static QVariant qFetchParameter(DBusMessageIter *it) map.insertMulti(key, qFetchParameter(&itemIter)); } while (dbus_message_iter_next(&sub)); return map; - } else { - QList<QVariant> list; - DBusMessageIter sub; - dbus_message_iter_recurse(it, &sub); - if (!dbus_message_iter_has_next(&sub)) - return list; - do { - list.append(qFetchParameter(&sub)); - } while (dbus_message_iter_next(&sub)); - return list; } - break; } + } + // fall through + // common handling for structs and lists + case DBUS_TYPE_STRUCT: { + QList<QVariant> list; + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + if (!dbus_message_iter_has_next(&sub)) + return list; + do { + list.append(qFetchParameter(&sub)); + } while (dbus_message_iter_next(&sub)); + return list; + } case DBUS_TYPE_VARIANT: { QDBusVariant dvariant; DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); - dvariant.signature = QString::fromUtf8(dbus_message_iter_get_signature(&sub)); + dvariant.type = QDBusType(dbus_message_iter_get_signature(&sub)); dvariant.value = qFetchParameter(&sub); return qVariantFromValue(dvariant); } -#if 0 - case DBUS_TYPE_DICT: { - QMap<QString, QVariant> map; - DBusMessageIter sub; - dbus_message - if (dbus_message_iter_init_dict_iterator(it, &dictIt)) { - do { - map[QString::fromUtf8(dbus_message_iter_get_dict_key(&dictIt))] = - qFetchParameter(&dictIt); - } while (dbus_message_iter_next(&dictIt)); - } - return map; - break; } - case DBUS_TYPE_CUSTOM: - return qGetCustomValue(it); - break; -#endif + default: qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it)); return QVariant(); @@ -153,148 +158,305 @@ void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message) } while (dbus_message_iter_next(&it)); } -#define DBUS_APPEND(type,dtype,var) \ -type dtype##v=(var); \ -dbus_message_append_args(msg, dtype, &dtype##v, DBUS_TYPE_INVALID) -#define DBUS_APPEND_LIST(type,dtype,var,size) \ -type dtype##v=(var); \ -dbus_message_append_args(msg, DBUS_TYPE_ARRAY, dtype, &dtype##v, size, DBUS_TYPE_INVALID) - - -static void qAppendToMessage(DBusMessageIter *it, const QString &str) -{ - QByteArray ba = str.toUtf8(); - const char *cdata = ba.constData(); - dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &cdata); -} - -static QVariant::Type qVariantListType(const QList<QVariant> &list) +// convert the variant to the given type and return true if it worked. +// if the type is not known, guess it from the variant and set. +// return false if conversion failed. +static bool checkType(QVariant &var, QDBusType &type) { - // TODO - catch lists that have a list as first parameter - QVariant::Type tp = list.value(0).type(); - if (tp < QVariant::Int || tp > QVariant::Double) - return QVariant::Invalid; - - for (int i = 1; i < list.count(); ++i) { - const QVariant &var = list.at(i); - if (var.type() != tp - && (var.type() != QVariant::List || qVariantListType(var.toList()) != tp)) - return QVariant::Invalid; + if (!type.isValid()) { + // guess it from the variant + type = QDBusType::guessFromVariant(var); + return true; } - return tp; -} -static const char *qDBusListType(const QList<QVariant> &list) -{ - static const char *DBusArgs[] = { 0, 0, DBUS_TYPE_INT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING, - DBUS_TYPE_INT64_AS_STRING, DBUS_TYPE_UINT64_AS_STRING, DBUS_TYPE_DOUBLE_AS_STRING }; - - return DBusArgs[qVariantListType(list)]; -} - -static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list); + // only catch the conversions that won't work + // let QVariant do the hard work + + // QVariant can't convert QDBusVariant: + if (var.userType() == qMetaTypeId<QDBusVariant>()) { + if (type.dbusType() == DBUS_TYPE_VARIANT) + return true; // no change -static void qVariantToIterator(DBusMessageIter *it, const QVariant &var) -{ - static const int Variant2DBus[] = { DBUS_TYPE_INVALID, - DBUS_TYPE_BOOLEAN, DBUS_TYPE_INT32, DBUS_TYPE_UINT32, - DBUS_TYPE_INT64, DBUS_TYPE_UINT64, DBUS_TYPE_DOUBLE }; - - // these really are static asserts - Q_ASSERT(QVariant::Invalid == 0); - Q_ASSERT(QVariant::Int == 2); - Q_ASSERT(QVariant::Double == 6); + // convert manually + QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); + var = dvariant.value; + return checkType(var, type); + } + + if (type.dbusType() == DBUS_TYPE_VARIANT) { + // variant can handle anything. Let it pass + return true; + } - switch (var.type()) { + switch (var.userType()) { + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: case QVariant::Double: - dbus_message_iter_append_basic(it, Variant2DBus[var.type()], - var.constData()); - break; case QVariant::String: - qAppendToMessage(it, var.toString()); - break; + if (type.isBasic()) + // QVariant can handle this on its own + return true; + + // cannot handle this + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + + case QVariant::ByteArray: + // make sure it's an "ARRAY of BYTE" + if (type.qvariantType() != QVariant::ByteArray) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + return true; + + case QVariant::StringList: + // make sure it's "ARRAY of STRING" + if (type.qvariantType() != QVariant::StringList) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + return true; + + case QVariant::List: + // could be either struct or array + if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + + return true; + + case QVariant::Map: + if (!type.isMap()) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + + return true; + + case QVariant::Invalid: + // create an empty variant + var.convert(type.qvariantType()); + break; + } + + qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(), + var.typeName()); + var.clear(); + return false; +} + +static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, + const QDBusType &type); + +static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, + const QDBusTypeList &list); + +template<typename T> +static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg) +{ + dbus_message_iter_append_basic(it, type.dbusType(), &arg); +} + +static void qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType, + const QVariant &var) +{ + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub); + + switch (var.type()) + { case QVariant::StringList: { const QStringList list = var.toStringList(); - DBusMessageIter sub; - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &sub); - for (int s = 0; s < list.count(); ++s) - qAppendToMessage(&sub, list.at(s)); - dbus_message_iter_close_container(it, &sub); + foreach (QString str, list) + qIterAppend(&sub, subType, str.toUtf8().constData()); break; } - case QVariant::List: { - const QList<QVariant> &list = var.toList(); - const char *listType = qDBusListType(list); - if (!listType) { - qWarning("Don't know how to marshall list."); - break; - } - DBusMessageIter sub; - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, listType, &sub); - qListToIterator(&sub, list); - dbus_message_iter_close_container(it, &sub); + + case QVariant::ByteArray: { + const QByteArray array = var.toByteArray(); + const char* cdata = array.constData(); + dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length()); break; } + case QVariant::Map: { - // ### TODO - marshall more than qstring/qstring maps - const QMap<QString, QVariant> &map = var.toMap(); - DBusMessageIter sub; - QVarLengthArray<char, 16> sig; - sig.append(DBUS_DICT_ENTRY_BEGIN_CHAR); - sig.append(DBUS_TYPE_STRING); - sig.append(DBUS_TYPE_STRING); - sig.append(DBUS_DICT_ENTRY_END_CHAR); - sig.append('\0'); - qDebug() << QString::fromAscii(sig.constData()); - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, sig.constData(), &sub); + const QVariantMap map = var.toMap(); + const QDBusTypeList& subTypes = subType.subTypes(); for (QMap<QString, QVariant>::const_iterator mit = map.constBegin(); mit != map.constEnd(); ++mit) { DBusMessageIter itemIterator; dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator); - qAppendToMessage(&itemIterator, mit.key()); - qAppendToMessage(&itemIterator, mit.value().toString()); + + // let the string be converted to QVariant + qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0]); + qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1]); + dbus_message_iter_close_container(&sub, &itemIterator); } - dbus_message_iter_close_container(it, &sub); break; } - case QVariant::UserType: { - if (var.userType() == QMetaTypeId<QDBusVariant>::qt_metatype_id()) { - DBusMessageIter sub; - QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); - dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, - dvariant.signature.toUtf8().constData(), &sub); - qVariantToIterator(&sub, dvariant.value); - dbus_message_iter_close_container(it, &sub); - break; - } + + case QVariant::List: { + const QVariantList list = var.toList(); + foreach (QVariant v, list) + qVariantToIteratorInternal(&sub, v, subType); + break; } - // fall through + default: - qWarning("Don't know how to handle type %s", var.typeName()); + qFatal("qAppendArrayToMessage got unknown type!"); break; } + + dbus_message_iter_close_container(it, &sub); } -void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list) +static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList, + const QVariantList &list) { - if (list.isEmpty()) - return; + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub); + qListToIterator(&sub, list, typeList); + dbus_message_iter_close_container(it, &sub); +} + +static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType & /* type */, + const QVariant &var) +{ + QVariant v; + QDBusType t; + + if (var.userType() == qMetaTypeId<QDBusVariant>()) { + QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); + v = dvariant.value; + t = dvariant.type; + } + else { + v = var; + t = QDBusType::guessFromVariant(v); + } + + // now add this variant + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub); + qVariantToIteratorInternal(&sub, v, t); + dbus_message_iter_close_container(it, &sub); +} + +static void qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type) +{ + if (var.isNull() && !type.isValid()) + return; // cannot add a null like this + if (!checkType(var, type)) + return; // type checking failed + + qVariantToIteratorInternal(it, var, type); +} + +static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, + const QDBusType &type) +{ + switch (type.dbusType()) { + case DBUS_TYPE_BYTE: + qIterAppend( it, type, static_cast<unsigned char>(var.toUInt()) ); + break; + case DBUS_TYPE_BOOLEAN: + qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) ); + break; + case DBUS_TYPE_INT16: + qIterAppend( it, type, static_cast<dbus_int16_t>(var.toInt()) ); + break; + case DBUS_TYPE_UINT16: + qIterAppend( it, type, static_cast<dbus_uint16_t>(var.toUInt()) ); + break; + case DBUS_TYPE_INT32: + qIterAppend( it, type, static_cast<dbus_int32_t>(var.toInt()) ); + break; + case DBUS_TYPE_UINT32: + qIterAppend( it, type, static_cast<dbus_uint32_t>(var.toUInt()) ); + break; + case DBUS_TYPE_INT64: + qIterAppend( it, type, static_cast<dbus_int64_t>(var.toLongLong()) ); + break; + case DBUS_TYPE_UINT64: + qIterAppend( it, type, static_cast<dbus_uint64_t>(var.toULongLong()) ); + break; + case DBUS_TYPE_DOUBLE: + qIterAppend( it, type, var.toDouble() ); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + qIterAppend( it, type, var.toString().toUtf8().constData() ); + break; + + // compound types: + case DBUS_TYPE_ARRAY: + // could be many things + qAppendArrayToMessage( it, type.arrayElement(), var ); + break; + + case DBUS_TYPE_VARIANT: + qAppendVariantToMessage( it, type, var ); + break; + case DBUS_TYPE_STRUCT: + qAppendStructToMessage( it, type.subTypes(), var.toList() ); + break; + + case DBUS_TYPE_DICT_ENTRY: + qFatal("qVariantToIterator got a DICT_ENTRY!"); + break; + + default: + qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData()); + break; + } +} + +void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list) +{ for (int i = 0; i < list.count(); ++i) - qVariantToIterator(it, list.at(i)); + qVariantToIterator(it, list.at(i), QDBusType()); } -void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg) +void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, const QDBusTypeList &types) +{ + int min = qMin(list.count(), types.count()); + for (int i = 0; i < min; ++i) + qVariantToIterator(it, list.at(i), types.at(i)); + + for (int i = min; i < types.count(); ++i) + // we're missing a few arguments, so add default parameters + qVariantToIterator(it, QVariant(), types.at(i)); +} + +void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg, + const QString &signature) { Q_ASSERT(msg); DBusMessageIter it; dbus_message_iter_init_append(msg, &it); - qListToIterator(&it, list); + + if (signature.isEmpty()) + qListToIterator(&it, list); + else + qListToIterator(&it, list, QDBusTypeList(signature.toUtf8())); } |