* dbus/dbus-sysdeps.c: Make tcp socket connection error somewhat more
[platform/upstream/dbus.git] / qt / src / qdbusabstractadaptor.cpp
1 /* -*- mode: C++ -*-
2  *
3  * Copyright (C) 2006 Trolltech AS. All rights reserved.
4  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "qdbusabstractadaptor.h"
25
26 #include <QtCore/qmetaobject.h>
27 #include <QtCore/qtimer.h>
28
29 #include "qdbusconnection.h"
30
31 #include "qdbusconnection_p.h"  // for qDBusParametersForMethod
32 #include "qdbusabstractadaptor_p.h"
33
34 struct QDBusAdaptorInit
35 {
36     QSignalSpyCallbackSet callbacks;
37     QDBusAdaptorInit()
38     {
39         extern void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set);
40         callbacks.signal_begin_callback = QDBusAdaptorConnector::signalBeginCallback;
41         callbacks.signal_end_callback = QDBusAdaptorConnector::signalEndCallback;
42         callbacks.slot_begin_callback = 0;
43         callbacks.slot_end_callback = 0;
44         qt_register_signal_spy_callbacks(callbacks);
45         
46         //QDBusAdaptorConnector::id = QObject::registerUserData();
47     }
48 };
49
50 Q_GLOBAL_STATIC(QDBusAdaptorInit, qAdaptorInit)
51
52 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
53 {
54     (void)qAdaptorInit();
55
56     if (!obj)
57         return 0;
58     QDBusAdaptorConnector *connector = qFindChild<QDBusAdaptorConnector *>(obj);
59     if (connector)
60         connector->polish();
61     return connector;
62 }
63
64 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
65 {
66     return qDBusFindAdaptorConnector(adaptor->parent());
67 }
68
69 QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
70 {
71     (void)qAdaptorInit();
72
73     QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
74     if (connector)
75         return connector;
76     return new QDBusAdaptorConnector(obj);
77 }
78
79 QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
80 {
81     return adaptor->d->xml;
82 }
83
84 void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
85                                                        const QString &xml)
86 {
87     adaptor->d->xml = xml;
88 }
89
90 /*!
91     \page usingannotations.html
92     \title Using annotations in adaptors
93
94     It is currently not possible to specify arbitrary annotations in adaptors.
95 */
96
97 /*!
98     \class QDBusAbstractAdaptor
99     \brief Abstract adaptor for D-Bus adaptor classes.
100
101     The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
102     interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
103     classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
104     with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
105     light-weight wrappers, mostly just relaying calls into the real object (see object()) and the
106     signals from it.
107
108     Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
109     using the Q_CLASSINFO macro in the class definition.
110
111     QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
112     determine what signals, methods and properties to export to the bus. Any signal emitted by
113     QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
114     connections the object is registered on.
115
116     Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
117     and must not be deleted by the user (they will be deleted automatically when the object they are
118     connected to is also deleted).
119
120     \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
121 */
122
123 /*!
124     Constructs a QDBusAbstractAdaptor with \a parent as the object we refer to.
125 */
126 QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent)
127     : QObject(parent), d(new QDBusAbstractAdaptorPrivate)
128 {
129     QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(parent);
130
131     connector->waitingForPolish = true;
132     QTimer::singleShot(0, connector, SLOT(polish()));
133 }
134
135 /*!
136     Destroys the adaptor.
137
138     \warning Adaptors are destroyed automatically when the real object they refer to is
139              destroyed. Do not delete the adaptors yourself.
140 */
141 QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
142 {
143     delete d;
144 }
145
146 /*!
147     Returns the QObject that we're the adaptor for. This is the same object that was passed as an
148     argument to the QDBusAbstractAdaptor constructor.
149 */
150 QObject* QDBusAbstractAdaptor::object() const
151 {
152     return parent();
153 }
154
155 /*!
156     Toggles automatic signal relaying from the real object (see object()).
157
158     Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
159     that have the exact same method signatue in both classes.
160
161     If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
162 */
163 void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
164 {
165     const QMetaObject *us = metaObject();
166     const QMetaObject *them = parent()->metaObject();
167     for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
168         QMetaMethod mm = us->method(idx);
169
170         if (mm.methodType() != QMetaMethod::Signal)
171             continue;
172         
173         // try to connect/disconnect to a signal on the parent that has the same method signature
174         QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
175         if (them->indexOfSignal(sig) == -1)
176             continue;
177         sig.prepend(QSIGNAL_CODE + '0');
178         parent()->disconnect(sig, this, sig);
179         if (enable)
180             connect(parent(), sig, sig);
181     }
182 }
183
184 QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *parent)
185     : QObject(parent), waitingForPolish(false), lastSignalIdx(0), argv(0)
186 {
187 }
188
189 QDBusAdaptorConnector::~QDBusAdaptorConnector()
190 {
191 }
192
193 void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
194 {
195     // find the interface name
196     const QMetaObject *mo = adaptor->metaObject();
197     while (mo != &QDBusAbstractAdaptor::staticMetaObject) {
198         int ciend = mo->classInfoCount();
199         for (int i = mo->classInfoOffset(); i < ciend; ++i) {
200             QMetaClassInfo mci = mo->classInfo(i);
201             if (strcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) {
202                 // find out if this interface exists first
203                 QString interface = QString::fromUtf8(mci.value());
204                 AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), interface);
205                 if (it != adaptors.end() && it->interface == interface) {
206                     // exists. Replace it (though it's probably the same)
207                     it->adaptor = adaptor;
208                     it->metaObject = mo;
209                 } else {
210                     // create a new one
211                     AdaptorData entry;
212                     entry.interface = interface;
213                     entry.adaptor = adaptor;
214                     entry.metaObject = mo;
215                     adaptors << entry;
216                 }
217             }
218         }
219
220         mo = mo->superClass();
221     }
222         
223     // connect the adaptor's signals to our relaySlot slot
224     mo = adaptor->metaObject();
225     for (int i = QDBusAbstractAdaptor::staticMetaObject.methodCount();
226          i < mo->methodCount(); ++i) {
227         QMetaMethod mm = mo->method(i);
228
229         if (mm.methodType() != QMetaMethod::Signal)
230             continue;
231
232         QByteArray sig = mm.signature();
233         sig.prepend(QSIGNAL_CODE + '0');
234         disconnect(adaptor, sig, this, SLOT(relaySlot()));
235         connect(adaptor, sig, this, SLOT(relaySlot()));
236     }
237 }
238
239 void QDBusAdaptorConnector::polish()
240 {
241     if (!waitingForPolish)
242         return;                 // avoid working multiple times if multiple adaptors were added
243
244     waitingForPolish = false;
245     const QObjectList &objs = parent()->children();
246     foreach (QObject *obj, objs) {
247         QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(obj);
248         if (adaptor)
249             addAdaptor(adaptor);
250     }
251
252     // sort the adaptor list
253     qSort(adaptors);
254 }
255
256 void QDBusAdaptorConnector::relaySlot()
257 {
258     relay(sender());
259 }
260
261 void QDBusAdaptorConnector::relay(QObject *sender)
262 {
263     // we're being called because there is a signal being emitted that we must relay
264     Q_ASSERT(lastSignalIdx);
265     Q_ASSERT(argv);
266     Q_ASSERT(senderMetaObject);
267
268     if (senderMetaObject != sender->metaObject()) {
269         qWarning("Inconsistency detected: QDBusAdaptorConnector::relay got called with unexpected sender object!");
270     } else {
271         QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
272         QObject *object = static_cast<QDBusAbstractAdaptor *>(sender)->parent();
273
274         // break down the parameter list
275         QList<int> types;
276         int inputCount = qDBusParametersForMethod(mm, types);
277         if (inputCount == -1)
278             // invalid signal signature
279             // qDBusParametersForMethod has already complained
280             return;
281         if (inputCount + 1 != types.count() ||
282             types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
283             // invalid signal signature
284             // qDBusParametersForMethod has not yet complained about this one
285             qWarning("Cannot relay signal %s::%s", senderMetaObject->className(), mm.signature());
286             return;
287         }
288
289         QByteArray signature = QMetaObject::normalizedSignature(mm.signature());
290         signature.truncate(signature.indexOf('(')); // remove parameter decoration
291
292         QVariantList args;
293         for (int i = 1; i < types.count(); ++i)
294             args << QVariant(types.at(i), argv[i]);
295
296         // find all the interfaces this signal belongs to
297         for (const QMetaObject *mo = senderMetaObject; mo != &QDBusAbstractAdaptor::staticMetaObject;
298              mo = mo->superClass()) {
299             if (lastSignalIdx < mo->methodOffset())
300                 break;
301
302             for (int i = mo->classInfoOffset(); i < mo->classInfoCount(); ++i) {
303                 QMetaClassInfo mci = mo->classInfo(i);
304                 if (qstrcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value())
305                     // now emit the signal with all the information
306                     emit relaySignal(object, mci.value(), signature.constData(), args);
307             }
308         }
309     }
310 }
311
312 void QDBusAdaptorConnector::signalBeginCallback(QObject *caller, int method_index, void **argv)
313 {
314     QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
315     if (adaptor) {
316         QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
317         data->lastSignalIdx = method_index;
318         data->argv = argv;
319         data->senderMetaObject = caller->metaObject();
320         data->polish();         // make sure it's polished
321     }
322 }
323
324 void QDBusAdaptorConnector::signalEndCallback(QObject *caller, int)
325 {
326     QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
327     if (adaptor) {
328         QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
329         data->lastSignalIdx = 0;
330         data->argv = 0;
331         data->senderMetaObject = 0;
332     }
333 }
334
335 #include "qdbusabstractadaptor.moc"
336 #include "qdbusabstractadaptor_p.moc"