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