limiting package size for the SignalProxy to 4MB
[quassel.git] / src / common / signalproxy.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "signalproxy.h"
22
23 #include <QObject>
24 #include <QIODevice>
25 #include <QAbstractSocket>
26 #include <QHostAddress>
27 #include <QHash>
28 #include <QMultiHash>
29 #include <QList>
30 #include <QSet>
31 #include <QDebug>
32 #include <QMetaMethod>
33 #include <QMetaProperty>
34 #include <QRegExp>
35 #include <QThread>
36
37 #include "syncableobject.h"
38 #include "util.h"
39
40 class SignalRelay: public QObject {
41
42 /* Q_OBJECT is not necessary or even allowed, because we implement
43    qt_metacall ourselves (and don't use any other features of the meta
44    object system)
45 */
46 public:
47   SignalRelay(SignalProxy* parent, QObject* source);
48   int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
49
50   void attachSignal(int methodId, const QByteArray &func);
51
52   void setSynchronize(bool);
53   bool synchronize() const;
54   
55   int sigCount() const;
56   
57 private:
58   bool isSyncMethod(int i);
59   
60   SignalProxy* proxy;
61   QObject* caller;
62   QMultiHash<int, QByteArray> sigNames;
63   bool _sync;
64 };
65
66 SignalRelay::SignalRelay(SignalProxy* parent, QObject* source)
67   : QObject(parent),
68     proxy(parent),
69     caller(source),
70     _sync(false)
71 {
72   QObject::connect(source, SIGNAL(destroyed()), parent, SLOT(detachSender()));
73 }
74
75 int SignalRelay::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
76   _id = QObject::qt_metacall(_c, _id, _a);
77   if(_id < 0)
78     return _id;
79   if(_c == QMetaObject::InvokeMetaMethod) {
80     if(sigNames.contains(_id) || synchronize()) {
81       const QList<int> &argTypes = proxy->argTypes(caller, _id);
82       QVariantList params;
83       int n = argTypes.size();
84       for(int i=0; i<n; i++) {
85         if(argTypes[i] == 0) {
86           qWarning() << "SignalRelay::qt_metacall(): received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(caller->metaObject()->className()).arg(caller->metaObject()->method(_id).signature());
87           qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
88           return _id;
89         }
90         params.append(QVariant(argTypes[i], _a[i+1]));
91       }
92       QMultiHash<int, QByteArray>::const_iterator funcIter = sigNames.constFind(_id);
93       while(funcIter != sigNames.constEnd() && funcIter.key() == _id) {
94         proxy->dispatchSignal(SignalProxy::RpcCall, QVariantList() << funcIter.value() << params);
95         funcIter++;
96       }
97       
98       // dispatch Sync Signal if necessary
99       QByteArray signature(caller->metaObject()->method(_id).signature());
100       SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
101       if(synchronize() && proxy->syncMap(syncObject).contains(signature)) {
102          //qDebug() << "__SYNC__ >>>"
103          //      << caller->metaObject()->className()
104          //      << caller->objectName()
105          //      << signature
106          //      << params;
107         // params.prepend(QVariant(_id));
108         params.prepend(signature);
109         params.prepend(syncObject->objectName());
110         params.prepend(syncObject->syncMetaObject()->className());
111         proxy->dispatchSignal(SignalProxy::Sync, params);
112       }
113     }
114     _id -= QObject::staticMetaObject.methodCount();
115   }
116   return _id;
117 }
118
119 void SignalRelay::setSynchronize(bool sync) {
120   SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
121   if(!syncObject)
122     return;
123
124   const QMetaObject *meta = syncObject->syncMetaObject();
125   if(!_sync && sync) {
126     // enable Sync
127     for(int i = 0; i < meta->methodCount(); i++ ) {
128       if(isSyncMethod(i))
129         QMetaObject::connect(caller, i, this, QObject::staticMetaObject.methodCount() + i);
130     }
131   } else if (_sync && !sync) {
132     // disable Sync
133     for(int i = 0; i < meta->methodCount(); i++ ) {
134       if(isSyncMethod(i))
135         QMetaObject::disconnect(caller, i, this, QObject::staticMetaObject.methodCount() + i);
136     }
137   }
138   _sync = sync;
139 }
140
141 bool SignalRelay::isSyncMethod(int i) {
142   SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
143   if(!syncObject)
144     return false;
145
146   QByteArray signature = syncObject->syncMetaObject()->method(i).signature();
147   if(!proxy->syncMap(syncObject).contains(signature))
148     return false;
149   
150   if(proxy->proxyMode() == SignalProxy::Server && !signature.contains("Requested"))
151     return true;
152
153   if(proxy->proxyMode() == SignalProxy::Client && signature.contains("Requested"))
154     return true;
155
156   return false;
157 }
158
159 bool SignalRelay::synchronize() const {
160   return _sync;
161 }
162
163 int SignalRelay::sigCount() const {
164   // only for debuging purpose
165   return sigNames.count();
166 }
167
168 void SignalRelay::attachSignal(int methodId, const QByteArray &func) {
169   // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
170   // all connected methodIds are offset by the standard methodCount of QObject
171   if(!sigNames.contains(methodId))
172     QMetaObject::connect(caller, methodId, this, QObject::staticMetaObject.methodCount() + methodId);
173
174   QByteArray fn;
175   if(!func.isEmpty()) {
176     fn = QMetaObject::normalizedSignature(func);
177   } else {
178     fn = QByteArray("2") + caller->metaObject()->method(methodId).signature();
179   }
180   sigNames.insert(methodId, fn);
181 }
182 // ====================
183 // END SIGNALRELAY
184 // ====================
185
186
187 // ====================
188 //  SignalProxy
189 // ====================
190 SignalProxy::SignalProxy(QObject* parent)
191   : QObject(parent)
192 {
193   setProxyMode(Client);
194 }
195
196 SignalProxy::SignalProxy(ProxyMode mode, QObject* parent)
197   : QObject(parent)
198 {
199   setProxyMode(mode);
200 }
201
202 SignalProxy::SignalProxy(ProxyMode mode, QIODevice* device, QObject* parent)
203   : QObject(parent)
204 {
205   setProxyMode(mode);
206   addPeer(device);
207
208
209 SignalProxy::~SignalProxy() {
210   QList<QObject*> senders = _relayHash.keys();
211   foreach(QObject* sender, senders)
212     detachObject(sender);
213
214   // close peer connections
215   foreach(QIODevice *device, _peerByteCount.keys()) {
216     device->close();
217     delete device;
218   }
219 }
220
221 void SignalProxy::setProxyMode(ProxyMode mode) {
222   foreach(QIODevice* peer, _peerByteCount.keys()) {
223     if(peer->isOpen()) {
224       qWarning() << "SignalProxy: Cannot change proxy mode while connected";
225       return;
226     }
227   }
228   _proxyMode = mode;
229   if(mode == Server)
230     initServer();
231   else
232     initClient();
233 }
234
235 SignalProxy::ProxyMode SignalProxy::proxyMode() const {
236   return _proxyMode;
237 }
238
239 void SignalProxy::initServer() {
240   disconnect(&_heartBeatTimer, 0, this, 0);
241   _heartBeatTimer.stop();
242 }
243
244 void SignalProxy::initClient() {
245   attachSlot("__objectRenamed__", this, SLOT(objectRenamed(QByteArray, QString, QString)));
246   connect(&_heartBeatTimer, SIGNAL(timeout()),
247           this, SLOT(sendHeartBeat()));
248   _heartBeatTimer.start(60 * 1000); // msecs: one beep per minute
249 }
250
251 bool SignalProxy::addPeer(QIODevice* iodev) {
252   if(!iodev)
253     return false;
254   
255   if(_peerByteCount.contains(iodev))
256     return true;
257
258   if(proxyMode() == Client && !_peerByteCount.isEmpty()) {
259     qWarning("SignalProxy: only one peer allowed in client mode!");
260     return false;
261   }
262
263   if(!iodev->isOpen())
264     qWarning("SignalProxy::the device you passed is not open!");
265
266   connect(iodev, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
267   connect(iodev, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
268
269   QAbstractSocket* sock  = qobject_cast<QAbstractSocket*>(iodev);
270   if(sock) {
271     connect(sock, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
272   }
273
274   _peerByteCount[iodev] = 0;
275
276   if(_peerByteCount.count() == 1)
277     emit connected();
278
279   return true;
280 }
281
282 void SignalProxy::removePeer(QIODevice* iodev) {
283   if(_peerByteCount.isEmpty()) {
284     qWarning() << "SignalProxy::removePeer(): No peers in use!";
285     return;
286   }
287
288   if(proxyMode() == Server && !iodev) {
289     // disconnect all
290     QList<QIODevice *> peers = _peerByteCount.keys();
291     foreach(QIODevice *peer, peers)
292       removePeer(peer);
293   }
294
295   if(proxyMode() != Server && !iodev)
296     iodev = _peerByteCount.keys().first();
297
298   Q_ASSERT(iodev);
299
300   if(!_peerByteCount.contains(iodev)) {
301     qWarning() << "SignalProxy: unknown QIODevice" << iodev;
302     return;
303   }
304
305   _peerByteCount.remove(iodev);
306
307   disconnect(iodev, 0, this, 0);
308   emit peerRemoved(iodev);
309
310   if(_peerByteCount.isEmpty())
311     emit disconnected();
312 }
313
314 void SignalProxy::removePeerBySender() {
315   // OK we're brutal here... but since it's a private slot we know what we've got connected to it...
316   // this Slot is not triggered by destroyed, so the object is still alive and can be used!
317   QIODevice *ioDev = (QIODevice *)(sender());
318   removePeer(ioDev);
319 }
320
321 void SignalProxy::objectRenamed(const QString &newname, const QString &oldname) {
322   SyncableObject *syncObject = qobject_cast<SyncableObject *>(sender());
323   const QMetaObject *meta = syncObject->metaObject();
324   const QByteArray className(meta->className());
325   objectRenamed(className, newname, oldname);
326
327   if(proxyMode() == Client)
328     return;
329   
330   QVariantList params;
331   params << "__objectRenamed__" << className << newname << oldname;
332   dispatchSignal(RpcCall, params);
333 }
334
335 void SignalProxy::objectRenamed(const QByteArray &classname, const QString &newname, const QString &oldname) {
336   if(_syncSlave.contains(classname) && _syncSlave[classname].contains(oldname) && oldname != newname) {
337     SyncableObject *obj = _syncSlave[classname][newname] = _syncSlave[classname].take(oldname);
338     requestInit(obj);
339   }
340 }
341
342 void SignalProxy::setArgTypes(QObject* obj, int methodId) {
343   const QMetaObject *meta = metaObject(obj);
344   QList<QByteArray> p = meta->method(methodId).parameterTypes();
345   QList<int> argTypes;
346   int ct = p.count();
347   for(int i=0; i<ct; i++)
348     argTypes.append(QMetaType::type(p.value(i)));
349
350   Q_ASSERT(!_classInfo[meta]->argTypes.contains(methodId));
351   _classInfo[meta]->argTypes[methodId] = argTypes;
352 }
353
354 const QList<int> &SignalProxy::argTypes(QObject *obj, int methodId) {
355   const QMetaObject *meta = metaObject(obj);
356   Q_ASSERT(_classInfo.contains(meta));
357   if(!_classInfo[meta]->argTypes.contains(methodId))
358     setArgTypes(obj, methodId);
359   return _classInfo[meta]->argTypes[methodId];
360 }
361
362 void SignalProxy::setReturnType(QObject *obj, int methodId) {
363   const QMetaObject *meta = metaObject(obj);
364   int returnType = QMetaType::type(meta->method(methodId).typeName());
365
366   Q_ASSERT(!_classInfo[meta]->returnType.contains(methodId));
367   _classInfo[meta]->returnType[methodId] = returnType;
368 }
369
370 const int &SignalProxy::returnType(QObject *obj, int methodId) {
371   const QMetaObject *meta = metaObject(obj);
372   Q_ASSERT(_classInfo.contains(meta));
373   if(!_classInfo[meta]->returnType.contains(methodId))
374     setReturnType(obj, methodId);
375   return _classInfo[meta]->returnType[methodId];
376 }
377
378 void SignalProxy::setMinArgCount(QObject *obj, int methodId) {
379   const QMetaObject *meta = metaObject(obj);
380   QString signature(meta->method(methodId).signature());
381   int minCount = meta->method(methodId).parameterTypes().count() - signature.count("=");
382   Q_ASSERT(!_classInfo[meta]->minArgCount.contains(methodId));
383   _classInfo[meta]->minArgCount[methodId] = minCount;
384 }
385
386 const int &SignalProxy::minArgCount(QObject *obj, int methodId) {
387   const QMetaObject *meta = metaObject(obj);
388   Q_ASSERT(_classInfo.contains(meta));
389   if(!_classInfo[meta]->minArgCount.contains(methodId))
390     setMinArgCount(obj, methodId);
391   return _classInfo[meta]->minArgCount[methodId];
392 }
393
394 void SignalProxy::setMethodName(QObject *obj, int methodId) {
395   const QMetaObject *meta = metaObject(obj);
396   QByteArray method(::methodName(meta->method(methodId)));
397   Q_ASSERT(!_classInfo[meta]->methodNames.contains(methodId));
398   _classInfo[meta]->methodNames[methodId] = method;
399 }
400
401 const QByteArray &SignalProxy::methodName(QObject *obj, int methodId) {
402   const QMetaObject *meta = metaObject(obj);
403   Q_ASSERT(_classInfo.contains(meta));
404   if(!_classInfo[meta]->methodNames.contains(methodId))
405     setMethodName(obj, methodId);
406   return _classInfo[meta]->methodNames[methodId];
407 }
408
409
410 void SignalProxy::setSyncMap(SyncableObject *obj) {
411   const QMetaObject *meta = obj->syncMetaObject();
412   QHash<QByteArray, int> syncMap;
413   
414   QList<int> slotIndexes;
415   for(int i = 0; i < meta->methodCount(); i++) {
416     if(meta->method(i).methodType() == QMetaMethod::Slot)
417       slotIndexes << i;
418   }
419
420   // we're faking sync pairs for sync replies
421   QByteArray slotSignature;
422   foreach(int slotIdx, slotIndexes) {
423     slotSignature = QByteArray(meta->method(slotIdx).signature());
424     if(!slotSignature.startsWith("receive"))
425       continue;
426     syncMap[slotSignature] = slotIdx;
427   }
428
429   QMetaMethod signal, slot;
430   int matchIdx;
431   for(int signalIdx = 0; signalIdx < meta->methodCount(); signalIdx++) {
432     signal = meta->method(signalIdx);
433     if(signal.methodType() != QMetaMethod::Signal)
434       continue;
435
436     matchIdx = -1;
437     foreach(int slotIdx, slotIndexes) {
438       slot = meta->method(slotIdx);
439       if(methodsMatch(signal, slot)) {
440         matchIdx = slotIdx;
441         break;
442       }
443     }
444     if(matchIdx != -1) {
445       slotIndexes.removeAt(slotIndexes.indexOf(matchIdx));
446       syncMap[QByteArray(signal.signature())] = matchIdx;
447     }
448   }
449
450   Q_ASSERT(_classInfo[meta]->syncMap.isEmpty());
451   _classInfo[meta]->syncMap = syncMap;
452 }
453
454 const QHash<QByteArray,int> &SignalProxy::syncMap(SyncableObject *obj) {
455   const QMetaObject *meta = obj->syncMetaObject();
456   Q_ASSERT(_classInfo.contains(meta));
457   if(_classInfo[meta]->syncMap.isEmpty())
458     setSyncMap(obj);
459   return _classInfo[meta]->syncMap;
460 }
461
462 void SignalProxy::setReceiveMap(SyncableObject *obj) {
463   const QMetaObject *meta = obj->syncMetaObject();
464   Q_ASSERT(_classInfo.contains(meta));
465
466   QHash<int, int> receiveMap;
467
468   QMetaMethod requestSlot;
469   QByteArray returnTypeName;
470   QByteArray signature;
471   QByteArray methodName;
472   QByteArray params;
473   int paramsPos;
474   int receiverId;
475   const int methodCount = meta->methodCount();
476   for(int i = 0; i < methodCount; i++) {
477     requestSlot = meta->method(i);
478     if(requestSlot.methodType() != QMetaMethod::Slot)
479       continue;
480
481     returnTypeName = requestSlot.typeName();
482     if(QMetaType::Void == (QMetaType::Type)returnType(obj, i))
483       continue;
484
485     signature = QByteArray(requestSlot.signature());
486     if(!signature.startsWith("request"))
487       continue;
488     
489     paramsPos = signature.indexOf('(');
490     if(paramsPos == -1)
491       continue;
492
493     methodName = signature.left(paramsPos);
494     params = signature.mid(paramsPos);
495
496     methodName = methodName.replace("request", "receive");
497     params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
498
499     signature = QMetaObject::normalizedSignature(methodName + params);
500     receiverId = meta->indexOfSlot(signature);
501
502     if(receiverId == -1) {
503       signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
504       receiverId = meta->indexOfSlot(signature);
505     }
506
507     if(receiverId != -1)
508       receiveMap[i] = receiverId;
509   }
510   _classInfo[meta]->receiveMap = receiveMap;  
511 }
512
513 const QHash<int, int> &SignalProxy::receiveMap(SyncableObject *obj) {
514   const QMetaObject *meta = obj->syncMetaObject();
515   Q_ASSERT(_classInfo.contains(meta));
516   if(_classInfo[meta]->receiveMap.isEmpty())
517     setReceiveMap(obj);
518   return _classInfo[meta]->receiveMap;
519 }
520
521 void SignalProxy::setUpdatedRemotelyId(SyncableObject *obj) {
522   const QMetaObject *meta = obj->syncMetaObject();
523   Q_ASSERT(_classInfo.contains(meta));
524   _classInfo[meta]->updatedRemotelyId = meta->indexOfSignal("updatedRemotely()");
525 }
526
527 int SignalProxy::updatedRemotelyId(SyncableObject *obj) {
528   Q_ASSERT(_classInfo.contains(obj->syncMetaObject()));
529   return _classInfo[obj->syncMetaObject()]->updatedRemotelyId;
530 }
531
532 const QMetaObject *SignalProxy::metaObject(QObject *obj) {
533   if(SyncableObject *syncObject = qobject_cast<SyncableObject *>(obj))
534     return syncObject->syncMetaObject();
535   else
536     return obj->metaObject();
537 }
538
539 void SignalProxy::createClassInfo(QObject *obj) {
540   const QMetaObject *meta = metaObject(obj);
541   if(_classInfo.contains(meta))
542     return;
543
544   ClassInfo *classInfo = new ClassInfo();
545   _classInfo[meta] = classInfo;
546 }
547
548 bool SignalProxy::attachSignal(QObject* sender, const char* signal, const QByteArray& sigName) {
549   const QMetaObject* meta = metaObject(sender);
550   QByteArray sig(meta->normalizedSignature(signal).mid(1));
551   int methodId = meta->indexOfMethod(sig.constData());
552   if(methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
553     qWarning() << "SignalProxy::attachSignal(): No such signal" << signal;
554     return false;
555   }
556
557   createClassInfo(sender);
558
559   SignalRelay* relay;
560   if(_relayHash.contains(sender))
561     relay = _relayHash[sender];
562   else
563     relay = _relayHash[sender] = new SignalRelay(this, sender);
564
565   relay->attachSignal(methodId, sigName);
566
567   return true;
568 }
569
570
571 bool SignalProxy::attachSlot(const QByteArray& sigName, QObject* recv, const char* slot) {
572   const QMetaObject* meta = recv->metaObject();
573   int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
574   if(methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
575     qWarning() << "SignalProxy::attachSlot(): No such slot" << slot;
576     return false;
577   }
578
579   createClassInfo(recv);
580
581   QByteArray funcName = QMetaObject::normalizedSignature(sigName.constData());
582   _attachedSlots.insert(funcName, qMakePair(recv, methodId));
583
584   QObject::disconnect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
585   QObject::connect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
586   return true;
587 }
588
589 void SignalProxy::synchronize(SyncableObject *obj) {
590   createClassInfo(obj);
591   setUpdatedRemotelyId(obj);
592   
593   // attaching all the Signals
594   SignalRelay* relay;
595   if(_relayHash.contains(obj))
596     relay = _relayHash[obj];
597   else
598     relay = _relayHash[obj] = new SignalRelay(this, obj);
599
600   relay->setSynchronize(true);
601
602   // attaching as slave to receive sync Calls
603   QByteArray className(obj->syncMetaObject()->className());
604   _syncSlave[className][obj->objectName()] = obj;
605
606   if(proxyMode() == Server) {
607     connect(obj, SIGNAL(objectRenamed(QString, QString)), this, SLOT(objectRenamed(QString, QString)));
608     setInitialized(obj);
609   } else {
610     requestInit(obj);
611   }
612 }
613
614 void SignalProxy::setInitialized(SyncableObject *obj) {
615   obj->setInitialized();
616   emit objectInitialized(obj);
617 }
618
619 bool SignalProxy::isInitialized(SyncableObject *obj) const {
620   return obj->isInitialized();
621 }
622
623 void SignalProxy::requestInit(SyncableObject *obj) {
624   if(proxyMode() == Server || isInitialized(obj))
625     return;
626
627   QVariantList params;
628   params << obj->syncMetaObject()->className()
629          << obj->objectName();
630   dispatchSignal(InitRequest, params);
631 }
632
633 void SignalProxy::detachSender() {
634   detachObject(sender());
635 }
636
637 void SignalProxy::detachObject(QObject* obj) {
638   detachSignals(obj);
639   detachSlots(obj);
640   stopSync(static_cast<SyncableObject *>(obj));
641 }
642
643 void SignalProxy::detachSignals(QObject* sender) {
644   if(!_relayHash.contains(sender))
645     return;
646   _relayHash.take(sender)->deleteLater();
647 }
648
649 void SignalProxy::detachSlots(QObject* receiver) {
650   SlotHash::iterator slotIter = _attachedSlots.begin();
651   while(slotIter != _attachedSlots.end()) {
652     if(slotIter.value().first == receiver) {
653       slotIter = _attachedSlots.erase(slotIter);
654     } else
655       slotIter++;
656   }
657 }
658
659 void SignalProxy::stopSync(SyncableObject* obj) {
660   if(_relayHash.contains(obj))
661     _relayHash[obj]->setSynchronize(false);
662
663   // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
664   // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
665   QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
666   while(classIter != _syncSlave.end()) {
667     if(classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
668       classIter->remove(obj->objectName());
669       break;
670     }
671     classIter++;
672   }
673 }
674
675 void SignalProxy::dispatchSignal(QIODevice *receiver, const RequestType &requestType, const QVariantList &params) {
676   QVariantList packedFunc;
677   packedFunc << (qint16)requestType;
678   packedFunc << params;
679   writeDataToDevice(receiver, QVariant(packedFunc));
680 }
681
682 void SignalProxy::dispatchSignal(const RequestType &requestType, const QVariantList &params) {
683   // yes I know we have a little code duplication here... it's for the sake of performance
684   QVariantList packedFunc;
685   packedFunc << (qint16)requestType;
686   packedFunc << params;
687   foreach(QIODevice* dev, _peerByteCount.keys())
688     writeDataToDevice(dev, QVariant(packedFunc));
689 }
690
691 void SignalProxy::receivePeerSignal(QIODevice *sender, const QVariant &packedFunc) {
692   QVariantList params(packedFunc.toList());
693
694   if(params.isEmpty()) {
695     qWarning() << "SignalProxy::receivePeerSignal(): received incompatible Data:" << packedFunc;
696     return;
697   }
698   
699   int callType = params.takeFirst().value<int>();
700
701   switch(callType) {
702   case RpcCall:
703     if(params.empty()) {
704       qWarning() << "SignalProxy::receivePeerSignal(): received empty RPC-Call";
705       return;
706     } else {
707       return handleSignal(params.takeFirst().toByteArray(), params);
708     }
709   case Sync:
710     return handleSync(sender, params);
711   case InitRequest:
712     return handleInitRequest(sender, params);
713   case InitData:
714     return handleInitData(sender, params);
715   case HeartBeat:
716     return;
717   default:
718     qWarning() << "SignalProxy::receivePeerSignal(): received undefined CallType" << callType << params;
719     return;
720   }
721 }
722
723 void SignalProxy::handleSync(QIODevice *sender, QVariantList params) {
724   if(params.count() < 3) {
725     qWarning() << "received invalid Sync call" << params;
726     return;
727   }
728   
729   QByteArray className = params.takeFirst().toByteArray();
730   QString objectName = params.takeFirst().toString();
731   QByteArray signal = params.takeFirst().toByteArray();
732
733   if(!_syncSlave.contains(className) || !_syncSlave[className].contains(objectName)) {
734     qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
735                << params;
736     return;
737   }
738
739   SyncableObject *receiver = _syncSlave[className][objectName];
740   if(!syncMap(receiver).contains(signal)) {
741     qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
742                << params;
743     return;
744   }
745
746   int slotId = syncMap(receiver)[signal];
747
748   QVariant returnValue((QVariant::Type)returnType(receiver, slotId));
749   if(!invokeSlot(receiver, slotId, params, returnValue)) {
750     qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", methodName(receiver, slotId).constData());
751     return;
752   }
753
754   if(returnValue.type() != QVariant::Invalid && receiveMap(receiver).contains(slotId)) {
755     int receiverId = receiveMap(receiver)[slotId];
756     QVariantList returnParams;
757     returnParams << className
758                  << objectName
759                  << QByteArray(receiver->metaObject()->method(receiverId).signature());
760     if(argTypes(receiver, receiverId).count() > 1)
761       returnParams << params;
762     returnParams << returnValue;
763     dispatchSignal(sender, Sync, returnParams);
764   }
765   
766   // send emit update signal
767   invokeSlot(receiver, updatedRemotelyId(receiver));
768 }
769
770 void SignalProxy::handleInitRequest(QIODevice *sender, const QVariantList &params) {
771   if(params.count() != 2) {
772     qWarning() << "SignalProxy::handleInitRequest() received initRequest with invalid param Count:"
773                << params;
774     return;
775   }
776   
777   QByteArray className(params[0].toByteArray());
778   QString objectName(params[1].toString());
779   
780   if(!_syncSlave.contains(className)) {
781     qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:"
782                << className;
783     return;
784   }
785
786   if(!_syncSlave[className].contains(objectName)) {
787     qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:"
788                << className << objectName;
789     return;
790   }
791   
792   SyncableObject *obj = _syncSlave[className][objectName];
793
794   QVariantList params_;
795   params_ << className
796           << objectName
797           << initData(obj);
798
799   dispatchSignal(sender, InitData, params_);
800 }
801
802 void SignalProxy::handleInitData(QIODevice *sender, const QVariantList &params) {
803   Q_UNUSED(sender)
804   if(params.count() != 3) {
805     qWarning() << "SignalProxy::handleInitData() received initData with invalid param Count:"
806                << params;
807     return;
808   }
809   
810   QByteArray className(params[0].toByteArray());
811   QString objectName(params[1].toString());
812   QVariantMap propertyMap(params[2].toMap());
813
814   if(!_syncSlave.contains(className)) {
815     qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:"
816                << className;
817     return;
818   }
819
820   if(!_syncSlave[className].contains(objectName)) {
821     qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:"
822                << className << objectName;
823     return;
824   }
825
826   SyncableObject *obj = _syncSlave[className][objectName];
827   setInitData(obj, propertyMap);
828 }
829
830 void SignalProxy::handleSignal(const QByteArray &funcName, const QVariantList &params) {
831   QObject* receiver;
832   int methodId;
833   SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
834   while(slot != _attachedSlots.constEnd() && slot.key() == funcName) {
835     receiver = (*slot).first;
836     methodId = (*slot).second;
837     if(!invokeSlot(receiver, methodId, params))
838       qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", methodName(receiver, methodId).constData());
839     slot++;
840   }
841 }
842
843 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, QVariant &returnValue) {
844   const QList<int> args = argTypes(receiver, methodId);
845   const int numArgs = params.count() < args.count()
846     ? params.count()
847     : args.count();
848
849   if(minArgCount(receiver, methodId) > params.count()) {
850       qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << methodName(receiver, methodId);
851       return false;
852   }
853
854   void *_a[] = {0,              // return type... 
855                 0, 0, 0, 0 , 0, // and 10 args - that's the max size qt can handle with signals and slots
856                 0, 0, 0, 0 , 0};
857
858   // check for argument compatibility and build params array
859   for(int i = 0; i < numArgs; i++) {
860     if(!params[i].isValid()) {
861       qWarning() << "SignalProxy::invokeSlot(): received invalid data for argument number" << i << "of method" << QString("%1::%2()").arg(receiver->metaObject()->className()).arg(receiver->metaObject()->method(methodId).signature());
862       qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
863       return false;
864     }
865     if(args[i] != QMetaType::type(params[i].typeName())) {
866       qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << methodName(receiver, methodId);
867       return false;
868     }
869     _a[i+1] = const_cast<void *>(params[i].constData());
870   }
871
872   if(returnValue.type() != QVariant::Invalid)
873     _a[0] = const_cast<void *>(returnValue.constData());
874     
875   Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
876     ? Qt::DirectConnection
877     : Qt::QueuedConnection;
878
879   if (type == Qt::DirectConnection) {
880     return receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
881   } else {
882     qWarning() << "Queued Connections are not implemented yet";
883     // note to self: qmetaobject.cpp:990 ff
884     return false;
885   }
886   
887 }
888
889 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params) {
890   QVariant ret;
891   return invokeSlot(receiver, methodId, params, ret);
892 }
893
894 void SignalProxy::dataAvailable() {
895   // yet again. it's a private slot. no need for checks.
896   QIODevice* ioDev = qobject_cast<QIODevice* >(sender());
897   QVariant var;
898   while(readDataFromDevice(ioDev, _peerByteCount[ioDev], var))
899     receivePeerSignal(ioDev, var);
900 }
901
902 void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item) {
903   QAbstractSocket* sock  = qobject_cast<QAbstractSocket*>(dev);
904   if(!dev->isOpen() || (sock && sock->state()!=QAbstractSocket::ConnectedState)) {
905     qWarning("SignalProxy: Can't call write on a closed device");
906     return;
907   }
908   QByteArray block;
909   QDataStream out(&block, QIODevice::WriteOnly);
910   out.setVersion(QDataStream::Qt_4_2);
911   out << (quint32)0 << item;
912   out.device()->seek(0);
913   out << (quint32)(block.size() - sizeof(quint32));
914   dev->write(block);
915 }
916
917 bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
918   QDataStream in(dev);
919   in.setVersion(QDataStream::Qt_4_2);
920
921   if(blockSize == 0) {
922     if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
923     in >> blockSize;
924   }
925
926   if(blockSize > 1 << 22) {
927     qWarning() << qPrintable(tr("Client tried to send package larger than max package size!"));
928     QAbstractSocket* sock  = qobject_cast<QAbstractSocket*>(dev);
929     qWarning() << qPrintable(tr("Disconnecting")) << (sock ? qPrintable(sock->peerAddress().toString()) : qPrintable(tr("local client")));
930     dev->close();
931     return false;
932   }
933     
934   if(dev->bytesAvailable() < blockSize)
935     return false;
936   in >> item;
937   blockSize = 0;
938   return true;
939 }
940
941 bool SignalProxy::methodsMatch(const QMetaMethod &signal, const QMetaMethod &slot) const {
942   // if we don't even have the same basename it's a sure NO
943   if(methodBaseName(signal) != methodBaseName(slot))
944     return false;
945
946   // are the signatures compatible?
947   if(!QObject::staticMetaObject.checkConnectArgs(signal.signature(), slot.signature()))
948     return false;
949
950   // we take an educated guess if the signals and slots match
951   QString signalsuffix = ::methodName(signal).mid(QString(::methodName(signal)).lastIndexOf(QRegExp("[A-Z]"))).toLower();
952   QString slotprefix = ::methodName(slot).left(QString(::methodName(slot)).indexOf(QRegExp("[A-Z]"))).toLower();
953
954   uint sizediff = qAbs(slotprefix.size() - signalsuffix.size());
955   int ratio = editingDistance(slotprefix, signalsuffix) - sizediff;
956   return (ratio < 2);
957 }
958
959 QString SignalProxy::methodBaseName(const QMetaMethod &method) {
960   QString methodname = QString(method.signature()).section("(", 0, 0);
961
962   // determine where we have to chop:
963   int upperCharPos;
964   if(method.methodType() == QMetaMethod::Slot) {
965     // we take evertyhing from the first uppercase char if it's slot
966     upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
967     if(upperCharPos == -1)
968       return QString();
969     methodname = methodname.mid(upperCharPos);
970   } else {
971     // and if it's a signal we discard everything from the last uppercase char
972     upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
973     if(upperCharPos == -1)
974       return QString();
975     methodname = methodname.left(upperCharPos);
976   }
977
978   methodname[0] = methodname[0].toUpper();
979
980   return methodname;
981 }
982
983 QVariantMap SignalProxy::initData(SyncableObject *obj) const {
984   return obj->toVariantMap();
985 }
986
987 void SignalProxy::setInitData(SyncableObject *obj, const QVariantMap &properties) {
988   if(isInitialized(obj))
989     return;
990   obj->fromVariantMap(properties);
991   setInitialized(obj);
992   invokeSlot(obj, updatedRemotelyId(obj));
993 }
994
995 void SignalProxy::sendHeartBeat() {
996   dispatchSignal(SignalProxy::HeartBeat, QVariantList());
997 }
998
999 void SignalProxy::dumpProxyStats() {
1000   QString mode;
1001   if(proxyMode() == Server)
1002     mode = "Server";
1003   else
1004     mode = "Client";
1005
1006   int sigCount = 0;
1007   foreach(SignalRelay *relay, _relayHash.values())
1008     sigCount += relay->sigCount();
1009
1010   int slaveCount = 0;
1011   foreach(ObjectId oid, _syncSlave.values())
1012     slaveCount += oid.count();
1013   
1014   qDebug() << this;
1015   qDebug() << "              Proxy Mode:" << mode;
1016   qDebug() << "attached sending Objects:" << _relayHash.count();
1017   qDebug() << "       number of Signals:" << sigCount;
1018   qDebug() << "          attached Slots:" << _attachedSlots.count();
1019   qDebug() << " number of synced Slaves:" << slaveCount;
1020   qDebug() << "number of Classes cached:" << _classInfo.count();
1021 }
1022
1023 void SignalProxy::dumpSyncMap(SyncableObject *object) {
1024   const QMetaObject *meta = object->metaObject();
1025   qDebug() << "SignalProxy: SyncMap for Class" << meta->className();
1026
1027   QHash<QByteArray, int> syncMap_ = syncMap(object);
1028   QHash<QByteArray, int>::const_iterator iter = syncMap_.constBegin();
1029   while(iter != syncMap_.constEnd()) {
1030     qDebug() << iter.key() << "-->" << iter.value() << meta->method(iter.value()).signature();    
1031     iter++;
1032   }
1033 //   QHash<int, int> syncMap_ = syncMap(object);
1034 //   QHash<int, int>::const_iterator iter = syncMap_.constBegin();
1035 //   while(iter != syncMap_.constEnd()) {
1036 //     qDebug() << iter.key() << meta->method(iter.key()).signature() << "-->" << iter.value() << meta->method(iter.value()).signature();    
1037 //     iter++;
1038 //   }
1039 }