qdbusmarshall.cpp

00001 /* qdbusmarshall.cpp
00002  *
00003  * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
00004  *
00005  * Licensed under the Academic Free License version 2.1
00006  *
00007  * This program is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  */
00022 
00023 #include "qdbusmarshall.h"
00024 #include "qdbusvariant.h"
00025 
00026 #include <QtCore/qdebug.h>
00027 #include <QtCore/qvariant.h>
00028 #include <QtCore/qlist.h>
00029 #include <QtCore/qmap.h>
00030 #include <QtCore/qstringlist.h>
00031 #include <QtCore/qvarlengtharray.h>
00032 #include <QtCore/qvector.h>
00033 
00034 #include <dbus/dbus.h>
00035 
00036 template <typename T>
00037 inline T qIterGet(DBusMessageIter *it)
00038 {
00039     T t;
00040     dbus_message_iter_get_basic(it, &t);
00041     return t;
00042 }
00043 
00044 static QStringList qFetchStringList(DBusMessageIter *arrayIt)
00045 {
00046     QStringList list;
00047 
00048     DBusMessageIter it;
00049     dbus_message_iter_recurse(arrayIt, &it);
00050 
00051     do {
00052         list.append(QString::fromUtf8(qIterGet<char *>(&it)));
00053     } while (dbus_message_iter_next(&it));
00054 
00055     return list;
00056 }
00057 
00058 static QVariant qFetchParameter(DBusMessageIter *it)
00059 {
00060     switch (dbus_message_iter_get_arg_type(it)) {
00061     case DBUS_TYPE_BYTE:
00062         return qIterGet<unsigned char>(it);
00063     case DBUS_TYPE_INT32:
00064         return qIterGet<dbus_int32_t>(it);
00065     case DBUS_TYPE_UINT32:
00066         return qIterGet<dbus_uint32_t>(it);
00067     case DBUS_TYPE_DOUBLE:
00068         return qIterGet<double>(it);
00069     case DBUS_TYPE_BOOLEAN:
00070         return qIterGet<dbus_bool_t>(it);
00071     case DBUS_TYPE_INT64:
00072         return (qlonglong) qIterGet<dbus_int64_t>(it);
00073     case DBUS_TYPE_UINT64:
00074         return (qulonglong) qIterGet<dbus_uint64_t>(it);
00075     case DBUS_TYPE_STRING:
00076     case DBUS_TYPE_OBJECT_PATH:
00077     case DBUS_TYPE_SIGNATURE:
00078         return QString::fromUtf8(qIterGet<char *>(it));
00079     case DBUS_TYPE_ARRAY: {
00080         int arrayType = dbus_message_iter_get_element_type(it);
00081         if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH) {
00082             return qFetchStringList(it);
00083         } else if (arrayType == DBUS_TYPE_DICT_ENTRY) {
00084             // ### support other types of maps?
00085             QMap<QString, QVariant> map;
00086             DBusMessageIter sub;
00087             dbus_message_iter_recurse(it, &sub);
00088             if (!dbus_message_iter_has_next(&sub))
00089                 return map;
00090             do {
00091                 DBusMessageIter itemIter;
00092                 dbus_message_iter_recurse(&sub, &itemIter);
00093                 Q_ASSERT(dbus_message_iter_has_next(&itemIter));
00094                 QString key = qFetchParameter(&itemIter).toString();
00095                 dbus_message_iter_next(&itemIter);
00096                 map.insertMulti(key, qFetchParameter(&itemIter));
00097             } while (dbus_message_iter_next(&sub));
00098             return map;
00099         } else {
00100             QList<QVariant> list;
00101             DBusMessageIter sub;
00102             dbus_message_iter_recurse(it, &sub);
00103             if (!dbus_message_iter_has_next(&sub))
00104                 return list;
00105             do {
00106                 list.append(qFetchParameter(&sub));
00107             } while (dbus_message_iter_next(&sub));
00108             return list;
00109         }
00110         break; }
00111     case DBUS_TYPE_VARIANT: {
00112         QDBusVariant dvariant;
00113         DBusMessageIter sub;
00114         dbus_message_iter_recurse(it, &sub);
00115         dvariant.signature = QString::fromUtf8(dbus_message_iter_get_signature(&sub));
00116         dvariant.value = qFetchParameter(&sub);
00117         return qVariantFromValue(dvariant);
00118     }
00119 #if 0
00120     case DBUS_TYPE_DICT: {
00121         QMap<QString, QVariant> map;
00122         DBusMessageIter sub;
00123         dbus_message
00124         if (dbus_message_iter_init_dict_iterator(it, &dictIt)) {
00125             do {
00126                 map[QString::fromUtf8(dbus_message_iter_get_dict_key(&dictIt))] =
00127                     qFetchParameter(&dictIt);
00128             } while (dbus_message_iter_next(&dictIt));
00129         }
00130         return map;
00131         break; }
00132     case DBUS_TYPE_CUSTOM:
00133         return qGetCustomValue(it);
00134         break;
00135 #endif
00136     default:
00137         qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it));
00138         return QVariant();
00139         break;
00140     }
00141 }
00142 
00143 void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
00144 {
00145     Q_ASSERT(message);
00146 
00147     DBusMessageIter it;
00148     if (!dbus_message_iter_init(message, &it))
00149         return;
00150 
00151     do {
00152         list.append(qFetchParameter(&it));
00153     } while (dbus_message_iter_next(&it));
00154 }
00155 
00156 #define DBUS_APPEND(type,dtype,var) \
00157 type dtype##v=(var); \
00158 dbus_message_append_args(msg, dtype, &dtype##v, DBUS_TYPE_INVALID)
00159 #define DBUS_APPEND_LIST(type,dtype,var,size) \
00160 type dtype##v=(var); \
00161 dbus_message_append_args(msg, DBUS_TYPE_ARRAY, dtype, &dtype##v, size, DBUS_TYPE_INVALID)
00162 
00163 
00164 static void qAppendToMessage(DBusMessageIter *it, const QString &str)
00165 {
00166     QByteArray ba = str.toUtf8();
00167     const char *cdata = ba.constData();
00168     dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &cdata);
00169 }
00170 
00171 static QVariant::Type qVariantListType(const QList<QVariant> &list)
00172 {
00173     // TODO - catch lists that have a list as first parameter
00174     QVariant::Type tp = list.value(0).type();
00175     if (tp < QVariant::Int || tp > QVariant::Double)
00176         return QVariant::Invalid;
00177 
00178     for (int i = 1; i < list.count(); ++i) {
00179         const QVariant &var = list.at(i);
00180         if (var.type() != tp
00181                && (var.type() != QVariant::List || qVariantListType(var.toList()) != tp))
00182             return QVariant::Invalid;
00183     }
00184     return tp;
00185 }
00186 
00187 static const char *qDBusListType(const QList<QVariant> &list)
00188 {
00189     static const char *DBusArgs[] = { 0, 0, DBUS_TYPE_INT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING,
00190             DBUS_TYPE_INT64_AS_STRING, DBUS_TYPE_UINT64_AS_STRING, DBUS_TYPE_DOUBLE_AS_STRING };
00191 
00192     return DBusArgs[qVariantListType(list)];
00193 }
00194 
00195 static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list);
00196 
00197 static void qVariantToIterator(DBusMessageIter *it, const QVariant &var)
00198 {
00199     static const int Variant2DBus[] = { DBUS_TYPE_INVALID,
00200         DBUS_TYPE_BOOLEAN, DBUS_TYPE_INT32, DBUS_TYPE_UINT32,
00201         DBUS_TYPE_INT64, DBUS_TYPE_UINT64, DBUS_TYPE_DOUBLE };
00202 
00203     // these really are static asserts
00204     Q_ASSERT(QVariant::Invalid == 0);
00205     Q_ASSERT(QVariant::Int == 2);
00206     Q_ASSERT(QVariant::Double == 6);
00207 
00208     switch (var.type()) {
00209     case QVariant::Int:
00210     case QVariant::UInt:
00211     case QVariant::LongLong:
00212     case QVariant::ULongLong:
00213     case QVariant::Double:
00214         dbus_message_iter_append_basic(it, Variant2DBus[var.type()],
00215                 var.constData());
00216         break;
00217     case QVariant::String:
00218         qAppendToMessage(it, var.toString());
00219         break;
00220     case QVariant::StringList: {
00221         const QStringList list = var.toStringList();
00222         DBusMessageIter sub;
00223         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
00224                                          DBUS_TYPE_STRING_AS_STRING, &sub);
00225         for (int s = 0; s < list.count(); ++s)
00226             qAppendToMessage(&sub, list.at(s));
00227         dbus_message_iter_close_container(it, &sub);
00228         break;
00229     }
00230     case QVariant::List: {
00231         const QList<QVariant> &list = var.toList();
00232         const char *listType = qDBusListType(list);
00233         if (!listType) {
00234             qWarning("Don't know how to marshall list.");
00235             break;
00236         }
00237         DBusMessageIter sub;
00238         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, listType, &sub);
00239         qListToIterator(&sub, list);
00240         dbus_message_iter_close_container(it, &sub);
00241         break;
00242     }
00243     case QVariant::Map: {
00244         // ### TODO - marshall more than qstring/qstring maps
00245         const QMap<QString, QVariant> &map = var.toMap();
00246         DBusMessageIter sub;
00247         QVarLengthArray<char, 16> sig;
00248         sig.append(DBUS_DICT_ENTRY_BEGIN_CHAR);
00249         sig.append(DBUS_TYPE_STRING);
00250         sig.append(DBUS_TYPE_STRING);
00251         sig.append(DBUS_DICT_ENTRY_END_CHAR);
00252         sig.append('\0');
00253         qDebug() << QString::fromAscii(sig.constData());
00254         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, sig.constData(), &sub);
00255         for (QMap<QString, QVariant>::const_iterator mit = map.constBegin();
00256              mit != map.constEnd(); ++mit) {
00257             DBusMessageIter itemIterator;
00258             dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator);
00259             qAppendToMessage(&itemIterator, mit.key());
00260             qAppendToMessage(&itemIterator, mit.value().toString());
00261             dbus_message_iter_close_container(&sub, &itemIterator);
00262         }
00263         dbus_message_iter_close_container(it, &sub);
00264         break;
00265     }
00266     case QVariant::UserType: {
00267         if (var.userType() == QMetaTypeId<QDBusVariant>::qt_metatype_id()) {
00268             DBusMessageIter sub;
00269             QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var);
00270             dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT,
00271                     dvariant.signature.toUtf8().constData(), &sub);
00272             qVariantToIterator(&sub, dvariant.value);
00273             dbus_message_iter_close_container(it, &sub);
00274             break;
00275         }
00276     }
00277     // fall through
00278     default:
00279         qWarning("Don't know how to handle type %s", var.typeName());
00280         break;
00281     }
00282 }
00283 
00284 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
00285 {
00286     if (list.isEmpty())
00287         return;
00288 
00289     for (int i = 0; i < list.count(); ++i)
00290         qVariantToIterator(it, list.at(i));
00291 }
00292 
00293 void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg)
00294 {
00295     Q_ASSERT(msg);
00296     DBusMessageIter it;
00297     dbus_message_iter_init_append(msg, &it);
00298     qListToIterator(&it, list);
00299 }
00300 

Generated on Wed Feb 27 10:13:38 2008 for D-BUS by  doxygen 1.4.6