1 /*
2 * Copyright (C) 2008 Justin Karneges
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 */
19
20 #include "objectsession.h"
21
22 #include <stdlib.h>
23 #include <QList>
24 #include <QByteArray>
25 #include <QMetaObject>
26 #include <QMetaType>
27 #include <QTimer>
28
29 namespace XMPP {
30
31 class ObjectSessionWatcherPrivate
32 {
33 public:
34 ObjectSession *sess;
35 };
36
37 class ObjectSessionPrivate : public QObject
38 {
39 Q_OBJECT
40
41 public:
42 ObjectSession *q;
43
44 class MethodCall
45 {
46 public:
47 QObject *obj;
48 QByteArray method;
49 class Argument
50 {
51 public:
52 int type;
53 void *data;
54 };
55 QList<Argument> args;
56
MethodCall(QObject * _obj,const char * _method)57 MethodCall(QObject *_obj, const char *_method) :
58 obj(_obj),
59 method(_method)
60 {
61 }
62
~MethodCall()63 ~MethodCall()
64 {
65 clearArgs();
66 }
67
clearArgs()68 void clearArgs()
69 {
70 for(int n = 0; n < args.count(); ++n)
71 QMetaType::destroy(args[n].type, args[n].data);
72 args.clear();
73 }
74
setArgs(QGenericArgument val0=QGenericArgument (),QGenericArgument val1=QGenericArgument (),QGenericArgument val2=QGenericArgument (),QGenericArgument val3=QGenericArgument (),QGenericArgument val4=QGenericArgument (),QGenericArgument val5=QGenericArgument (),QGenericArgument val6=QGenericArgument (),QGenericArgument val7=QGenericArgument (),QGenericArgument val8=QGenericArgument (),QGenericArgument val9=QGenericArgument ())75 bool setArgs(QGenericArgument val0 = QGenericArgument(),
76 QGenericArgument val1 = QGenericArgument(),
77 QGenericArgument val2 = QGenericArgument(),
78 QGenericArgument val3 = QGenericArgument(),
79 QGenericArgument val4 = QGenericArgument(),
80 QGenericArgument val5 = QGenericArgument(),
81 QGenericArgument val6 = QGenericArgument(),
82 QGenericArgument val7 = QGenericArgument(),
83 QGenericArgument val8 = QGenericArgument(),
84 QGenericArgument val9 = QGenericArgument())
85 {
86 const char *arg_name[] =
87 {
88 val0.name(), val1.name(), val2.name(),
89 val3.name(), val4.name(), val5.name(),
90 val6.name(), val7.name(), val8.name(),
91 val9.name()
92 };
93
94 void *arg_data[] =
95 {
96 val0.data(), val1.data(), val2.data(),
97 val3.data(), val4.data(), val5.data(),
98 val6.data(), val7.data(), val8.data(),
99 val9.data()
100 };
101
102 clearArgs();
103
104 for(int n = 0; n < 10; ++n)
105 {
106 if(arg_name[n] == 0)
107 break;
108
109 Argument arg;
110 arg.type = QMetaType::type(arg_name[n]);
111 if(!arg.type)
112 {
113 clearArgs();
114 return false;
115 }
116
117 #if QT_VERSION < 0x050000
118 arg.data = QMetaType::construct(arg.type, arg_data[n]);
119 #else
120 arg.data = QMetaType::create(arg.type, arg_data[n]);
121 #endif
122 args += arg;
123 }
124
125 return true;
126 }
127 };
128
129 QList<MethodCall*> pendingCalls;
130 QTimer *callTrigger;
131 bool paused;
132 QList<ObjectSessionWatcherPrivate*> watchers;
133
ObjectSessionPrivate(ObjectSession * _q)134 ObjectSessionPrivate(ObjectSession *_q) :
135 QObject(_q),
136 q(_q),
137 paused(false)
138 {
139 callTrigger = new QTimer(this);
140 connect(callTrigger, SIGNAL(timeout()), SLOT(doCall()));
141 callTrigger->setSingleShot(true);
142 }
143
~ObjectSessionPrivate()144 ~ObjectSessionPrivate()
145 {
146 invalidateWatchers();
147
148 callTrigger->disconnect(this);
149 callTrigger->setParent(0);
150 callTrigger->deleteLater();
151 qDeleteAll(pendingCalls);
152 pendingCalls.clear();
153 }
154
addPendingCall(MethodCall * call)155 void addPendingCall(MethodCall *call)
156 {
157 pendingCalls += call;
158 if(!paused && !callTrigger->isActive())
159 callTrigger->start();
160 }
161
havePendingCall(QObject * obj,const char * method) const162 bool havePendingCall(QObject *obj, const char *method) const
163 {
164 foreach(const MethodCall *call, pendingCalls)
165 {
166 if(call->obj == obj && qstrcmp(call->method.data(), method) == 0)
167 return true;
168 }
169 return false;
170 }
171
invalidateWatchers()172 void invalidateWatchers()
173 {
174 for(int n = 0; n < watchers.count(); ++n)
175 watchers[n]->sess = 0;
176 watchers.clear();
177 }
178
179 private slots:
doCall()180 void doCall()
181 {
182 MethodCall *call = pendingCalls.takeFirst();
183 if(!pendingCalls.isEmpty())
184 callTrigger->start();
185
186 Q_ASSERT(call->args.count() <= 10);
187
188 QGenericArgument arg[10];
189 for(int n = 0; n < call->args.count(); ++n)
190 arg[n] = QGenericArgument(QMetaType::typeName(call->args[n].type), call->args[n].data);
191
192 bool ok;
193 ok = QMetaObject::invokeMethod(call->obj, call->method.data(),
194 Qt::DirectConnection,
195 arg[0], arg[1], arg[2], arg[3], arg[4],
196 arg[5], arg[6], arg[7], arg[8], arg[9]);
197 Q_ASSERT(ok);
198 if(!ok)
199 abort();
200
201 delete call;
202 }
203 };
204
ObjectSessionWatcher(ObjectSession * sess)205 ObjectSessionWatcher::ObjectSessionWatcher(ObjectSession *sess)
206 {
207 d = new ObjectSessionWatcherPrivate;
208 d->sess = sess;
209 if(d->sess)
210 d->sess->d->watchers += d;
211 }
212
~ObjectSessionWatcher()213 ObjectSessionWatcher::~ObjectSessionWatcher()
214 {
215 if(d->sess)
216 d->sess->d->watchers.removeAll(d);
217 delete d;
218 }
219
isValid() const220 bool ObjectSessionWatcher::isValid() const
221 {
222 if(d->sess)
223 return true;
224 else
225 return false;
226 }
227
228
ObjectSession(QObject * parent)229 ObjectSession::ObjectSession(QObject *parent) :
230 QObject(parent)
231 {
232 d = new ObjectSessionPrivate(this);
233 }
234
~ObjectSession()235 ObjectSession::~ObjectSession()
236 {
237 delete d;
238 }
239
reset()240 void ObjectSession::reset()
241 {
242 d->invalidateWatchers();
243 if(d->callTrigger->isActive())
244 d->callTrigger->stop();
245 qDeleteAll(d->pendingCalls);
246 d->pendingCalls.clear();
247 }
248
isDeferred(QObject * obj,const char * method)249 bool ObjectSession::isDeferred(QObject *obj, const char *method)
250 {
251 return d->havePendingCall(obj, method);
252 }
253
defer(QObject * obj,const char * method,QGenericArgument val0,QGenericArgument val1,QGenericArgument val2,QGenericArgument val3,QGenericArgument val4,QGenericArgument val5,QGenericArgument val6,QGenericArgument val7,QGenericArgument val8,QGenericArgument val9)254 void ObjectSession::defer(QObject *obj, const char *method,
255 QGenericArgument val0,
256 QGenericArgument val1,
257 QGenericArgument val2,
258 QGenericArgument val3,
259 QGenericArgument val4,
260 QGenericArgument val5,
261 QGenericArgument val6,
262 QGenericArgument val7,
263 QGenericArgument val8,
264 QGenericArgument val9)
265 {
266 ObjectSessionPrivate::MethodCall *call = new ObjectSessionPrivate::MethodCall(obj, method);
267 call->setArgs(val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
268 d->addPendingCall(call);
269 }
270
deferExclusive(QObject * obj,const char * method,QGenericArgument val0,QGenericArgument val1,QGenericArgument val2,QGenericArgument val3,QGenericArgument val4,QGenericArgument val5,QGenericArgument val6,QGenericArgument val7,QGenericArgument val8,QGenericArgument val9)271 void ObjectSession::deferExclusive(QObject *obj, const char *method,
272 QGenericArgument val0,
273 QGenericArgument val1,
274 QGenericArgument val2,
275 QGenericArgument val3,
276 QGenericArgument val4,
277 QGenericArgument val5,
278 QGenericArgument val6,
279 QGenericArgument val7,
280 QGenericArgument val8,
281 QGenericArgument val9)
282 {
283 if(d->havePendingCall(obj, method))
284 return;
285
286 ObjectSessionPrivate::MethodCall *call = new ObjectSessionPrivate::MethodCall(obj, method);
287 call->setArgs(val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
288 d->addPendingCall(call);
289 }
290
pause()291 void ObjectSession::pause()
292 {
293 Q_ASSERT(!d->paused);
294
295 if(d->callTrigger->isActive())
296 d->callTrigger->stop();
297 d->paused = true;
298 }
299
resume()300 void ObjectSession::resume()
301 {
302 Q_ASSERT(d->paused);
303
304 d->paused = false;
305 if(!d->pendingCalls.isEmpty())
306 d->callTrigger->start();
307 }
308
309 }
310
311 #include "objectsession.moc"
312