#include "JsonRpcAdaptorPrivate.h" #include "VariantToJson.h" #include #include #include #include namespace JsonQt { JsonRpcAdaptorPrivate::JsonRpcAdaptorPrivate(QObject* adapt, QObject* parent) : QObject(parent) { m_adapted = adapt; connect( &m_jsonRpc, SIGNAL(sendJson(const QString&)), this, SIGNAL(sendJson(const QString&)) ); connect( &m_jsonRpc, SIGNAL(requestReceived(const QVariant&, const QString&, const QVariant&)), this, SLOT(requestReceived(const QVariant&, const QString&, const QVariant&)) ); populateServiceDescription(); } void JsonRpcAdaptorPrivate::populateServiceDescription() { m_serviceDescription.clear(); const QMetaObject* metaObject = m_adapted->metaObject(); QString id = getClassInfo("JsonQt-RPC-id"); Q_ASSERT(!id.isEmpty()); m_serviceDescription["id"] = id; m_serviceDescription["sdversion"] = "1.0"; QString name = getClassInfo("JsonQt-RPC-name"); if(name.isEmpty()) name = metaObject->className(); m_serviceDescription["name"] = name; QString version = getClassInfo("JsonQt-RPC-version"); if(!version.isNull()) m_serviceDescription["version"] = version; QString summary = getClassInfo("JsonQt-RPC-summary"); if(!summary.isNull()) m_serviceDescription["summary"] = summary; QString help = getClassInfo("JsonQt-RPC-help"); if(!help.isNull()) m_serviceDescription["help"] = help; QMap typeMap; typeMap[""] = "nil"; // void typeMap["bool"] = "bit"; typeMap["int"] = "num"; typeMap["QString"] = "str"; // typeMap["QVariant"] = "any"; // not supported typeMap["QVariantList"] = "arr"; typeMap["QVariantMap"] = "obj"; QVariantList procs; for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) { QMetaMethod method = metaObject->method(i); // Check we should export it if(method.access() != QMetaMethod::Public || method.methodType() != QMetaMethod::Slot) continue; QVariantMap proc; QString signature = method.signature(); // Name QString methodName = signature.left(signature.indexOf('(')); proc["name"] = methodName; // Return type if(!typeMap.contains(method.typeName())) { qDebug() << "Public slot" << signature << "has unknown return type" << method.typeName(); continue; } proc["return"] = typeMap.value(method.typeName()); // Parameters QVariantList parameters; bool badParameter = false; for(int i = 0; i < method.parameterNames().count(); ++i) { QVariantMap parameter; QString parameterName(method.parameterNames().at(i)); parameter["name"] = parameterName; QString parameterType = method.parameterTypes().at(i); if(!typeMap.contains(method.parameterTypes().at(i))) { qDebug() << "Public slot" << signature << "has parameter" << parameterName << "with unknown type" << parameterType; badParameter = true; continue; } parameter["type"] = typeMap.value(parameterType); parameters.append(parameter); m_parameterIndices[methodName].insert(parameterName, i); } if(badParameter) continue; m_methods.insert(methodName, method); proc["params"] = parameters; // Done procs.append(proc); } m_serviceDescription["procs"] = procs; } QString JsonRpcAdaptorPrivate::getClassInfo(const char* name) { const QMetaObject* metaObject = m_adapted->metaObject(); int index = metaObject->indexOfClassInfo(name); if(index == -1) return QString(); return metaObject->classInfo(index).value(); } void JsonRpcAdaptorPrivate::requestReceived(const QVariant& id, const QString& method, const QVariant& parameters) { if(method == "system.describe") { m_jsonRpc.sendResponse(id, m_serviceDescription); return; } ReturnData result = invokeMethod(method, parameters); if(result.succeeded) { m_jsonRpc.sendResponse(id, result.data); } else { m_jsonRpc.sendError(id, result.code, result.message, result.data); } } JsonRpcAdaptorPrivate::ReturnData JsonRpcAdaptorPrivate::invokeMethod(const QString& methodName, const QVariant& parameters) { ReturnData ret; ret.succeeded = false; if(!m_methods.contains(methodName)) { ret.code = JsonRpc::MethodNotFound; ret.message = QString("The method %1 does not exist.").arg(methodName); return ret; } QMetaMethod metaMethod(m_methods.value(methodName)); int parameterCount; QVariantList parameterList; switch(parameters.type()) { case QVariant::List: parameterList = parameters.toList(); parameterCount = parameterList.count(); break; case QVariant::Map: parameterCount = parameters.toMap().count(); for(int i = 0; i < parameterCount; ++i) { parameterList.append(QVariant()); } break; default: parameterCount = 0; } Q_ASSERT(parameterCount <= 9); ret.code = JsonRpc::BadParameters; if(parameterCount != metaMethod.parameterNames().count()) { ret.message = "Parameter count mismatch."; return ret; } if(parameters.type() == QVariant::Map) { QMap parameterIndices = m_parameterIndices.value(methodName); QVariantMap parameterMap = parameters.toMap(); for(QVariantMap::ConstIterator it = parameterMap.constBegin(); it != parameterMap.constEnd(); ++it) { if(!parameterIndices.contains(it.key())) { ret.message = QString("'%1' is not a parameter of method '%2'.").arg(it.key()).arg(methodName); return ret; } int index = parameterIndices.value(it.key()); Q_ASSERT(index < parameterCount); parameterList[index] = it.value(); } } QMap typeMap; typeMap[""] = QVariant::Invalid; typeMap["bool"] = QVariant::Bool; typeMap["int"] = QVariant::Int; typeMap["QString"] = QVariant::String; typeMap["QVariantList"] = QVariant::List; typeMap["QVariantMap"] = QVariant::Map; ///@todo more types QVariant::Type returnType = typeMap.value(metaMethod.typeName()); // QMetaObject::invokeMethod takes 9 generic arguments QGenericArgument arguments[9]; for(int i = 0; i < parameterCount; ++i) { const QVariant& value = parameterList.value(i); if(typeMap.value(metaMethod.parameterTypes().at(i)) != value.type()) { ret.message = QString("Value for parameter %1 was not of the correct type.").arg(QString(metaMethod.parameterNames().at(i))); return ret; } void* data = 0; switch(value.type()) { case QVariant::Bool: data = new bool(value.toBool()); arguments[i] = Q_ARG(bool, *static_cast(data)); break; case QVariant::Int: data = new int(value.toInt()); arguments[i] = Q_ARG(int, *static_cast(data)); break; case QVariant::String: data = new QString(value.toString()); arguments[i] = Q_ARG(QString, *static_cast(data)); break; case QVariant::List: data = new QVariantList(value.toList()); arguments[i] = Q_ARG(QVariantList, *static_cast(data)); break; case QVariant::Map: data = new QVariantMap(value.toMap()); arguments[i] = Q_ARG(QVariantMap, *static_cast(data)); break; default: break; } } QGenericReturnArgument returnValue; switch(returnType) { case QVariant::Bool: returnValue = Q_RETURN_ARG(bool, *(new bool)); break; case QVariant::Int: returnValue = Q_RETURN_ARG(int, *(new int)); break; case QVariant::String: returnValue = Q_RETURN_ARG(QString, *(new QString)); break; case QVariant::List: returnValue = Q_RETURN_ARG(QVariantList, *(new QVariantList)); break; case QVariant::Map: returnValue = Q_RETURN_ARG(QVariantMap, *(new QVariantMap)); break; default: break; } bool success = m_adapted->metaObject()->invokeMethod( m_adapted, methodName.toLatin1().constData(), Qt::DirectConnection, returnValue, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9] ); // clean up memory allocation for(int i = 0; i < parameterCount; ++i) { QVariant::Type parameterType = parameterList.value(i).type(); switch(parameterType) { case QVariant::Bool: delete static_cast(arguments[i].data()); break; case QVariant::Int: delete static_cast(arguments[i].data()); break; case QVariant::String: delete static_cast(arguments[i].data()); break; case QVariant::List: delete static_cast(arguments[i].data()); break; case QVariant::Map: delete static_cast(arguments[i].data()); break; default: break; } } if(success == false) { ret.code = JsonRpc::InternalError; ret.message = "Could not execute method."; return ret; } ret.succeeded = true; switch(returnType) { case QVariant::Bool: ret.data = *static_cast(returnValue.data()); break; case QVariant::Int: ret.data = *static_cast(returnValue.data()); break; case QVariant::String: ret.data = *static_cast(returnValue.data()); break; case QVariant::List: ret.data = *static_cast(returnValue.data()); break; case QVariant::Map: ret.data = *static_cast(returnValue.data()); break; default: break; } return ret; } void JsonRpcAdaptorPrivate::processJson(const QString& json) { m_jsonRpc.processJson(json); } }