Exemple #1
0
void Converter::convertOutputMessage( const Port&, const Message &message, KODE::Class &newClass )
{
  Message::Part part = message.parts().first();

  // signal
  QString messageName = message.name();
  messageName[ 0 ] = messageName[ 0 ].lower();
  KODE::Function respSignal( messageName, "void", KODE::Function::Signal );

  /**
    If one output message is used by two input messages, don't define
    it twice.
   */
  if ( newClass.hasFunction( respSignal.name() ) )
    return;

  const Message::Part::List parts = message.parts();
  Message::Part::List::ConstIterator it;
  for ( it = parts.begin(); it != parts.end(); ++it ) {
    QStringList headers = mTypeMapper.header( (*it).type() );
    for ( uint i = 0; i < headers.count(); ++i )
      if ( !headers[ i ].isEmpty() )
        newClass.addHeaderInclude( headers[ i ] );

    respSignal.addArgument( mTypeMapper.argument( "value", (*it).type() ) );
  }

  newClass.addFunction( respSignal );

  // slot
  KODE::Function respSlot( messageName + "Slot", "void", KODE::Function::Slot | KODE::Function::Private );
  respSlot.addArgument( "const QString &xml" );

  KODE::Code code;
  code += "QDomDocument doc;";
  code += "QString errorMsg;";
  code += "int column, row;";
  code.newLine();
  code += "qDebug( \"%s\", xml.latin1() );";
  code.newLine();
  code += "if ( !doc.setContent( xml, true, &errorMsg, &row, &column ) ) {";
  code.indent();
  code += "qDebug( \"Unable to parse xml: %s (%d:%d)\", errorMsg.latin1(), row, column );";
  code += "return;";
  code.unindent();
  code += "}";
  code.newLine();
  code += mTypeMapper.type( part.type() ) + "* value = new " + mTypeMapper.type( part.type() ) + ";";
  code += "QDomElement envelope = doc.documentElement();";
  code += "QDomElement body = envelope.firstChild().toElement();";
  code += "QDomElement method = body.firstChild().toElement();";
  code += "Serializer::demarshal( method.firstChild().toElement(), value );";
  code.newLine();
  code += "emit " + respSignal.name() + "( value );";
  respSlot.setBody( code );

  newClass.addFunction( respSlot );
}
static bool usePointerForElement(const XSD::Element &elem, const KODE::Class &newClass, const KWSDL::TypeMap &typeMap, bool isList)
{
    const QString typeName = typeMap.localType(elem.type());
    const bool polymorphic = isElementPolymorphic(elem, typeMap, isList);
    if (!isList && isElementOptional(elem) && newClass.name() == typeName) {
        if (qgetenv("KDSOAP_TYPE_DEBUG").toInt()) {
            qDebug() << "Making element polymorphic:" << typeName << "in" << newClass.name();
        }
        // Optional "Foo" in class "Foo" - can't just have a value and bool, we need a pointer, to avoid infinite recursion
        //use == XSD::Attribute::Required;
        return true;
    }
    return polymorphic;
}
Exemple #3
0
void Converter::convertInputMessage( const Port &port, const Message &message, KODE::Class &newClass )
{
  KODE::MemberVariable transport( message.name() + "Transport", "Transport*" );
  newClass.addMemberVariable( transport );

  // call
  QString messageName = message.name();
  messageName[ 0 ] = messageName[ 0 ].lower();
  KODE::Function callFunc( mNameMapper.escape( messageName ), "void", KODE::Function::Public );

  const Message::Part::List parts = message.parts();
  Message::Part::List::ConstIterator it;
  for ( it = parts.begin(); it != parts.end(); ++it ) {
    newClass.addHeaderIncludes( mTypeMapper.header( (*it).type() ) );
    QString lowerName = (*it).name();
    lowerName[ 0 ] = lowerName[ 0 ].lower();
    callFunc.addArgument( mTypeMapper.argument( mNameMapper.escape( lowerName ), (*it).type() ) );
  }

  KODE::Code code;
  code += "QDomDocument doc( \"kwsdl\" );";
  code += "doc.appendChild( doc.createProcessingInstruction( \"xml\", \"version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"\" ) );";
  code += "QDomElement env = doc.createElement( \"SOAP-ENV:Envelope\" );";
  code += "env.setAttribute( \"xmlns:SOAP-ENV\", \"http://schemas.xmlsoap.org/soap/envelope/\" );";
  code += "env.setAttribute( \"xmlns:xsi\", \"http://www.w3.org/1999/XMLSchema-instance\" );";
  code += "env.setAttribute( \"xmlns:xsd\", \"http://www.w3.org/1999/XMLSchema\" );";
  code += "doc.appendChild( env );";
  code += "QDomElement body = doc.createElement( \"SOAP-ENV:Body\" );";
  code += "env.appendChild( body );";
  code += "QDomElement method = doc.createElement( \"ns1:" + message.name() + "\" );";
  QString nameSpace = mWSDL.findBindingOperation( port.name(), message.name() ).input().nameSpace();
  code += "method.setAttribute( \"xmlns:ns1\", \"" + nameSpace + "\" );";
  code += "method.setAttribute( \"SOAP-ENV:encodingStyle\", \"http://schemas.xmlsoap.org/soap/encoding/\" );";
  code += "body.appendChild( method );";
  code.newLine();

  for ( it = parts.begin(); it != parts.end(); ++it ) {
    QString lowerName = (*it).name();
    lowerName[ 0 ] = lowerName[ 0 ].lower();
    code += "Serializer::marshal( doc, method, \"" + (*it).name() + "\", " + mNameMapper.escape( lowerName ) + " );";
    code += "delete " + mNameMapper.escape( lowerName ) + ";";
  }

  code += "qDebug( \"%s\", doc.toString().latin1() );";
  code += transport.name() + "->query( doc.toString() );";
  callFunc.setBody( code );

  newClass.addFunction( callFunc );
}
QString Converter::listTypeFor(const QString &itemTypeName, KODE::Class &newClass)
{
    if (itemTypeName == QLatin1String("QString")) {
        newClass.addHeaderInclude("QtCore/QStringList");
        return "QStringList";
    }
    return "QList< " + itemTypeName + " >";
}
Exemple #5
0
static KODE::Code createRangeCheckCode( const XSD::SimpleType *type, const QString &variableName, KODE::Class &parentClass )
{
    KODE::Code code;
    code += "bool rangeOk = true;";
    code.newLine();

    // TODO
    /*
      WhiteSpaceType facetWhiteSpace() const;
      int facetTotalDigits() const;
      int facetFractionDigits() const;
    */

    if ( type->facetType() & XSD::SimpleType::MININC )
        code += "rangeOk = rangeOk && (" + variableName + " >= " + QString::number( type->facetMinimumInclusive() ) + ");";
    if ( type->facetType() & XSD::SimpleType::MINEX )
        code += "rangeOk = rangeOk && (" + variableName + " > " + QString::number( type->facetMinimumExclusive() ) + ");";
    if ( type->facetType() & XSD::SimpleType::MAXINC )
        code += "rangeOk = rangeOk && (" + variableName + " <= " + QString::number( type->facetMaximumInclusive() ) + ");";
    if ( type->facetType() & XSD::SimpleType::MINEX )
        code += "rangeOk = rangeOk && (" + variableName + " < " + QString::number( type->facetMaximumExclusive() ) + ");";

    if ( type->facetType() & XSD::SimpleType::LENGTH )
        code += "rangeOk = rangeOk && (" + variableName + ".length() == " + QString::number( type->facetLength() ) + ");";
    if ( type->facetType() & XSD::SimpleType::MINLEN )
        code += "rangeOk = rangeOk && (" + variableName + ".length() >= " + QString::number( type->facetMinimumLength() ) + ");";
    if ( type->facetType() & XSD::SimpleType::MAXLEN )
        code += "rangeOk = rangeOk && (" + variableName + ".length() <= " + QString::number( type->facetMaximumLength() ) + ");";
    if ( type->facetType() & XSD::SimpleType::PATTERN ) {
        code += "QRegExp exp( \"" + type->facetPattern() + "\" );";
        code += "rangeOk = rangeOk && exp.exactMatch( " + variableName + " );";

        parentClass.addInclude( "QRegExp" );
    }

    return code;
}
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);
    }
Exemple #7
0
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::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::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()));
    }