1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "signalproxy.h"
25 #include <QAbstractSocket>
26 #include <QHostAddress>
32 #include <QMetaMethod>
33 #include <QMetaProperty>
38 #include "syncableobject.h"
42 // ==================================================
44 // ==================================================
45 class SignalRelay: public QObject {
46 /* Q_OBJECT is not necessary or even allowed, because we implement
47 qt_metacall ourselves (and don't use any other features of the meta
51 SignalRelay(SignalProxy* parent, QObject* source);
52 int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
54 void attachSignal(int methodId, const QByteArray &func);
56 void setSynchronize(bool);
57 bool synchronize() const;
62 bool isSyncMethod(int i);
66 QMultiHash<int, QByteArray> sigNames;
70 SignalRelay::SignalRelay(SignalProxy* parent, QObject* source)
76 QObject::connect(source, SIGNAL(destroyed()), parent, SLOT(detachSender()));
79 int SignalRelay::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
80 _id = QObject::qt_metacall(_c, _id, _a);
83 if(_c == QMetaObject::InvokeMetaMethod) {
84 if(sigNames.contains(_id) || synchronize()) {
85 const QList<int> &argTypes = proxy->argTypes(caller, _id);
87 int n = argTypes.size();
88 for(int i=0; i<n; i++) {
89 if(argTypes[i] == 0) {
90 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());
91 qWarning() << " - make sure all your data types are known by the Qt MetaSystem";
94 params.append(QVariant(argTypes[i], _a[i+1]));
96 QMultiHash<int, QByteArray>::const_iterator funcIter = sigNames.constFind(_id);
97 while(funcIter != sigNames.constEnd() && funcIter.key() == _id) {
98 proxy->dispatchSignal(SignalProxy::RpcCall, QVariantList() << funcIter.value() << params);
102 // dispatch Sync Signal if necessary
103 QByteArray signature(caller->metaObject()->method(_id).signature());
104 SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
105 if(synchronize() && proxy->syncMap(syncObject).contains(signature)) {
106 //qDebug() << "__SYNC__ >>>"
107 // << caller->metaObject()->className()
108 // << caller->objectName()
111 // params.prepend(QVariant(_id));
112 params.prepend(signature);
113 params.prepend(syncObject->objectName());
114 params.prepend(syncObject->syncMetaObject()->className());
115 proxy->dispatchSignal(SignalProxy::Sync, params);
118 _id -= QObject::staticMetaObject.methodCount();
123 void SignalRelay::setSynchronize(bool sync) {
124 SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
128 const QMetaObject *meta = syncObject->syncMetaObject();
131 for(int i = 0; i < meta->methodCount(); i++ ) {
133 QMetaObject::connect(caller, i, this, QObject::staticMetaObject.methodCount() + i);
135 } else if (_sync && !sync) {
137 for(int i = 0; i < meta->methodCount(); i++ ) {
139 QMetaObject::disconnect(caller, i, this, QObject::staticMetaObject.methodCount() + i);
145 bool SignalRelay::isSyncMethod(int i) {
146 SyncableObject *syncObject = qobject_cast<SyncableObject *>(caller);
150 QByteArray signature = syncObject->syncMetaObject()->method(i).signature();
151 if(!proxy->syncMap(syncObject).contains(signature))
154 if(proxy->proxyMode() == SignalProxy::Server && !signature.contains("Requested"))
157 if(proxy->proxyMode() == SignalProxy::Client && signature.contains("Requested"))
163 bool SignalRelay::synchronize() const {
167 int SignalRelay::sigCount() const {
168 // only for debuging purpose
169 return sigNames.count();
172 void SignalRelay::attachSignal(int methodId, const QByteArray &func) {
173 // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
174 // all connected methodIds are offset by the standard methodCount of QObject
175 if(!sigNames.contains(methodId))
176 QMetaObject::connect(caller, methodId, this, QObject::staticMetaObject.methodCount() + methodId);
179 if(!func.isEmpty()) {
180 fn = QMetaObject::normalizedSignature(func);
182 fn = QByteArray("2") + caller->metaObject()->method(methodId).signature();
184 sigNames.insert(methodId, fn);
187 // ==================================================
189 // ==================================================
190 void SignalProxy::IODevicePeer::dispatchSignal(const RequestType &requestType, const QVariantList ¶ms) {
191 QVariantList packedFunc;
192 packedFunc << (qint16)requestType
194 dispatchPackedFunc(QVariant(packedFunc));
197 // ==================================================
199 // ==================================================
200 SignalProxy::SignalProxy(QObject* parent)
203 setProxyMode(Client);
207 SignalProxy::SignalProxy(ProxyMode mode, QObject* parent)
214 SignalProxy::SignalProxy(ProxyMode mode, QIODevice* device, QObject* parent)
222 SignalProxy::~SignalProxy() {
223 QList<QObject*> senders = _relayHash.keys();
224 foreach(QObject* sender, senders)
225 detachObject(sender);
228 void SignalProxy::setProxyMode(ProxyMode mode) {
229 PeerHash::iterator peer = _peers.begin();
230 while(peer != _peers.end()) {
231 if((*peer)->type() != AbstractPeer::IODevicePeer) {
232 IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
233 if(ioPeer->isOpen()) {
234 qWarning() << "SignalProxy: Cannot change proxy mode while connected";
238 if((*peer)->type() != AbstractPeer::SignalProxyPeer) {
239 qWarning() << "SignalProxy: Cannot change proxy mode while connected to another internal SignalProxy";
252 void SignalProxy::init() {
253 connect(&_heartBeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartBeat()));
254 _heartBeatTimer.start(30 * 1000);
257 void SignalProxy::initServer() {
260 void SignalProxy::initClient() {
261 attachSlot("__objectRenamed__", this, SLOT(objectRenamed(QByteArray, QString, QString)));
264 bool SignalProxy::addPeer(QIODevice* iodev) {
268 if(_peers.contains(iodev))
271 if(proxyMode() == Client && !_peers.isEmpty()) {
272 qWarning("SignalProxy: only one peer allowed in client mode!");
277 qWarning("SignalProxy::the device you passed is not open!");
279 connect(iodev, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
280 connect(iodev, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
282 QAbstractSocket* sock = qobject_cast<QAbstractSocket*>(iodev);
284 connect(sock, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
287 // we take ownership of that device
288 iodev->setParent(this);
290 _peers[iodev] = new IODevicePeer(iodev, iodev->property("UseCompression").toBool());
292 if(_peers.count() == 1)
298 void SignalProxy::removeAllPeers() {
299 Q_ASSERT(proxyMode() == Server || _peers.count() <= 1);
300 // wee need to copy that list since we modify it in the loop
301 QList<QObject *> peers = _peers.keys();
302 foreach(QObject *peer, peers) {
303 switch(_peers[peer]->type()) {
304 case AbstractPeer::IODevicePeer:
305 removePeer(static_cast<QIODevice *>(peer));
307 case AbstractPeer::SignalProxyPeer:
308 removePeer(static_cast<SignalProxy *>(peer));
311 Q_ASSERT(false); // there shouldn't be any peers with wrong / unknown type
316 void SignalProxy::removePeer(QIODevice* iodev) {
317 if(_peers.isEmpty()) {
318 qWarning() << "SignalProxy::removePeer(): No peers in use!";
323 if(!_peers.contains(iodev)) {
324 qWarning() << "SignalProxy: unknown QIODevice" << iodev;
328 AbstractPeer *peer = _peers[iodev];
329 _peers.remove(iodev);
332 disconnect(iodev, 0, this, 0);
333 emit peerRemoved(iodev);
339 void SignalProxy::removePeer(SignalProxy *proxy) {
340 if(!_peers.contains(proxy)) {
341 qWarning() << "SignalProxy: unknown QIODevice" << proxy;
345 _peers.remove(proxy);
352 void SignalProxy::removePeerBySender() {
353 // OK we're brutal here... but since it's a private slot we know what we've got connected to it...
354 // this Slot is not triggered by destroyed, so the object is still alive and can be used!
355 QIODevice *ioDev = (QIODevice *)(sender());
359 void SignalProxy::objectRenamed(const QString &newname, const QString &oldname) {
360 SyncableObject *syncObject = qobject_cast<SyncableObject *>(sender());
361 const QMetaObject *meta = syncObject->metaObject();
362 const QByteArray className(meta->className());
363 objectRenamed(className, newname, oldname);
365 if(proxyMode() == Client)
369 params << "__objectRenamed__" << className << newname << oldname;
370 dispatchSignal(RpcCall, params);
373 void SignalProxy::objectRenamed(const QByteArray &classname, const QString &newname, const QString &oldname) {
374 if(_syncSlave.contains(classname) && _syncSlave[classname].contains(oldname) && oldname != newname) {
375 SyncableObject *obj = _syncSlave[classname][newname] = _syncSlave[classname].take(oldname);
380 void SignalProxy::setArgTypes(QObject* obj, int methodId) {
381 const QMetaObject *meta = metaObject(obj);
382 QList<QByteArray> p = meta->method(methodId).parameterTypes();
385 for(int i=0; i<ct; i++)
386 argTypes.append(QMetaType::type(p.value(i)));
388 Q_ASSERT(!_classInfo[meta]->argTypes.contains(methodId));
389 _classInfo[meta]->argTypes[methodId] = argTypes;
392 const QList<int> &SignalProxy::argTypes(QObject *obj, int methodId) {
393 const QMetaObject *meta = metaObject(obj);
394 Q_ASSERT(_classInfo.contains(meta));
395 if(!_classInfo[meta]->argTypes.contains(methodId))
396 setArgTypes(obj, methodId);
397 return _classInfo[meta]->argTypes[methodId];
400 void SignalProxy::setReturnType(QObject *obj, int methodId) {
401 const QMetaObject *meta = metaObject(obj);
402 int returnType = QMetaType::type(meta->method(methodId).typeName());
404 Q_ASSERT(!_classInfo[meta]->returnType.contains(methodId));
405 _classInfo[meta]->returnType[methodId] = returnType;
408 const int &SignalProxy::returnType(QObject *obj, int methodId) {
409 const QMetaObject *meta = metaObject(obj);
410 Q_ASSERT(_classInfo.contains(meta));
411 if(!_classInfo[meta]->returnType.contains(methodId))
412 setReturnType(obj, methodId);
413 return _classInfo[meta]->returnType[methodId];
416 void SignalProxy::setMinArgCount(QObject *obj, int methodId) {
417 const QMetaObject *meta = metaObject(obj);
418 QString signature(meta->method(methodId).signature());
419 int minCount = meta->method(methodId).parameterTypes().count() - signature.count("=");
420 Q_ASSERT(!_classInfo[meta]->minArgCount.contains(methodId));
421 _classInfo[meta]->minArgCount[methodId] = minCount;
424 const int &SignalProxy::minArgCount(QObject *obj, int methodId) {
425 const QMetaObject *meta = metaObject(obj);
426 Q_ASSERT(_classInfo.contains(meta));
427 if(!_classInfo[meta]->minArgCount.contains(methodId))
428 setMinArgCount(obj, methodId);
429 return _classInfo[meta]->minArgCount[methodId];
432 void SignalProxy::setMethodName(QObject *obj, int methodId) {
433 const QMetaObject *meta = metaObject(obj);
434 QByteArray method(::methodName(meta->method(methodId)));
435 Q_ASSERT(!_classInfo[meta]->methodNames.contains(methodId));
436 _classInfo[meta]->methodNames[methodId] = method;
439 const QByteArray &SignalProxy::methodName(QObject *obj, int methodId) {
440 const QMetaObject *meta = metaObject(obj);
441 Q_ASSERT(_classInfo.contains(meta));
442 if(!_classInfo[meta]->methodNames.contains(methodId))
443 setMethodName(obj, methodId);
444 return _classInfo[meta]->methodNames[methodId];
448 void SignalProxy::setSyncMap(SyncableObject *obj) {
449 const QMetaObject *meta = obj->syncMetaObject();
450 QHash<QByteArray, int> syncMap;
452 QList<int> slotIndexes;
453 for(int i = 0; i < meta->methodCount(); i++) {
454 if(meta->method(i).methodType() == QMetaMethod::Slot)
458 // we're faking sync pairs for sync replies
459 QByteArray slotSignature;
460 foreach(int slotIdx, slotIndexes) {
461 slotSignature = QByteArray(meta->method(slotIdx).signature());
462 if(!slotSignature.startsWith("receive"))
464 syncMap[slotSignature] = slotIdx;
467 QMetaMethod signal, slot;
469 for(int signalIdx = 0; signalIdx < meta->methodCount(); signalIdx++) {
470 signal = meta->method(signalIdx);
471 if(signal.methodType() != QMetaMethod::Signal)
475 foreach(int slotIdx, slotIndexes) {
476 slot = meta->method(slotIdx);
477 if(methodsMatch(signal, slot)) {
483 slotIndexes.removeAt(slotIndexes.indexOf(matchIdx));
484 syncMap[QByteArray(signal.signature())] = matchIdx;
488 Q_ASSERT(_classInfo[meta]->syncMap.isEmpty());
489 _classInfo[meta]->syncMap = syncMap;
492 const QHash<QByteArray,int> &SignalProxy::syncMap(SyncableObject *obj) {
493 const QMetaObject *meta = obj->syncMetaObject();
494 Q_ASSERT(_classInfo.contains(meta));
495 if(_classInfo[meta]->syncMap.isEmpty())
497 return _classInfo[meta]->syncMap;
500 void SignalProxy::setReceiveMap(SyncableObject *obj) {
501 const QMetaObject *meta = obj->syncMetaObject();
502 Q_ASSERT(_classInfo.contains(meta));
504 QHash<int, int> receiveMap;
506 QMetaMethod requestSlot;
507 QByteArray returnTypeName;
508 QByteArray signature;
509 QByteArray methodName;
513 const int methodCount = meta->methodCount();
514 for(int i = 0; i < methodCount; i++) {
515 requestSlot = meta->method(i);
516 if(requestSlot.methodType() != QMetaMethod::Slot)
519 returnTypeName = requestSlot.typeName();
520 if(QMetaType::Void == (QMetaType::Type)returnType(obj, i))
523 signature = QByteArray(requestSlot.signature());
524 if(!signature.startsWith("request"))
527 paramsPos = signature.indexOf('(');
531 methodName = signature.left(paramsPos);
532 params = signature.mid(paramsPos);
534 methodName = methodName.replace("request", "receive");
535 params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
537 signature = QMetaObject::normalizedSignature(methodName + params);
538 receiverId = meta->indexOfSlot(signature);
540 if(receiverId == -1) {
541 signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
542 receiverId = meta->indexOfSlot(signature);
546 receiveMap[i] = receiverId;
548 _classInfo[meta]->receiveMap = receiveMap;
551 const QHash<int, int> &SignalProxy::receiveMap(SyncableObject *obj) {
552 const QMetaObject *meta = obj->syncMetaObject();
553 Q_ASSERT(_classInfo.contains(meta));
554 if(_classInfo[meta]->receiveMap.isEmpty())
556 return _classInfo[meta]->receiveMap;
559 void SignalProxy::setUpdatedRemotelyId(SyncableObject *obj) {
560 const QMetaObject *meta = obj->syncMetaObject();
561 Q_ASSERT(_classInfo.contains(meta));
562 _classInfo[meta]->updatedRemotelyId = meta->indexOfSignal("updatedRemotely()");
565 int SignalProxy::updatedRemotelyId(SyncableObject *obj) {
566 Q_ASSERT(_classInfo.contains(obj->syncMetaObject()));
567 return _classInfo[obj->syncMetaObject()]->updatedRemotelyId;
570 const QMetaObject *SignalProxy::metaObject(QObject *obj) {
571 if(SyncableObject *syncObject = qobject_cast<SyncableObject *>(obj))
572 return syncObject->syncMetaObject();
574 return obj->metaObject();
577 void SignalProxy::createClassInfo(QObject *obj) {
578 const QMetaObject *meta = metaObject(obj);
579 if(_classInfo.contains(meta))
582 ClassInfo *classInfo = new ClassInfo();
583 _classInfo[meta] = classInfo;
586 bool SignalProxy::attachSignal(QObject* sender, const char* signal, const QByteArray& sigName) {
587 const QMetaObject* meta = metaObject(sender);
588 QByteArray sig(meta->normalizedSignature(signal).mid(1));
589 int methodId = meta->indexOfMethod(sig.constData());
590 if(methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
591 qWarning() << "SignalProxy::attachSignal(): No such signal" << signal;
595 createClassInfo(sender);
598 if(_relayHash.contains(sender))
599 relay = _relayHash[sender];
601 relay = _relayHash[sender] = new SignalRelay(this, sender);
603 relay->attachSignal(methodId, sigName);
609 bool SignalProxy::attachSlot(const QByteArray& sigName, QObject* recv, const char* slot) {
610 const QMetaObject* meta = recv->metaObject();
611 int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
612 if(methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
613 qWarning() << "SignalProxy::attachSlot(): No such slot" << slot;
617 createClassInfo(recv);
619 QByteArray funcName = QMetaObject::normalizedSignature(sigName.constData());
620 _attachedSlots.insert(funcName, qMakePair(recv, methodId));
622 QObject::disconnect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
623 QObject::connect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
627 void SignalProxy::synchronize(SyncableObject *obj) {
628 createClassInfo(obj);
629 setUpdatedRemotelyId(obj);
631 // attaching all the Signals
633 if(_relayHash.contains(obj))
634 relay = _relayHash[obj];
636 relay = _relayHash[obj] = new SignalRelay(this, obj);
638 relay->setSynchronize(true);
640 // attaching as slave to receive sync Calls
641 QByteArray className(obj->syncMetaObject()->className());
642 _syncSlave[className][obj->objectName()] = obj;
644 if(proxyMode() == Server) {
645 connect(obj, SIGNAL(objectRenamed(QString, QString)), this, SLOT(objectRenamed(QString, QString)));
646 obj->setInitialized();
647 emit objectInitialized(obj);
649 if(obj->isInitialized())
650 emit objectInitialized(obj);
656 void SignalProxy::requestInit(SyncableObject *obj) {
657 if(proxyMode() == Server || obj->isInitialized())
661 params << obj->syncMetaObject()->className()
662 << obj->objectName();
663 dispatchSignal(InitRequest, params);
666 void SignalProxy::detachSender() {
667 detachObject(sender());
670 void SignalProxy::detachObject(QObject* obj) {
673 stopSync(static_cast<SyncableObject *>(obj));
676 void SignalProxy::detachSignals(QObject* sender) {
677 if(!_relayHash.contains(sender))
679 _relayHash.take(sender)->deleteLater();
682 void SignalProxy::detachSlots(QObject* receiver) {
683 SlotHash::iterator slotIter = _attachedSlots.begin();
684 while(slotIter != _attachedSlots.end()) {
685 if(slotIter.value().first == receiver) {
686 slotIter = _attachedSlots.erase(slotIter);
692 void SignalProxy::stopSync(SyncableObject* obj) {
693 if(_relayHash.contains(obj))
694 _relayHash[obj]->setSynchronize(false);
696 // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
697 // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
698 QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
699 while(classIter != _syncSlave.end()) {
700 if(classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
701 classIter->remove(obj->objectName());
708 void SignalProxy::dispatchSignal(const RequestType &requestType, const QVariantList ¶ms) {
709 QVariant packedFunc(QVariantList() << (qint16)requestType << params);
710 PeerHash::iterator peer = _peers.begin();
711 while(peer != _peers.end()) {
712 switch((*peer)->type()) {
713 case AbstractPeer::IODevicePeer:
715 IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
716 ioPeer->dispatchPackedFunc(packedFunc);
719 case AbstractPeer::SignalProxyPeer:
720 (*peer)->dispatchSignal(requestType, params);
723 Q_ASSERT(false); // there shouldn't be any peers with wrong / unknown type
729 void SignalProxy::receivePackedFunc(AbstractPeer *sender, const QVariant &packedFunc) {
730 QVariantList params(packedFunc.toList());
732 if(params.isEmpty()) {
733 qWarning() << "SignalProxy::receivePeerSignal(): received incompatible Data:" << packedFunc;
737 RequestType requestType = (RequestType)params.takeFirst().value<int>();
738 receivePeerSignal(sender, requestType, params);
741 void SignalProxy::receivePeerSignal(AbstractPeer *sender, const RequestType &requestType, const QVariantList ¶ms) {
742 switch(requestType) {
745 qWarning() << "SignalProxy::receivePeerSignal(): received empty RPC-Call";
747 handleSignal(params);
748 //handleSignal(params.takeFirst().toByteArray(), params);
752 handleSync(sender, params);
756 handleInitRequest(sender, params);
760 handleInitData(sender, params);
764 receiveHeartBeat(sender, params);
768 receiveHeartBeatReply(sender, params);
772 qWarning() << "SignalProxy::receivePeerSignal(): received undefined CallType" << requestType << params;
776 void SignalProxy::handleSync(AbstractPeer *sender, QVariantList params) {
777 if(params.count() < 3) {
778 qWarning() << "received invalid Sync call" << params;
782 QByteArray className = params.takeFirst().toByteArray();
783 QString objectName = params.takeFirst().toString();
784 QByteArray signal = params.takeFirst().toByteArray();
786 if(!_syncSlave.contains(className) || !_syncSlave[className].contains(objectName)) {
787 qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
792 SyncableObject *receiver = _syncSlave[className][objectName];
793 if(!syncMap(receiver).contains(signal)) {
794 qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
799 int slotId = syncMap(receiver)[signal];
801 QVariant returnValue((QVariant::Type)returnType(receiver, slotId));
802 if(!invokeSlot(receiver, slotId, params, returnValue)) {
803 qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", methodName(receiver, slotId).constData());
807 if(returnValue.type() != QVariant::Invalid && receiveMap(receiver).contains(slotId)) {
808 int receiverId = receiveMap(receiver)[slotId];
809 QVariantList returnParams;
810 returnParams << className
812 << QByteArray(receiver->metaObject()->method(receiverId).signature());
813 if(argTypes(receiver, receiverId).count() > 1)
814 returnParams << params;
815 returnParams << returnValue;
816 sender->dispatchSignal(Sync, returnParams);
819 // send emit update signal
820 invokeSlot(receiver, updatedRemotelyId(receiver));
823 void SignalProxy::handleInitRequest(AbstractPeer *sender, const QVariantList ¶ms) {
824 if(params.count() != 2) {
825 qWarning() << "SignalProxy::handleInitRequest() received initRequest with invalid param Count:"
830 QByteArray className(params[0].toByteArray());
831 QString objectName(params[1].toString());
833 if(!_syncSlave.contains(className)) {
834 qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:"
839 if(!_syncSlave[className].contains(objectName)) {
840 qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:"
841 << className << objectName;
845 SyncableObject *obj = _syncSlave[className][objectName];
847 QVariantList params_;
852 sender->dispatchSignal(InitData, params_);
855 void SignalProxy::handleInitData(AbstractPeer *sender, const QVariantList ¶ms) {
857 if(params.count() != 3) {
858 qWarning() << "SignalProxy::handleInitData() received initData with invalid param Count:"
863 QByteArray className(params[0].toByteArray());
864 QString objectName(params[1].toString());
865 QVariantMap propertyMap(params[2].toMap());
867 if(!_syncSlave.contains(className)) {
868 qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:"
873 if(!_syncSlave[className].contains(objectName)) {
874 qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:"
875 << className << objectName;
879 SyncableObject *obj = _syncSlave[className][objectName];
880 setInitData(obj, propertyMap);
883 //void SignalProxy::handleSignal(const QByteArray &funcName, const QVariantList ¶ms) {
884 void SignalProxy::handleSignal(const QVariantList &data) {
885 QVariantList params = data;
886 QByteArray funcName = params.takeFirst().toByteArray();
890 SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
891 while(slot != _attachedSlots.constEnd() && slot.key() == funcName) {
892 receiver = (*slot).first;
893 methodId = (*slot).second;
894 if(!invokeSlot(receiver, methodId, params))
895 qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", methodName(receiver, methodId).constData());
900 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList ¶ms, QVariant &returnValue) {
901 const QList<int> args = argTypes(receiver, methodId);
902 const int numArgs = params.count() < args.count()
906 if(minArgCount(receiver, methodId) > params.count()) {
907 qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << methodName(receiver, methodId);
911 void *_a[] = {0, // return type...
912 0, 0, 0, 0 , 0, // and 10 args - that's the max size qt can handle with signals and slots
915 // check for argument compatibility and build params array
916 for(int i = 0; i < numArgs; i++) {
917 if(!params[i].isValid()) {
918 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());
919 qWarning() << " - make sure all your data types are known by the Qt MetaSystem";
922 if(args[i] != QMetaType::type(params[i].typeName())) {
923 qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << methodName(receiver, methodId);
926 _a[i+1] = const_cast<void *>(params[i].constData());
929 if(returnValue.type() != QVariant::Invalid)
930 _a[0] = const_cast<void *>(returnValue.constData());
932 Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
933 ? Qt::DirectConnection
934 : Qt::QueuedConnection;
936 if (type == Qt::DirectConnection) {
937 return receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
939 qWarning() << "Queued Connections are not implemented yet";
940 // note to self: qmetaobject.cpp:990 ff
946 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList ¶ms) {
948 return invokeSlot(receiver, methodId, params, ret);
951 void SignalProxy::dataAvailable() {
952 // yet again. it's a private slot. no need for checks.
953 QIODevice* ioDev = qobject_cast<QIODevice* >(sender());
954 Q_ASSERT(_peers.contains(ioDev) && _peers[ioDev]->type() == AbstractPeer::IODevicePeer);
955 IODevicePeer *peer = static_cast<IODevicePeer *>(_peers[ioDev]);
957 while(peer->readData(var))
958 receivePackedFunc(peer, var);
961 void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item, bool compressed) {
962 QAbstractSocket* sock = qobject_cast<QAbstractSocket*>(dev);
963 if(!dev->isOpen() || (sock && sock->state()!=QAbstractSocket::ConnectedState)) {
964 qWarning("SignalProxy: Can't call write on a closed device");
969 QDataStream out(&block, QIODevice::WriteOnly);
970 out.setVersion(QDataStream::Qt_4_2);
975 QDataStream itemStream(&rawItem, QIODevice::WriteOnly);
977 itemStream.setVersion(QDataStream::Qt_4_2);
980 rawItem = qCompress(rawItem);
987 out.device()->seek(0);
988 out << (quint32)(block.size() - sizeof(quint32));
993 bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item, bool compressed) {
995 in.setVersion(QDataStream::Qt_4_2);
998 if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
1002 if(blockSize > 1 << 22) {
1003 qWarning() << qPrintable(tr("Client tried to send package larger than max package size!"));
1004 QAbstractSocket *sock = qobject_cast<QAbstractSocket *>(dev);
1005 qWarning() << qPrintable(tr("Disconnecting")) << (sock ? qPrintable(sock->peerAddress().toString()) : qPrintable(tr("local client")));
1010 if(dev->bytesAvailable() < blockSize)
1017 int nbytes = rawItem.size();
1019 const char *data = rawItem.constData();
1020 if (nbytes < 4 || (data[0]!=0 || data[1]!=0 || data[2]!=0 || data[3]!=0))
1021 qWarning() << "receieved corrupted compressed data:"
1022 << blockSize << rawItem << rawItem.size() << dev;
1025 rawItem = qUncompress(rawItem);
1027 QDataStream itemStream(&rawItem, QIODevice::ReadOnly);
1028 itemStream.setVersion(QDataStream::Qt_4_2);
1039 bool SignalProxy::methodsMatch(const QMetaMethod &signal, const QMetaMethod &slot) const {
1040 // if we don't even have the same basename it's a sure NO
1041 if(methodBaseName(signal) != methodBaseName(slot))
1044 // are the signatures compatible?
1045 if(!QObject::staticMetaObject.checkConnectArgs(signal.signature(), slot.signature()))
1048 // we take an educated guess if the signals and slots match
1049 QString signalsuffix = ::methodName(signal).mid(QString(::methodName(signal)).lastIndexOf(QRegExp("[A-Z]"))).toLower();
1050 QString slotprefix = ::methodName(slot).left(QString(::methodName(slot)).indexOf(QRegExp("[A-Z]"))).toLower();
1052 uint sizediff = qAbs(slotprefix.size() - signalsuffix.size());
1053 int ratio = editingDistance(slotprefix, signalsuffix) - sizediff;
1057 QString SignalProxy::methodBaseName(const QMetaMethod &method) {
1058 QString methodname = QString(method.signature()).section("(", 0, 0);
1060 // determine where we have to chop:
1062 if(method.methodType() == QMetaMethod::Slot) {
1063 // we take evertyhing from the first uppercase char if it's slot
1064 upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
1065 if(upperCharPos == -1)
1067 methodname = methodname.mid(upperCharPos);
1069 // and if it's a signal we discard everything from the last uppercase char
1070 upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
1071 if(upperCharPos == -1)
1073 methodname = methodname.left(upperCharPos);
1076 methodname[0] = methodname[0].toUpper();
1081 QVariantMap SignalProxy::initData(SyncableObject *obj) const {
1082 return obj->toVariantMap();
1085 void SignalProxy::setInitData(SyncableObject *obj, const QVariantMap &properties) {
1086 if(obj->isInitialized())
1088 obj->fromVariantMap(properties);
1089 obj->setInitialized();
1090 emit objectInitialized(obj);
1091 invokeSlot(obj, updatedRemotelyId(obj));
1094 void SignalProxy::sendHeartBeat() {
1095 dispatchSignal(SignalProxy::HeartBeat, QVariantList() << QTime::currentTime());
1096 PeerHash::iterator peer = _peers.begin();
1097 while(peer != _peers.end()) {
1098 if((*peer)->type() == AbstractPeer::IODevicePeer) {
1099 IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
1100 if(ioPeer->sentHeartBeats > 0) {
1101 updateLag(ioPeer, ioPeer->sentHeartBeats * _heartBeatTimer.interval());
1103 if(ioPeer->sentHeartBeats > 1) {
1104 //FIXME: proper disconnect.
1105 // QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(peerIter.key());
1106 // qWarning() << "SignalProxy: Disconnecting peer:"
1107 // << (socket ? qPrintable(socket->peerAddress().toString()) : "local client")
1108 // << "(didn't receive a heartbeat for over" << peerIter->sentHeartBeats * _heartBeatTimer.interval() / 1000 << "seconds)";
1109 // peerIter.key()->close();
1111 ioPeer->sentHeartBeats++;
1118 void SignalProxy::receiveHeartBeat(AbstractPeer *peer, const QVariantList ¶ms) {
1119 peer->dispatchSignal(SignalProxy::HeartBeatReply, params);
1122 void SignalProxy::receiveHeartBeatReply(AbstractPeer *peer, const QVariantList ¶ms) {
1123 if(peer->type() != AbstractPeer::IODevicePeer) {
1124 qWarning() << "SignalProxy::receiveHeartBeatReply: received heart beat from a non IODevicePeer!";
1128 IODevicePeer *ioPeer = static_cast<IODevicePeer *>(peer);
1129 ioPeer->sentHeartBeats = 0;
1131 if(params.isEmpty()) {
1132 qWarning() << "SignalProxy: received heart beat reply with less params then sent from:" << ioPeer->device();
1136 QTime sendTime = params[0].value<QTime>();
1137 updateLag(ioPeer, sendTime.msecsTo(QTime::currentTime()) / 2);
1140 void SignalProxy::updateLag(IODevicePeer *peer, int lag) {
1142 if(proxyMode() == Client) {
1143 emit lagUpdated(lag);
1148 void SignalProxy::dumpProxyStats() {
1150 if(proxyMode() == Server)
1156 foreach(SignalRelay *relay, _relayHash.values())
1157 sigCount += relay->sigCount();
1160 foreach(ObjectId oid, _syncSlave.values())
1161 slaveCount += oid.count();
1164 qDebug() << " Proxy Mode:" << mode;
1165 qDebug() << "attached sending Objects:" << _relayHash.count();
1166 qDebug() << " number of Signals:" << sigCount;
1167 qDebug() << " attached Slots:" << _attachedSlots.count();
1168 qDebug() << " number of synced Slaves:" << slaveCount;
1169 qDebug() << "number of Classes cached:" << _classInfo.count();
1172 void SignalProxy::dumpSyncMap(SyncableObject *object) {
1173 const QMetaObject *meta = object->metaObject();
1174 qDebug() << "SignalProxy: SyncMap for Class" << meta->className();
1176 QHash<QByteArray, int> syncMap_ = syncMap(object);
1177 QHash<QByteArray, int>::const_iterator iter = syncMap_.constBegin();
1178 while(iter != syncMap_.constEnd()) {
1179 qDebug() << iter.key() << "-->" << iter.value() << meta->method(iter.value()).signature();
1182 // QHash<int, int> syncMap_ = syncMap(object);
1183 // QHash<int, int>::const_iterator iter = syncMap_.constBegin();
1184 // while(iter != syncMap_.constEnd()) {
1185 // qDebug() << iter.key() << meta->method(iter.key()).signature() << "-->" << iter.value() << meta->method(iter.value()).signature();