3 * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
4 * Copyright (C) 2006 Trolltech AS. All rights reserved.
5 * Author: Thiago Macieira <thiago.macieira@trolltech.com>
7 * Licensed under the Academic Free License version 2.1
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation
21 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "qdbusmarshall_p.h"
26 #include "qdbustype_p.h"
27 #include "qdbustypehelper_p.h"
33 #include <qstringlist.h>
34 #include <qvarlengtharray.h>
37 #include <dbus/dbus.h>
39 static QVariant qFetchParameter(DBusMessageIter *it);
42 inline T qIterGet(DBusMessageIter *it)
45 dbus_message_iter_get_basic(it, &t);
50 inline QVariant qIterGet(DBusMessageIter *it)
53 dbus_message_iter_recurse(it, &sub);
54 return QDBusTypeHelper<QVariant>::toVariant(qFetchParameter(&sub));
57 template <typename DBusType, typename QtType>
58 inline QVariant qFetchList(DBusMessageIter *arrayIt)
63 dbus_message_iter_recurse(arrayIt, &it);
64 if (dbus_message_iter_get_array_len(&it) == 0)
65 return QDBusTypeHelper<QList<QtType> >::toVariant(list);
68 list.append( static_cast<QtType>( qIterGet<DBusType>(&it) ) );
69 } while (dbus_message_iter_next(&it));
71 return QDBusTypeHelper<QList<QtType> >::toVariant(list);
74 static QStringList qFetchStringList(DBusMessageIter *arrayIt)
79 dbus_message_iter_recurse(arrayIt, &it);
80 if (dbus_message_iter_get_array_len(&it) == 0)
84 list.append(QString::fromUtf8(qIterGet<char *>(&it)));
85 } while (dbus_message_iter_next(&it));
90 static QVariant qFetchParameter(DBusMessageIter *it)
92 switch (dbus_message_iter_get_arg_type(it)) {
94 return qVariantFromValue(qIterGet<unsigned char>(it));
96 return qVariantFromValue(qIterGet<dbus_int16_t>(it));
97 case DBUS_TYPE_UINT16:
98 return qVariantFromValue(qIterGet<dbus_uint16_t>(it));
100 return qIterGet<dbus_int32_t>(it);
101 case DBUS_TYPE_UINT32:
102 return qIterGet<dbus_uint32_t>(it);
103 case DBUS_TYPE_DOUBLE:
104 return qIterGet<double>(it);
105 case DBUS_TYPE_BOOLEAN:
106 return bool(qIterGet<dbus_bool_t>(it));
107 case DBUS_TYPE_INT64:
108 return static_cast<qlonglong>(qIterGet<dbus_int64_t>(it));
109 case DBUS_TYPE_UINT64:
110 return static_cast<qulonglong>(qIterGet<dbus_uint64_t>(it));
111 case DBUS_TYPE_STRING:
112 case DBUS_TYPE_OBJECT_PATH:
113 case DBUS_TYPE_SIGNATURE:
114 return QString::fromUtf8(qIterGet<char *>(it));
115 case DBUS_TYPE_VARIANT:
116 return qIterGet<QVariant>(it);
117 case DBUS_TYPE_ARRAY: {
118 int arrayType = dbus_message_iter_get_element_type(it);
121 case DBUS_TYPE_BYTE: {
124 dbus_message_iter_recurse(it, &sub);
125 int len = dbus_message_iter_get_array_len(&sub);
127 dbus_message_iter_get_fixed_array(&sub,&data,&len);
128 return QByteArray(data,len);
130 case DBUS_TYPE_INT16:
131 return qFetchList<dbus_int16_t, short>(it);
132 case DBUS_TYPE_UINT16:
133 return qFetchList<dbus_uint16_t, ushort>(it);
134 case DBUS_TYPE_INT32:
135 return qFetchList<dbus_int32_t, int>(it);
136 case DBUS_TYPE_UINT32:
137 return qFetchList<dbus_uint32_t, uint>(it);
138 case DBUS_TYPE_BOOLEAN:
139 return qFetchList<dbus_bool_t, bool>(it);
140 case DBUS_TYPE_DOUBLE:
141 return qFetchList<double, double>(it);
142 case DBUS_TYPE_INT64:
143 return qFetchList<dbus_int64_t, qlonglong>(it);
144 case DBUS_TYPE_UINT64:
145 return qFetchList<dbus_uint64_t, qulonglong>(it);
146 case DBUS_TYPE_STRING:
147 case DBUS_TYPE_OBJECT_PATH:
148 case DBUS_TYPE_SIGNATURE:
149 return qFetchStringList(it);
150 case DBUS_TYPE_VARIANT:
151 return qFetchList<QVariant, QVariant>(it);
152 case DBUS_TYPE_DICT_ENTRY: {
153 // ### support other types of maps?
154 QMap<QString, QVariant> map;
157 dbus_message_iter_recurse(it, &sub);
158 if (dbus_message_iter_get_array_len(&sub) == 0)
163 DBusMessageIter itemIter;
164 dbus_message_iter_recurse(&sub, &itemIter);
165 Q_ASSERT(dbus_message_iter_has_next(&itemIter));
166 QString key = qFetchParameter(&itemIter).toString();
167 dbus_message_iter_next(&itemIter);
168 map.insertMulti(key, qFetchParameter(&itemIter));
169 } while (dbus_message_iter_next(&sub));
175 // common handling for structs and lists of lists (for now)
176 case DBUS_TYPE_STRUCT: {
177 QList<QVariant> list;
179 dbus_message_iter_recurse(it, &sub);
180 if (dbus_message_iter_get_array_len(&sub) == 0)
183 list.append(qFetchParameter(&sub));
184 } while (dbus_message_iter_next(&sub));
189 qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it));
195 void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
200 if (!dbus_message_iter_init(message, &it))
204 list.append(qFetchParameter(&it));
205 } while (dbus_message_iter_next(&it));
208 // convert the variant to the given type and return true if it worked.
209 // if the type is not known, guess it from the variant and set.
210 // return false if conversion failed.
211 static bool checkType(QVariant &var, QDBusType &type)
213 if (!type.isValid()) {
214 // guess it from the variant
215 type = QDBusType::guessFromVariant(var);
219 int id = var.userType();
221 if (type.dbusType() == DBUS_TYPE_VARIANT) {
222 // this is a non symmetrical operation:
223 // nest a QVariant if we want variant and it isn't so
224 if (id != QDBusTypeHelper<QVariant>::id()) {
226 var = QDBusTypeHelper<QVariant>::toVariant(tmp);
233 case QMetaType::Short:
234 case QMetaType::UShort:
235 case QMetaType::UChar:
238 case QVariant::LongLong:
239 case QVariant::ULongLong:
240 case QVariant::Double:
241 case QVariant::String:
243 // QVariant can handle this on its own
246 // cannot handle this
247 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
248 type.dbusSignature().constData());
252 case QVariant::ByteArray:
253 // make sure it's an "ARRAY of BYTE"
254 if (type.qvariantType() != QVariant::ByteArray) {
255 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
256 type.dbusSignature().constData());
262 case QVariant::StringList:
263 // make sure it's "ARRAY of STRING"
264 if (type.qvariantType() != QVariant::StringList) {
265 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
266 type.dbusSignature().constData());
273 // could be either struct or array
274 if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) {
275 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
276 type.dbusSignature().constData());
285 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
286 type.dbusSignature().constData());
293 case QVariant::Invalid: {
294 // create an empty variant
296 var = QVariant(type.qvariantType(), null);
301 if (id == QDBusTypeHelper<QVariant>::id()) {
302 // if we got here, it means the match above for DBUS_TYPE_VARIANT didn't work
303 qWarning("Invalid conversion from nested variant to '%s'",
304 type.dbusSignature().constData());
306 } else if (type.dbusType() == DBUS_TYPE_ARRAY) {
307 int subType = type.arrayElement().dbusType();
308 if ((id == QDBusTypeHelper<bool>::listId() && subType == DBUS_TYPE_BOOLEAN) ||
309 (id == QDBusTypeHelper<short>::listId() && subType == DBUS_TYPE_INT16) ||
310 (id == QDBusTypeHelper<ushort>::listId() && subType == DBUS_TYPE_UINT16) ||
311 (id == QDBusTypeHelper<int>::listId() && subType == DBUS_TYPE_INT32) ||
312 (id == QDBusTypeHelper<uint>::listId() && subType == DBUS_TYPE_UINT32) ||
313 (id == QDBusTypeHelper<qlonglong>::listId() && subType == DBUS_TYPE_INT64) ||
314 (id == QDBusTypeHelper<qulonglong>::listId() && subType == DBUS_TYPE_UINT64) ||
315 (id == QDBusTypeHelper<double>::listId() && subType == DBUS_TYPE_DOUBLE))
319 qWarning("Invalid conversion from %s to '%s'", var.typeName(),
320 type.dbusSignature().constData());
324 qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(),
330 static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
331 const QDBusType &type);
333 static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list,
334 const QDBusTypeList &typelist);
337 static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg)
339 dbus_message_iter_append_basic(it, type.dbusType(), &arg);
342 template<typename DBusType, typename QtType>
343 static void qAppendListToMessage(DBusMessageIter *it, const QDBusType &subType,
346 QList<QtType> list = QDBusTypeHelper<QList<QtType> >::fromVariant(var);
347 foreach (const QtType &item, list)
348 qIterAppend(it, subType, static_cast<DBusType>(item));
351 static void qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType,
355 dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub);
359 case QVariant::StringList: {
360 const QStringList list = var.toStringList();
361 foreach (QString str, list)
362 qIterAppend(&sub, subType, str.toUtf8().constData());
366 case QVariant::ByteArray: {
367 const QByteArray array = var.toByteArray();
368 const char* cdata = array.constData();
369 dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length());
373 case QVariant::Map: {
374 const QVariantMap map = var.toMap();
375 const QDBusTypeList& subTypes = subType.subTypes();
376 for (QMap<QString, QVariant>::const_iterator mit = map.constBegin();
377 mit != map.constEnd(); ++mit) {
378 DBusMessageIter itemIterator;
379 dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator);
381 // let the string be converted to QVariant
382 qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0]);
383 qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1]);
385 dbus_message_iter_close_container(&sub, &itemIterator);
390 case QVariant::List: {
391 const QVariantList list = var.toList();
392 foreach (QVariant v, list)
393 qVariantToIteratorInternal(&sub, v, subType);
398 int id = var.userType();
399 if (id == QDBusTypeHelper<bool>::listId())
400 qAppendListToMessage<dbus_bool_t,bool>(&sub, subType, var);
401 else if (id == QDBusTypeHelper<short>::listId())
402 qAppendListToMessage<dbus_int16_t,short>(&sub, subType, var);
403 else if (id == QDBusTypeHelper<ushort>::listId())
404 qAppendListToMessage<dbus_uint16_t,ushort>(&sub, subType, var);
405 else if (id == QDBusTypeHelper<int>::listId())
406 qAppendListToMessage<dbus_int32_t,int>(&sub, subType, var);
407 else if (id == QDBusTypeHelper<uint>::listId())
408 qAppendListToMessage<dbus_uint32_t,uint>(&sub, subType, var);
409 else if (id == QDBusTypeHelper<qlonglong>::listId())
410 qAppendListToMessage<dbus_int64_t,qlonglong>(&sub, subType, var);
411 else if (id == QDBusTypeHelper<qulonglong>::listId())
412 qAppendListToMessage<dbus_uint64_t,qulonglong>(&sub, subType, var);
413 else if (id == QDBusTypeHelper<double>::listId())
414 qAppendListToMessage<double,double>(&sub, subType, var);
415 #if 0 // never reached, since QVariant::List mached
416 else if (id == QDBusTypeHelper<QVariant>::listId())
417 qAppendListToMessage<QVariant,QVariant>(&sub, subType, var);
420 qFatal("qAppendArrayToMessage got unknown type!");
425 dbus_message_iter_close_container(it, &sub);
428 static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList,
429 const QVariantList &list)
432 dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub);
433 qListToIterator(&sub, list, typeList);
434 dbus_message_iter_close_container(it, &sub);
437 static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType &type,
440 Q_UNUSED(type); // type is 'v'
443 if (var.userType() == QDBusTypeHelper<QVariant>::id())
444 arg = QDBusTypeHelper<QVariant>::fromVariant(var); // extract the inner variant
446 QDBusType t = QDBusType::guessFromVariant(arg);
448 // now add this variant
450 dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub);
451 qVariantToIteratorInternal(&sub, arg, t);
452 dbus_message_iter_close_container(it, &sub);
455 static void qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type)
457 if (var.isNull() && !type.isValid())
458 return; // cannot add a null like this
459 if (!checkType(var, type))
460 return; // type checking failed
462 qVariantToIteratorInternal(it, var, type);
465 static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
466 const QDBusType &type)
468 switch (type.dbusType()) {
470 qIterAppend( it, type, QDBusTypeHelper<uchar>::fromVariant(var) );
472 case DBUS_TYPE_BOOLEAN:
473 qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) );
475 case DBUS_TYPE_INT16:
476 qIterAppend( it, type, QDBusTypeHelper<short>::fromVariant(var) );
478 case DBUS_TYPE_UINT16:
479 qIterAppend( it, type, QDBusTypeHelper<ushort>::fromVariant(var) );
481 case DBUS_TYPE_INT32:
482 qIterAppend( it, type, static_cast<dbus_int32_t>(var.toInt()) );
484 case DBUS_TYPE_UINT32:
485 qIterAppend( it, type, static_cast<dbus_uint32_t>(var.toUInt()) );
487 case DBUS_TYPE_INT64:
488 qIterAppend( it, type, static_cast<dbus_int64_t>(var.toLongLong()) );
490 case DBUS_TYPE_UINT64:
491 qIterAppend( it, type, static_cast<dbus_uint64_t>(var.toULongLong()) );
493 case DBUS_TYPE_DOUBLE:
494 qIterAppend( it, type, var.toDouble() );
496 case DBUS_TYPE_STRING:
497 case DBUS_TYPE_OBJECT_PATH:
498 case DBUS_TYPE_SIGNATURE:
499 qIterAppend( it, type, var.toString().toUtf8().constData() );
503 case DBUS_TYPE_ARRAY:
504 // could be many things
505 qAppendArrayToMessage( it, type.arrayElement(), var );
508 case DBUS_TYPE_VARIANT:
509 qAppendVariantToMessage( it, type, var );
512 case DBUS_TYPE_STRUCT:
513 qAppendStructToMessage( it, type.subTypes(), var.toList() );
516 case DBUS_TYPE_DICT_ENTRY:
517 qFatal("qVariantToIterator got a DICT_ENTRY!");
521 qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData());
526 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
528 for (int i = 0; i < list.count(); ++i)
529 qVariantToIterator(it, list.at(i), QDBusType());
532 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, const QDBusTypeList &types)
534 int min = qMin(list.count(), types.count());
535 for (int i = 0; i < min; ++i)
536 qVariantToIterator(it, list.at(i), types.at(i));
538 for (int i = min; i < types.count(); ++i)
539 // we're missing a few arguments, so add default parameters
540 qVariantToIterator(it, QVariant(), types.at(i));
543 void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg,
544 const QString &signature)
548 dbus_message_iter_init_append(msg, &it);
550 if (signature.isEmpty())
551 qListToIterator(&it, list);
553 qListToIterator(&it, list, QDBusTypeList(signature.toUtf8()));