void Converter::generateServerMethod(KODE::Code &code, const Binding &binding, const Operation &operation, KODE::Class &newClass, bool first) { const QString requestVarName = "_request"; const QString responseVarName = "_response"; const Message message = mWSDL.findMessage(operation.input().message()); Message outputMessage; if (operation.operationType() != Operation::OneWayOperation) { outputMessage = mWSDL.findMessage(operation.output().message()); } const QString operationName = operation.name(); const QString methodName = mNameMapper.escape(lowerlize(operationName)); KODE::Function virtualMethod(methodName); virtualMethod.setVirtualMode(KODE::Function::PureVirtual); QString condition = "method == \"" + operationName + "\""; if (binding.type() == Binding::SOAPBinding) { const SoapBinding soapBinding(binding.soapBinding()); const SoapBinding::Operation op = soapBinding.operations().value(operation.name()); if (!op.action().isEmpty()) { condition += " || _soapAction == \"" + op.action() + "\""; } } code += QString(first ? "" : "else ") + "if (" + condition + ") {"; code.indent(); QStringList inputVars; const Part::List parts = message.parts(); for (int partNum = 0; partNum < parts.count(); ++partNum) { const Part part = parts.at(partNum); const QString lowerName = lowerlize(part.name()); const QString argType = mTypeMap.localType(part.type(), part.element()); //qDebug() << "localInputType" << part.type().qname() << part.element().qname() << "->" << argType; if (argType != "void") { const QString varName = mNameMapper.escape(lowerName); code += argType + ' ' + varName + ";" + COMMENT; QString soapValueVarName = requestVarName; if (soapStyle(binding) == SoapBinding::RPCStyle) { // RPC comes with a wrapper element, dig into it here soapValueVarName = "val"; if (partNum > 0) { soapValueVarName += QString::number(partNum + 1); } code += QString::fromLatin1("const KDSoapValue %1 = %2.childValues().at(%3);").arg(soapValueVarName, requestVarName).arg(partNum) + COMMENT; } // what if there's more than one? code.addBlock(demarshalVar(part.type(), part.element(), varName, argType, soapValueVarName, false, false)); inputVars += varName; newClass.addIncludes(mTypeMap.headerIncludes(part.type()), mTypeMap.forwardDeclarationsForElement(part.element())); virtualMethod.addArgument(mTypeMap.localInputType(part.type(), part.element()) + ' ' + varName); } } const Part::List outParts = outputMessage.parts(); if (outParts.count() > 1) { qWarning("ERROR: multiple output parameters are not supported (operation %s) - please file" "an issue on github with your wsdl file", qPrintable(operation.name())); virtualMethod.setReturnType("void /*UNSUPPORTED*/"); } else if (outParts.isEmpty()) { code += lowerlize(operationName) + '(' + inputVars.join(", ") + ");"; virtualMethod.setReturnType("void"); } else { QString retType; QString retInputType; //bool isBuiltin = false; //bool isComplex = false; Part retPart; Q_FOREACH (const Part &outPart, outParts /* only one */) { retType = mTypeMap.localType(outPart.type(), outPart.element()); retInputType = mTypeMap.localInputType(outPart.type(), outPart.element()); //isBuiltin = mTypeMap.isBuiltinType( outPart.type(), outPart.element() ); //isComplex = mTypeMap.isComplexType( outPart.type(), outPart.element() ); retPart = outPart; } const QString methodCall = methodName + '(' + inputVars.join(", ") + ')'; if (retType == "void") { code += methodCall + ";" + COMMENT; } else { code += retType + " ret = " + methodCall + ";" + COMMENT; } code += "if (!hasFault()) {"; code.indent(); // TODO factorize with same code in next method if (soapStyle(binding) == SoapBinding::DocumentStyle) { code.addBlock(serializePart(retPart, "ret", responseVarName, false)); } else { code += QString("KDSoapValue wrapper(\"%1\", QVariant(), \"%2\");").arg(outputMessage.name()).arg(outputMessage.nameSpace()); code.addBlock(serializePart(retPart, "ret", "wrapper.childValues()", true)); code += responseVarName + " = wrapper;"; } code.unindent(); code += "}"; Q_ASSERT(!retType.isEmpty()); virtualMethod.setReturnType(retType); newClass.addIncludes(mTypeMap.headerIncludes(retPart.type()), mTypeMap.forwardDeclarationsForElement(retPart.element())); generateDelayedReponseMethod(methodName, retInputType, retPart, newClass, binding, outputMessage); }
void Converter::convertSimpleType(const XSD::SimpleType *type, const XSD::SimpleType::List &simpleTypeList) { const QString typeName(mTypeMap.localType(type->qualifiedName())); //qDebug() << "convertSimpleType:" << type->qualifiedName() << typeName; KODE::Class newClass; newClass.setNamespaceAndName(typeName); if (!Settings::self()->exportDeclaration().isEmpty()) { newClass.setExportDeclaration(Settings::self()->exportDeclaration()); } newClass.setNameSpace(Settings::self()->nameSpace()); QString classDocumentation; switch (type->subType()) { case XSD::SimpleType::TypeRestriction: { /** Use setter and getter method for enums as well. */ if (type->facetType() & XSD::SimpleType::ENUM) { classDocumentation = "This class is a wrapper for an enumeration.\n"; NameMapper nameMapper; QStringList enums = type->facetEnums(); for (int i = 0; i < enums.count(); ++i) { enums[ i ] = nameMapper.escape(escapeEnum(enums[ i ])); } newClass.addEnum(KODE::Enum("Type", enums)); classDocumentation += "Whenever you have to pass an object of type " + newClass.name() + " you can also pass the enum directly. Example:\n" + "someMethod(" + newClass.name() + "::" + enums.first() + ")."; // member variables KODE::MemberVariable variable("type", "Type"); variable.setInitializer("Type(0)"); newClass.addMemberVariable(variable); // setter method KODE::Function setter("setType", "void"); setter.addArgument("Type type"); setter.setBody(variable.name() + " = type;"); // getter method KODE::Function getter("type", newClass.qualifiedName() + "::Type"); getter.setBody("return " + variable.name() + ';'); getter.setConst(true); // convenience constructor KODE::Function conctor(newClass.name()); conctor.addArgument("const Type &type"); KODE::Code code; code += variable.name() + " = type;"; conctor.setBody(code); // type operator KODE::Function op("operator Type"); op.setBody("return " + variable.name() + ';'); op.setConst(true); newClass.addFunction(conctor); newClass.addFunction(setter); newClass.addFunction(getter); newClass.addFunction(op); } /** A class can't derive from basic types (e.g. int or unsigned char), so we add setter and getter methods to set the value of this class. */ if (type->baseTypeName() != XmlAnyType && !type->baseTypeName().isEmpty() && !(type->facetType() & XSD::SimpleType::ENUM)) { classDocumentation = "This class encapsulates a simple type.\n"; const QName baseName = type->baseTypeName(); const QString baseTypeName = mTypeMap.localType(baseName); Q_ASSERT(!baseTypeName.isEmpty()); QList<QName> parentBasicTypes; parentBasicTypes.append(baseName); QName currentType = baseName; Q_FOREVER { const XSD::SimpleType simpleType = simpleTypeList.simpleType(currentType); if (!simpleType.isNull() && simpleType.isRestriction()) { currentType = simpleType.baseTypeName(); parentBasicTypes.append(currentType); continue; } break; } classDocumentation += "Whenever you have to pass an object of type " + newClass.name() + " you can also pass the value directly as a " + mTypeMap.localType(currentType) + '.'; // include header newClass.addIncludes(QStringList(), mTypeMap.forwardDeclarations(baseName)); newClass.addHeaderIncludes(mTypeMap.headerIncludes(baseName)); // member variables KODE::MemberVariable variable("value", baseTypeName); addVariableInitializer(variable); newClass.addMemberVariable(variable); // setter method KODE::Function setter("setValue", "void"); const QString inputType = mTypeMap.localInputType(baseName, QName()); setter.addArgument(inputType + " value"); KODE::Code setterBody; if (type->facetType() != XSD::SimpleType::NONE) { const XSD::SimpleType baseSimpleType = simpleTypeList.simpleType(baseName); setterBody += createRangeCheckCode(type, baseTypeName, "value", newClass, baseSimpleType); setterBody.newLine(); setterBody += "if (!rangeOk)"; setterBody.indent(); setterBody += "qDebug( \"Invalid range in " + newClass.name() + "::" + setter.name() + "()\" );"; setterBody.unindent(); setterBody.newLine(); } setterBody += variable.name() + " = value;"; // ### call setValue in base class? setter.setBody(setterBody); newClass.addFunction(setter); // getter method KODE::Function getter("value", baseTypeName); getter.setBody("return " + variable.name() + ';'); getter.setConst(true); newClass.addFunction(getter); // convenience constructor KODE::Function conctor(newClass.name()); conctor.addArgument(inputType + " value"); conctor.addBodyLine("setValue(value);"); newClass.addFunction(conctor); // even more convenient constructor, for the case of multiple-level simple-type restrictions //qDebug() << typeName << ": baseName=" << baseName << "further up:" << parentBasicTypes; if (parentBasicTypes.count() > 1) { parentBasicTypes.removeLast(); // the top-most one is in "currentType", so it's the input arg. KODE::Function baseCtor(conctor.name()); baseCtor.addArgument(mTypeMap.localInputType(currentType, QName()) + " value"); QString beginLine = "setValue("; QString endLine = ")"; Q_FOREACH (const QName &base, parentBasicTypes) { beginLine += mTypeMap.localType(base) + '('; endLine += ')'; } baseCtor.addBodyLine(beginLine + "value" + endLine + ';'); newClass.addFunction(baseCtor); } // type operator KODE::Function op("operator " + baseTypeName); op.setBody("return " + variable.name() + ';'); op.setConst(true); newClass.addFunction(op); }
void Converter::generateServerMethod(KODE::Code& code, const Binding& binding, const Operation& operation, KODE::Class &newClass, bool first) { const Message message = mWSDL.findMessage( operation.input().message() ); Message outputMessage; if (operation.operationType() != Operation::OneWayOperation) { outputMessage = mWSDL.findMessage( operation.output().message() ); } const QString operationName = operation.name(); const QString methodName = mNameMapper.escape( lowerlize( operationName ) ); KODE::Function virtualMethod(methodName); virtualMethod.setVirtualMode(KODE::Function::PureVirtual); QString condition = "method == \"" + operationName + "\""; if ( binding.type() == Binding::SOAPBinding ) { const SoapBinding soapBinding( binding.soapBinding() ); const SoapBinding::Operation op = soapBinding.operations().value( operation.name() ); if (!op.action().isEmpty()) { condition += "|| soapAction == \"" + op.action() + "\""; } } code += QString(first ? "" : "else ") + "if (" + condition + ") {"; code.indent(); QStringList inputVars; const Part::List parts = message.parts(); if (parts.count() > 1) { qWarning("ERROR: multiple input parameters are not supported - please report this with your wsdl file to [email protected]"); } Q_FOREACH( const Part& part, parts ) { const QString lowerName = lowerlize( part.name() ); const QString argType = mTypeMap.localType( part.type(), part.element() ); //qDebug() << "localInputType" << part.type().qname() << part.element().qname() << "->" << argType; if ( argType != "void" ) { const QString varName = mNameMapper.escape( lowerName ); code += argType + ' ' + varName + ";" + COMMENT; QString soapValueVarName = "request"; if (soapStyle(binding) == SoapBinding::RPCStyle) { // RPC comes with a wrapper element, dig into it here code += QLatin1String("const KDSoapValue val = request.childValues().first();") + COMMENT; soapValueVarName = "val"; } // what if there's more than one? code.addBlock( demarshalVar( part.type(), part.element(), varName, argType, soapValueVarName ) ); inputVars += varName; newClass.addIncludes( mTypeMap.headerIncludes( part.type() ) ); virtualMethod.addArgument( mTypeMap.localInputType( part.type(), part.element() ) + ' ' + varName ); } } const Part::List outParts = outputMessage.parts(); if (outParts.count() > 1) { qWarning("ERROR: multiple output parameters are not supported (operation %s) - please report" " this with your wsdl file to [email protected]", qPrintable(operation.name())); virtualMethod.setReturnType("void /*UNSUPPORTED*/"); } else if (outParts.isEmpty()) { code += operationName + '(' + inputVars.join(", ") + ");"; virtualMethod.setReturnType("void"); } else { QString retType; QString retInputType; //bool isBuiltin = false; //bool isComplex = false; Part retPart; Q_FOREACH( const Part& outPart, outParts /* only one */ ) { retType = mTypeMap.localType( outPart.type(), outPart.element() ); retInputType = mTypeMap.localInputType( outPart.type(), outPart.element() ); //isBuiltin = mTypeMap.isBuiltinType( outPart.type(), outPart.element() ); //isComplex = mTypeMap.isComplexType( outPart.type(), outPart.element() ); retPart = outPart; } const QString methodCall = methodName + '(' + inputVars.join(", ") + ')'; if (retType == "void") { code += methodCall + ";" + COMMENT; } else { code += retType + " ret = " + methodCall + ";" + COMMENT; } code += "if (!hasFault()) {"; code.indent(); bool qualified; const QName elemName = elementNameForPart( retPart, &qualified ); if (soapStyle(binding) == SoapBinding::RPCStyle) { code += QString("KDSoapValue wrapper(\"%1\", QVariant());").arg(outputMessage.name()); code.addBlock( serializeElementArg( retPart.type(), retPart.element(), elemName, "ret", "wrapper.childValues()", true, qualified ) ); code += "response = wrapper;"; } else { code.addBlock( serializeElementArg( retPart.type(), retPart.element(), elemName, "ret", "response", false, qualified ) ); } code.unindent(); code += "}"; Q_ASSERT(!retType.isEmpty()); virtualMethod.setReturnType(retType); generateDelayedReponseMethod(methodName, retInputType, retPart, newClass, binding, outputMessage); }
void Converter::convertComplexType(const XSD::ComplexType *type) { // An empty type is still useful, in document mode: it serializes the element name //if ( type->isEmpty() ) // return; // Skip the Array types we added in Parser::init... if (NSManager::soapEncNamespaces().contains(type->nameSpace())) { return; } const QString className(mTypeMap.localType(type->qualifiedName())); KODE::Class newClass; newClass.setNamespaceAndName(className); if (!Settings::self()->exportDeclaration().isEmpty()) { newClass.setExportDeclaration(Settings::self()->exportDeclaration()); } newClass.setUseSharedData(true, QLatin1String("d_ptr") /*avoid clash with possible d() method */); const bool doDebug = (qgetenv("KDSOAP_TYPE_DEBUG").toInt()); if (doDebug) { qDebug() << "Generating complex type" << className; } // subclass handling if (!type->baseTypeName().isEmpty()) { // this class extends something /** * A class can't subclass basic type (e.g. int, unsigned char), so we * add setValue() and value() methods to access the base type. * * In fact, let's do the same with string */ if (type->baseTypeName().localName() == QLatin1String("Array")) { // this is handled in the attribute section } else { const QName baseName = type->baseTypeName(); const QString typeName = mTypeMap.localType(baseName); const QString inputTypeName = mTypeMap.localInputType(baseName, QName()); // include header newClass.addIncludes(QStringList(), mTypeMap.forwardDeclarations(baseName)); newClass.addHeaderIncludes(mTypeMap.headerIncludes(baseName)); if (mTypeMap.isComplexType(baseName)) { newClass.addBaseClass(typeName); } else { const QString variableName = generateMemberVariable("value", typeName, inputTypeName, newClass, XSD::Attribute::Required, false, false); // convenience constructor KODE::Function conctor(upperlize(newClass.name())); conctor.addArgument(inputTypeName + QLatin1String(" value")); conctor.setBody(variableName + QLatin1String(" = value;")); // type operator KODE::Function op(QLatin1String("operator ") + typeName); op.setBody(QLatin1String("return ") + variableName + QLatin1Char(';')); op.setConst(true); newClass.addFunction(conctor); newClass.addFunction(op); } } } if (!type->documentation().isEmpty()) { newClass.setDocs(type->documentation().simplified()); } QVector<QString> seenElements; // elements in the complex type const XSD::Element::List elements = type->elements(); Q_FOREACH (const XSD::Element &elemIt, elements) { if (elemIt.type().isEmpty()) { qDebug() << "ERROR: Element from" << *type << "with no type:" << elemIt << "(skipping)"; Q_ASSERT(false); continue; } // When having <choice> // <sequence>A,B(opt)</sequence> // B // <sequence>C,B(opt)</sequence> // </choice> // we don't want to emit setB() three times, that's not valid C++ // (testcase in wsdl_document.wsdl TestRepeatedChildren) if (seenElements.contains(elemIt.name())) { continue; } seenElements.append(elemIt.name()); QString typeName = mTypeMap.localType(elemIt.type()); Q_ASSERT(!typeName.isEmpty()); if (typeName != QLatin1String("void")) { // void means empty element, probably just here for later extensions (testcase: SetPasswordResult in salesforce) QString inputTypeName = mTypeMap.localInputType(elemIt.type(), QName()); bool isList = false; if (elemIt.maxOccurs() > 1 || elemIt.compositor().maxOccurs() > 1) { QString itemType = mTypeMap.isPolymorphic(elemIt.type()) ? pointerStorageType(typeName) : typeName; typeName = listTypeFor(itemType, newClass); inputTypeName = QLatin1String("const ") + typeName + QLatin1String("&"); isList = true; } if (type->isArray()) { QString arrayTypeName = mTypeMap.localType(type->arrayType()); Q_ASSERT(!arrayTypeName.isEmpty()); if (mTypeMap.isPolymorphic(type->arrayType())) { arrayTypeName = pointerStorageType(arrayTypeName); } //qDebug() << "array of" << attribute.arrayType() << "->" << arrayTypeName; typeName = listTypeFor(arrayTypeName, newClass); if (!mTypeMap.isBasicType(type->arrayType())) { newClass.addInclude(QString(), arrayTypeName); // add forward declaration } newClass.addHeaderIncludes(QStringList() << QLatin1String("QtCore/QList")); inputTypeName = QLatin1String("const ") + typeName + QLatin1Char('&'); isList = true; } XSD::Attribute::AttributeUse use = isElementOptional(elemIt) ? XSD::Attribute::Optional : XSD::Attribute::Required; const bool polymorphic = isElementPolymorphic(elemIt, mTypeMap, isList); const bool usePointer = usePointerForElement(elemIt, newClass, mTypeMap, isList); generateMemberVariable(KODE::Style::makeIdentifier(elemIt.name()), typeName, inputTypeName, newClass, use, usePointer, polymorphic); } // include header newClass.addIncludes(QStringList(), mTypeMap.forwardDeclarations(elemIt.type())); newClass.addHeaderIncludes(mTypeMap.headerIncludes(elemIt.type())); if (elemIt.maxOccurs() > 1 || elemIt.compositor().maxOccurs() > 1) { newClass.addHeaderIncludes(QStringList() << QLatin1String("QtCore/QList")); } } // attributes in the complex type XSD::Attribute::List attributes = type->attributes(); Q_FOREACH (const XSD::Attribute &attribute, attributes) { QString typeName, inputTypeName; typeName = mTypeMap.localType(attribute.type()); if (typeName.isEmpty()) { qDebug() << "ERROR: attribute with unknown type:" << attribute.name() << attribute.type() << "in" << typeName; } inputTypeName = mTypeMap.localInputType(attribute.type(), QName()); //qDebug() << "Attribute" << attribute.name(); generateMemberVariable(KODE::Style::makeIdentifier(attribute.name()), typeName, inputTypeName, newClass, attribute.attributeUse(), false, false); // include header newClass.addIncludes(QStringList(), mTypeMap.forwardDeclarations(attribute.type())); newClass.addHeaderIncludes(mTypeMap.headerIncludes(attribute.type())); }