2006-02-13 John (J5) Palmieri <johnp@redhat.com>
[platform/upstream/dbus.git] / qt / qdbusmarshall.cpp
1 /* qdbusmarshall.cpp
2  *
3  * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
4  *
5  * Licensed under the Academic Free License version 2.1
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "qdbusmarshall.h"
24 #include "qdbusvariant.h"
25
26 #include <QtCore/qdebug.h>
27 #include <QtCore/qvariant.h>
28 #include <QtCore/qlist.h>
29 #include <QtCore/qmap.h>
30 #include <QtCore/qstringlist.h>
31 #include <QtCore/qvarlengtharray.h>
32 #include <QtCore/qvector.h>
33
34 #include <dbus/dbus.h>
35
36 template <typename T>
37 inline T qIterGet(DBusMessageIter *it)
38 {
39     T t;
40     dbus_message_iter_get_basic(it, &t);
41     return t;
42 }
43
44 static QStringList qFetchStringList(DBusMessageIter *arrayIt)
45 {
46     QStringList list;
47
48     DBusMessageIter it;
49     dbus_message_iter_recurse(arrayIt, &it);
50
51     do {
52         list.append(QString::fromUtf8(qIterGet<char *>(&it)));
53     } while (dbus_message_iter_next(&it));
54
55     return list;
56 }
57
58 static QVariant qFetchParameter(DBusMessageIter *it)
59 {
60     switch (dbus_message_iter_get_arg_type(it)) {
61     case DBUS_TYPE_BYTE:
62         return qIterGet<unsigned char>(it);
63     case DBUS_TYPE_INT32:
64         return qIterGet<dbus_int32_t>(it);
65     case DBUS_TYPE_UINT32:
66         return qIterGet<dbus_uint32_t>(it);
67     case DBUS_TYPE_DOUBLE:
68         return qIterGet<double>(it);
69     case DBUS_TYPE_BOOLEAN:
70         return qIterGet<dbus_bool_t>(it);
71     case DBUS_TYPE_INT64:
72         return static_cast<qlonglong>(qIterGet<dbus_int64_t>(it));
73     case DBUS_TYPE_UINT64:
74         return static_cast<qulonglong>(qIterGet<dbus_uint64_t>(it));
75     case DBUS_TYPE_STRING:
76     case DBUS_TYPE_OBJECT_PATH:
77     case DBUS_TYPE_SIGNATURE:
78         return QString::fromUtf8(qIterGet<char *>(it));
79     case DBUS_TYPE_ARRAY: {
80         int arrayType = dbus_message_iter_get_element_type(it);
81         if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH) {
82             return qFetchStringList(it);
83         } else if (arrayType == DBUS_TYPE_DICT_ENTRY) {
84             // ### support other types of maps?
85             QMap<QString, QVariant> map;
86             DBusMessageIter sub;
87             dbus_message_iter_recurse(it, &sub);
88             if (!dbus_message_iter_has_next(&sub))
89                 return map;
90             do {
91                 DBusMessageIter itemIter;
92                 dbus_message_iter_recurse(&sub, &itemIter);
93                 Q_ASSERT(dbus_message_iter_has_next(&itemIter));
94                 QString key = qFetchParameter(&itemIter).toString();
95                 dbus_message_iter_next(&itemIter);
96                 map.insertMulti(key, qFetchParameter(&itemIter));
97             } while (dbus_message_iter_next(&sub));
98             return map;
99         } else {
100             QList<QVariant> list;
101             DBusMessageIter sub;
102             dbus_message_iter_recurse(it, &sub);
103             if (!dbus_message_iter_has_next(&sub))
104                 return list;
105             do {
106                 list.append(qFetchParameter(&sub));
107             } while (dbus_message_iter_next(&sub));
108             return list;
109         }
110         break; }
111     case DBUS_TYPE_VARIANT: {
112         QDBusVariant dvariant;
113         DBusMessageIter sub;
114         dbus_message_iter_recurse(it, &sub);
115         dvariant.signature = QString::fromUtf8(dbus_message_iter_get_signature(&sub));
116         dvariant.value = qFetchParameter(&sub);
117         return qVariantFromValue(dvariant);
118     }
119 #if 0
120     case DBUS_TYPE_DICT: {
121         QMap<QString, QVariant> map;
122         DBusMessageIter sub;
123         dbus_message
124         if (dbus_message_iter_init_dict_iterator(it, &dictIt)) {
125             do {
126                 map[QString::fromUtf8(dbus_message_iter_get_dict_key(&dictIt))] =
127                     qFetchParameter(&dictIt);
128             } while (dbus_message_iter_next(&dictIt));
129         }
130         return map;
131         break; }
132     case DBUS_TYPE_CUSTOM:
133         return qGetCustomValue(it);
134         break;
135 #endif
136     default:
137         qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it));
138         return QVariant();
139         break;
140     }
141 }
142
143 void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
144 {
145     Q_ASSERT(message);
146
147     DBusMessageIter it;
148     if (!dbus_message_iter_init(message, &it))
149         return;
150
151     do {
152         list.append(qFetchParameter(&it));
153     } while (dbus_message_iter_next(&it));
154 }
155
156 #define DBUS_APPEND(type,dtype,var) \
157 type dtype##v=(var); \
158 dbus_message_append_args(msg, dtype, &dtype##v, DBUS_TYPE_INVALID)
159 #define DBUS_APPEND_LIST(type,dtype,var,size) \
160 type dtype##v=(var); \
161 dbus_message_append_args(msg, DBUS_TYPE_ARRAY, dtype, &dtype##v, size, DBUS_TYPE_INVALID)
162
163
164 static void qAppendToMessage(DBusMessageIter *it, const QString &str)
165 {
166     QByteArray ba = str.toUtf8();
167     const char *cdata = ba.constData();
168     dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &cdata);
169 }
170
171 static QVariant::Type qVariantListType(const QList<QVariant> &list)
172 {
173     // TODO - catch lists that have a list as first parameter
174     QVariant::Type tp = list.value(0).type();
175     if (tp < QVariant::Int || tp > QVariant::Double)
176         return QVariant::Invalid;
177
178     for (int i = 1; i < list.count(); ++i) {
179         const QVariant &var = list.at(i);
180         if (var.type() != tp
181                && (var.type() != QVariant::List || qVariantListType(var.toList()) != tp))
182             return QVariant::Invalid;
183     }
184     return tp;
185 }
186
187 static const char *qDBusListType(const QList<QVariant> &list)
188 {
189     static const char *DBusArgs[] = { 0, 0, DBUS_TYPE_INT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING,
190             DBUS_TYPE_INT64_AS_STRING, DBUS_TYPE_UINT64_AS_STRING, DBUS_TYPE_DOUBLE_AS_STRING };
191
192     return DBusArgs[qVariantListType(list)];
193 }
194
195 static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list);
196
197 static void qVariantToIterator(DBusMessageIter *it, const QVariant &var)
198 {
199     static const int Variant2DBus[] = { DBUS_TYPE_INVALID,
200         DBUS_TYPE_BOOLEAN, DBUS_TYPE_INT32, DBUS_TYPE_UINT32,
201         DBUS_TYPE_INT64, DBUS_TYPE_UINT64, DBUS_TYPE_DOUBLE };
202
203     // these really are static asserts
204     Q_ASSERT(QVariant::Invalid == 0);
205     Q_ASSERT(QVariant::Int == 2);
206     Q_ASSERT(QVariant::Double == 6);
207
208     switch (var.type()) {
209     case QVariant::Int:
210     case QVariant::UInt:
211     case QVariant::LongLong:
212     case QVariant::ULongLong:
213     case QVariant::Double:
214         dbus_message_iter_append_basic(it, Variant2DBus[var.type()],
215                 var.constData());
216         break;
217     case QVariant::String:
218         qAppendToMessage(it, var.toString());
219         break;
220     case QVariant::StringList: {
221         const QStringList list = var.toStringList();
222         DBusMessageIter sub;
223         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
224                                          DBUS_TYPE_STRING_AS_STRING, &sub);
225         for (int s = 0; s < list.count(); ++s)
226             qAppendToMessage(&sub, list.at(s));
227         dbus_message_iter_close_container(it, &sub);
228         break;
229     }
230     case QVariant::List: {
231         const QList<QVariant> &list = var.toList();
232         const char *listType = qDBusListType(list);
233         if (!listType) {
234             qWarning("Don't know how to marshall list.");
235             break;
236         }
237         DBusMessageIter sub;
238         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, listType, &sub);
239         qListToIterator(&sub, list);
240         dbus_message_iter_close_container(it, &sub);
241         break;
242     }
243     case QVariant::Map: {
244         // ### TODO - marshall more than qstring/qstring maps
245         const QMap<QString, QVariant> &map = var.toMap();
246         DBusMessageIter sub;
247         QVarLengthArray<char, 16> sig;
248         sig.append(DBUS_DICT_ENTRY_BEGIN_CHAR);
249         sig.append(DBUS_TYPE_STRING);
250         sig.append(DBUS_TYPE_STRING);
251         sig.append(DBUS_DICT_ENTRY_END_CHAR);
252         sig.append('\0');
253         qDebug() << QString::fromAscii(sig.constData());
254         dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, sig.constData(), &sub);
255         for (QMap<QString, QVariant>::const_iterator mit = map.constBegin();
256              mit != map.constEnd(); ++mit) {
257             DBusMessageIter itemIterator;
258             dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator);
259             qAppendToMessage(&itemIterator, mit.key());
260             qAppendToMessage(&itemIterator, mit.value().toString());
261             dbus_message_iter_close_container(&sub, &itemIterator);
262         }
263         dbus_message_iter_close_container(it, &sub);
264         break;
265     }
266     case QVariant::UserType: {
267         if (var.userType() == QMetaTypeId<QDBusVariant>::qt_metatype_id()) {
268             DBusMessageIter sub;
269             QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var);
270             dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT,
271                     dvariant.signature.toUtf8().constData(), &sub);
272             qVariantToIterator(&sub, dvariant.value);
273             dbus_message_iter_close_container(it, &sub);
274             break;
275         }
276     }
277     // fall through
278     default:
279         qWarning("Don't know how to handle type %s", var.typeName());
280         break;
281     }
282 }
283
284 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
285 {
286     if (list.isEmpty())
287         return;
288
289     for (int i = 0; i < list.count(); ++i)
290         qVariantToIterator(it, list.at(i));
291 }
292
293 void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg)
294 {
295     Q_ASSERT(msg);
296     DBusMessageIter it;
297     dbus_message_iter_init_append(msg, &it);
298     qListToIterator(&it, list);
299 }
300