+void SignalProxy::requestInit(SyncableObject *obj) {
+ if(proxyMode() == Server || obj->isInitialized())
+ return;
+
+ QVariantList params;
+ params << obj->syncMetaObject()->className()
+ << obj->objectName();
+ dispatchSignal(InitRequest, params);
+}
+
+QVariantMap SignalProxy::initData(SyncableObject *obj) const {
+ return obj->toVariantMap();
+}
+
+void SignalProxy::setInitData(SyncableObject *obj, const QVariantMap &properties) {
+ if(obj->isInitialized())
+ return;
+ obj->fromVariantMap(properties);
+ obj->setInitialized();
+ emit objectInitialized(obj);
+ invokeSlot(obj, extendedMetaObject(obj)->updatedRemotelyId());
+}
+
+void SignalProxy::sendHeartBeat() {
+ QVariantList heartBeatParams;
+ heartBeatParams << QTime::currentTime();
+ PeerHash::iterator peer = _peers.begin();
+ while(peer != _peers.end()) {
+ if((*peer)->type() == AbstractPeer::IODevicePeer) {
+ IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
+ ioPeer->dispatchSignal(SignalProxy::HeartBeat, heartBeatParams);
+ if(ioPeer->sentHeartBeats > 0) {
+ updateLag(ioPeer, ioPeer->sentHeartBeats * _heartBeatTimer.interval());
+ }
+ if(ioPeer->sentHeartBeats > 1) {
+ qWarning() << "SignalProxy: Disconnecting peer:" << ioPeer->address()
+ << "(didn't receive a heartbeat for over" << ioPeer->sentHeartBeats * _heartBeatTimer.interval() / 1000 << "seconds)";
+ ioPeer->close();
+ } else {
+ ioPeer->sentHeartBeats++;
+ }
+ }
+ peer++;
+ }
+}
+
+void SignalProxy::receiveHeartBeat(AbstractPeer *peer, const QVariantList ¶ms) {
+ peer->dispatchSignal(SignalProxy::HeartBeatReply, params);
+}
+
+void SignalProxy::receiveHeartBeatReply(AbstractPeer *peer, const QVariantList ¶ms) {
+ if(peer->type() != AbstractPeer::IODevicePeer) {
+ qWarning() << "SignalProxy::receiveHeartBeatReply: received heart beat from a non IODevicePeer!";
+ return;
+ }
+
+ IODevicePeer *ioPeer = static_cast<IODevicePeer *>(peer);
+ ioPeer->sentHeartBeats = 0;
+
+ if(params.isEmpty()) {
+ qWarning() << "SignalProxy: received heart beat reply with less params then sent from:" << ioPeer->address();
+ return;
+ }
+
+ QTime sendTime = params[0].value<QTime>();
+ updateLag(ioPeer, sendTime.msecsTo(QTime::currentTime()) / 2);
+}
+
+void SignalProxy::customEvent(QEvent *event) {
+ switch(event->type()) {
+ case PeerSignal:
+ {
+ PeerSignalEvent *e = static_cast<PeerSignalEvent *>(event);
+ receivePeerSignal(e->sender, e->requestType, e->params);
+ }
+ event->accept();
+ break;
+ case RemovePeer:
+ {
+ RemovePeerEvent *e = static_cast<RemovePeerEvent *>(event);
+ removePeer(e->peer);
+ }
+ event->accept();
+ default:
+ return;
+ }
+}
+
+void SignalProxy::disconnectDevice(QIODevice *dev, const QString &reason) {
+ if(!reason.isEmpty())
+ qWarning() << qPrintable(reason);
+ QAbstractSocket *sock = qobject_cast<QAbstractSocket *>(dev);
+ if(sock)
+ qWarning() << qPrintable(tr("Disconnecting")) << qPrintable(sock->peerAddress().toString());
+ dev->close();
+}
+
+void SignalProxy::updateLag(IODevicePeer *peer, int lag) {
+ peer->lag = lag;
+ if(proxyMode() == Client) {
+ emit lagUpdated(lag);
+ }
+}
+
+void SignalProxy::dumpProxyStats() {
+ QString mode;
+ if(proxyMode() == Server)
+ mode = "Server";
+ else
+ mode = "Client";
+
+ int sigCount = 0;
+ foreach(SyncRelay *relay, _syncRelays.values())
+ sigCount += relay->signalCount();
+
+ int slaveCount = 0;
+ foreach(ObjectId oid, _syncSlave.values())
+ slaveCount += oid.count();
+
+ qDebug() << this;
+ qDebug() << " Proxy Mode:" << mode;
+ // qDebug() << "attached sending Objects:" << _relayHash.count();
+ qDebug() << " number of Signals:" << sigCount;
+ qDebug() << " attached Slots:" << _attachedSlots.count();
+ qDebug() << " number of synced Slaves:" << slaveCount;
+ qDebug() << "number of Classes cached:" << _extendedMetaObjects.count();
+}
+
+void SignalProxy::dumpSyncMap(SyncableObject *object) {
+ const QMetaObject *meta = object->metaObject();
+ ExtendedMetaObject *eMeta = extendedMetaObject(object);
+ qDebug() << "SignalProxy: SyncMap for Class" << meta->className();
+
+ QHash<QByteArray, int> syncMap_ = eMeta->syncMap();
+ QHash<QByteArray, int>::const_iterator iter = syncMap_.constBegin();
+ while(iter != syncMap_.constEnd()) {
+ qDebug() << qPrintable(QString("%1 --> %2 %3").arg(QString(iter.key()), 40).arg(iter.value()).arg(QString(meta->method(iter.value()).signature())));
+ iter++;
+ }
+}
+
+void SignalProxy::updateSecureState() {
+ bool wasSecure = _secure;
+
+ _secure = !_peers.isEmpty();
+ PeerHash::const_iterator peerIter;
+ for(peerIter = _peers.constBegin(); peerIter != _peers.constEnd(); peerIter++) {
+ _secure &= (*peerIter)->isSecure();
+ }
+
+ if(wasSecure != _secure)
+ emit secureStateChanged(_secure);
+}
+
+
+// ==================================================
+// ExtendedMetaObject
+// ==================================================
+SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta)
+ : _meta(meta),
+ _updatedRemotelyId(-1)
+{
+}
+
+const QList<int> &SignalProxy::ExtendedMetaObject::argTypes(int methodId) {
+ if(!_argTypes.contains(methodId)) {
+ QList<QByteArray> paramTypes = _meta->method(methodId).parameterTypes();
+ QList<int> argTypes;
+ for(int i = 0; i < paramTypes.count(); i++) {
+ argTypes.append(QMetaType::type(paramTypes[i]));
+ }
+ _argTypes[methodId] = argTypes;
+ }
+ return _argTypes[methodId];
+}
+
+const int &SignalProxy::ExtendedMetaObject::returnType(int methodId) {
+ if(!_returnType.contains(methodId)) {
+ _returnType[methodId] = QMetaType::type(_meta->method(methodId).typeName());
+ }
+ return _returnType[methodId];
+}
+
+const int &SignalProxy::ExtendedMetaObject::minArgCount(int methodId) {
+ if(!_minArgCount.contains(methodId)) {
+ QString signature(_meta->method(methodId).signature());
+ _minArgCount[methodId] = _meta->method(methodId).parameterTypes().count() - signature.count("=");
+ }
+ return _minArgCount[methodId];
+}
+
+const QByteArray &SignalProxy::ExtendedMetaObject::methodName(int methodId) {
+ if(!_methodNames.contains(methodId)) {
+ _methodNames[methodId] = methodName(_meta->method(methodId));
+ }
+ return _methodNames[methodId];
+}
+
+const QHash<QByteArray, int> &SignalProxy::ExtendedMetaObject::syncMap() {
+ if(_syncMap.isEmpty()) {
+ QHash<QByteArray, int> syncMap;
+
+ QList<int> slotIndexes;
+ for(int i = 0; i < _meta->methodCount(); i++) {
+ if(_meta->method(i).methodType() == QMetaMethod::Slot)
+ slotIndexes << i;
+ }
+
+ // we're faking sync pairs for sync replies
+ // --> we deliver to every slot starting with "receive"
+ QByteArray slotSignature;
+ QList<int>::iterator slotIter = slotIndexes.begin();
+ while(slotIter != slotIndexes.end()) {
+ slotSignature = QByteArray(_meta->method(*slotIter).signature());
+ if(slotSignature.startsWith("receive")) {
+ syncMap[slotSignature] = *slotIter;
+ slotIter = slotIndexes.erase(slotIter);
+ } else {
+ slotIter++;
+ }
+ }
+
+ // find the proper sig -> slot matches
+ QMetaMethod signal, slot;
+ int matchIdx;
+ for(int signalIdx = 0; signalIdx < _meta->methodCount(); signalIdx++) {
+ signal = _meta->method(signalIdx);
+ if(signal.methodType() != QMetaMethod::Signal)
+ continue;
+
+ matchIdx = -1;
+ foreach(int slotIdx, slotIndexes) {
+ slot = _meta->method(slotIdx);
+ if(methodsMatch(signal, slot)) {
+ matchIdx = slotIdx;
+ break;
+ }
+ }
+ if(matchIdx != -1) {
+ slotIndexes.removeAll(matchIdx);
+ syncMap[QByteArray(signal.signature())] = matchIdx;
+ }
+ }
+ _syncMap = syncMap;
+ }
+ return _syncMap;
+}
+
+const QHash<int, int> &SignalProxy::ExtendedMetaObject::receiveMap() {
+ if(_receiveMap.isEmpty()) {
+ QHash<int, int> receiveMap;
+
+ QMetaMethod requestSlot;
+ QByteArray returnTypeName;
+ QByteArray signature;
+ QByteArray methodName;
+ QByteArray params;
+ int paramsPos;
+ int receiverId;
+ const int methodCount = _meta->methodCount();
+ for(int i = 0; i < methodCount; i++) {
+ requestSlot = _meta->method(i);
+ if(requestSlot.methodType() != QMetaMethod::Slot)
+ continue;
+
+ returnTypeName = requestSlot.typeName();
+ if(QMetaType::Void == (QMetaType::Type)returnType(i))
+ continue;
+
+ signature = QByteArray(requestSlot.signature());
+ if(!signature.startsWith("request"))
+ continue;
+
+ paramsPos = signature.indexOf('(');
+ if(paramsPos == -1)
+ continue;
+
+ methodName = signature.left(paramsPos);
+ params = signature.mid(paramsPos);
+
+ methodName = methodName.replace("request", "receive");
+ params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
+
+ signature = QMetaObject::normalizedSignature(methodName + params);
+ receiverId = _meta->indexOfSlot(signature);
+
+ if(receiverId == -1) {
+ signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
+ receiverId = _meta->indexOfSlot(signature);
+ }
+
+ if(receiverId != -1)
+ receiveMap[i] = receiverId;
+ }
+ _receiveMap = receiveMap;
+ }
+ return _receiveMap;
+}
+
+int SignalProxy::ExtendedMetaObject::updatedRemotelyId() {
+ if(_updatedRemotelyId == -1) {
+ _updatedRemotelyId = _meta->indexOfSignal("updatedRemotely()");
+ }
+ return _updatedRemotelyId;
+}
+
+QByteArray SignalProxy::ExtendedMetaObject::methodName(const QMetaMethod &method) {
+ QByteArray sig(method.signature());
+ return sig.left(sig.indexOf("("));
+}
+
+bool SignalProxy::ExtendedMetaObject::methodsMatch(const QMetaMethod &signal, const QMetaMethod &slot) {
+ // if we don't even have the same basename it's a sure NO
+ QString baseName = methodBaseName(signal);
+ if(baseName != methodBaseName(slot))
+ return false;
+
+ // are the signatures compatible?
+ if(!QObject::staticMetaObject.checkConnectArgs(signal.signature(), slot.signature()))
+ return false;
+
+ // we take an educated guess if the signals and slots match
+ QString signalsuffix = methodName(signal);
+ QString slotprefix = methodName(slot);
+ if(!baseName.isEmpty()) {
+ signalsuffix = signalsuffix.mid(baseName.count()).toLower();
+ slotprefix = slotprefix.left(slotprefix.count() - baseName.count()).toLower();
+ }
+
+ uint sizediff = qAbs(slotprefix.size() - signalsuffix.size());
+ int ratio = editingDistance(slotprefix, signalsuffix) - sizediff;
+ return (ratio < 2);
+}
+
+QString SignalProxy::ExtendedMetaObject::methodBaseName(const QMetaMethod &method) {
+ QString methodname = QString(method.signature()).section("(", 0, 0);
+
+ // determine where we have to chop:
+ int upperCharPos;
+ if(method.methodType() == QMetaMethod::Slot) {
+ // we take evertyhing from the first uppercase char if it's slot
+ upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
+ if(upperCharPos == -1)
+ return QString();
+ methodname = methodname.mid(upperCharPos);
+ } else {
+ // and if it's a signal we discard everything from the last uppercase char
+ upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
+ if(upperCharPos == -1)
+ return QString();
+ methodname = methodname.left(upperCharPos);
+ }
+
+ methodname[0] = methodname[0].toUpper();
+
+ return methodname;
+}