blob: b1f361d9b46fe2480a1bee6bafa6a911556f3fa2 [file] [log] [blame]
/*
service.cpp
Service structures.
--------------------------------------------------------------------------------
gSOAP XML Web services tools
Copyright (C) 2001-2005, Robert van Engelen, Genivia Inc. All Rights Reserved.
This part of the software is released under one of the following licenses:
GPL or Genivia's license for commercial use.
--------------------------------------------------------------------------------
GPL license.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
Author contact information:
engelen@genivia.com / engelen@acm.org
--------------------------------------------------------------------------------
A commercial use license is available from Genivia, Inc., contact@genivia.com
--------------------------------------------------------------------------------
TODO: consider adding support for non-SOAP HTTP operations
add headerfault output definitions
*/
#include "types.h"
#include "service.h"
static void comment(const char *start, const char *middle, const char *end, const char *text);
static void page(const char *page, const char *title, const char *text);
static void section(const char *section, const char *title, const char *text);
static void banner(const char*);
static void ident();
static void text(const char*);
////////////////////////////////////////////////////////////////////////////////
//
// Definitions methods
//
////////////////////////////////////////////////////////////////////////////////
Definitions::Definitions()
{ }
void Definitions::collect(const wsdl__definitions &definitions)
{ // Collect information: analyze WSDL definitions and imported definitions
analyze(definitions);
for (vector<wsdl__import>::const_iterator import = definitions.import.begin(); import != definitions.import.end(); ++import)
if ((*import).definitionsPtr())
analyze(*(*import).definitionsPtr());
}
void Definitions::analyze(const wsdl__definitions &definitions)
{ // Analyze WSDL and build Service information
for (vector<wsdl__binding>::const_iterator binding = definitions.binding.begin(); binding != definitions.binding.end(); ++binding)
{ // /definitions/binding/documentation
const char *binding_documentation = (*binding).documentation;
// /definitions/binding/soap:binding
soap__binding *soap__binding_ = (*binding).soap__binding_;
// /definitions/binding/soap:binding/@transport
const char *soap__binding_transport = NULL;
if (soap__binding_)
soap__binding_transport = soap__binding_->transport;
// /definitions/binding/soap:binding/@style
soap__styleChoice soap__binding_style = rpc;
if (soap__binding_ && soap__binding_->style)
soap__binding_style = *soap__binding_->style;
// /definitions/binding/http:binding
http__binding *http__binding_ = (*binding).http__binding_;
const char *http__binding_verb = NULL;
if (http__binding_)
http__binding_verb = http__binding_->verb;
// /definitions/binding/operation*
for (vector<wsdl__binding_operation>::const_iterator operation = (*binding).operation.begin(); operation != (*binding).operation.end(); ++operation)
{ // /definitions/portType/operation/ associated with /definitions/binding/operation
wsdl__operation *wsdl__operation_ = (*operation).operationPtr();
// /definitions/binding/operation/soap:operation
soap__operation *soap__operation_ = (*operation).soap__operation_;
// /definitions/binding/operation/soap:operation/@style
soap__styleChoice soap__operation_style = soap__binding_style;
if (soap__operation_ && soap__operation_->style)
soap__operation_style = *soap__operation_->style;
// /definitions/binding/operation/http:operation
http__operation *http__operation_ = (*operation).http__operation_;
// /definitions/binding/operation/http:operation/@location
const char *http__operation_location = NULL;
if (http__operation_)
http__operation_location = http__operation_->location;
// /definitions/binding/operation/input
wsdl__ext_input *ext_input = (*operation).input;
// /definitions/binding/operation/output
wsdl__ext_output *ext_output = (*operation).output;
// /definitions/portType/operation
if (wsdl__operation_)
{ wsdl__input *input = wsdl__operation_->input;
wsdl__output *output = wsdl__operation_->output;
if (http__operation_)
{ // TODO: HTTP operation
}
else if (input && ext_input)
{ soap__body *input_body = ext_input->soap__body_;
if (ext_input->mime__multipartRelated_)
{ for (vector<mime__part>::const_iterator part = ext_input->mime__multipartRelated_->part.begin(); part != ext_input->mime__multipartRelated_->part.end(); ++part)
if ((*part).soap__body_)
{ input_body = (*part).soap__body_;
break;
}
}
// MUST have an input, otherwise can't generate a service operation
if (input_body)
{ const char *URI;
if (soap__operation_style == rpc)
URI = input_body->namespace_;
else
URI = definitions.targetNamespace;
if (URI)
{ const char *prefix = types.nsprefix(NULL, URI);
const char *name = types.aname(NULL, NULL, (*binding).name); // name of service is binding name
Service *s = services[prefix];
if (!s)
{ s = services[prefix] = new Service();
s->prefix = prefix;
s->URI = URI;
s->name = name;
s->transport = soap__binding_transport;
if ((*binding).portTypePtr())
s->type = types.aname(NULL, NULL, (*binding).portTypePtr()->name);
else
s->type = NULL;
}
for (vector<wsdl__service>::const_iterator service = definitions.service.begin(); service != definitions.service.end(); ++service)
{ for (vector<wsdl__port>::const_iterator port = (*service).port.begin(); port != (*service).port.end(); ++port)
{ if ((*port).bindingPtr() == &(*binding))
{ if ((*port).soap__address_)
s->location.insert((*port).soap__address_->location);
// TODO: HTTP address for HTTP operations
// if ((*port).http__address_)
// http__address_location = http__address_->location;
if ((*service).documentation)
s->service_documentation[(*service).name] = (*service).documentation;
if ((*port).documentation && (*port).name)
s->port_documentation[(*port).name] = (*port).documentation;
if (binding_documentation)
s->binding_documentation[(*binding).name] = binding_documentation;
}
}
}
Operation *o = new Operation();
o->name = types.aname(NULL, NULL, wsdl__operation_->name);
o->prefix = prefix;
o->URI = URI;
o->style = soap__operation_style;
o->documentation = wsdl__operation_->documentation;
o->operation_documentation = (*operation).documentation;
o->parameterOrder = wsdl__operation_->parameterOrder;
if ((*operation).soap__operation_)
o->soapAction = (*operation).soap__operation_->soapAction;
else
{ o->soapAction = "";
// determine if we use SOAP 1.2 in which case soapAction is absent, this is a bit of a hack due to the lack of WSDL1.1/SOAP1.2 support and better alternatives
for (Namespace *p = definitions.soap->local_namespaces; p && p->id; p++)
{ if (p->out && !strcmp(p->id, "soap") && !strcmp(p->out, "http://schemas.xmlsoap.org/wsdl/soap12/"))
{ o->soapAction = NULL;
break;
}
}
}
o->input = new Message();
o->input->name = (*operation).name; // RPC uses operation/@name
if (soap__operation_style == rpc && !input_body->namespace_)
{ o->input->URI = "";
fprintf(stderr, "Error: no soap:body namespace attribute\n");
}
else
o->input->URI = input_body->namespace_;
o->input->use = input_body->use;
o->input->encodingStyle = input_body->encodingStyle;
o->input->message = input->messagePtr();
o->input->part = NULL;
o->input->multipartRelated = ext_input->mime__multipartRelated_;
o->input->content = NULL;
if (ext_input->mime__multipartRelated_ && !ext_input->mime__multipartRelated_->part.empty())
o->input->header = ext_input->mime__multipartRelated_->part.front().soap__header_;
else
o->input->header = ext_input->soap__header_;
if (ext_input->mime__multipartRelated_ && !ext_input->mime__multipartRelated_->part.empty() && ext_input->mime__multipartRelated_->part.front().soap__body_)
o->input->body_parts = ext_input->mime__multipartRelated_->part.front().soap__body_->parts;
else
o->input->body_parts = input_body->parts;
if (ext_input->dime__message_)
o->input->layout = ext_input->dime__message_->layout;
else
o->input->layout = NULL;
o->input->documentation = input->documentation;
o->input->ext_documentation = ext_input->documentation;
if (soap__operation_style == document)
o->input_name = types.oname("__", o->URI, o->input->name);
else
o->input_name = types.oname(NULL, o->input->URI, o->input->name);
if (output && ext_output)
{ soap__body *output_body = ext_output->soap__body_;
if (ext_output->mime__multipartRelated_)
{ for (vector<mime__part>::const_iterator part = ext_output->mime__multipartRelated_->part.begin(); part != ext_output->mime__multipartRelated_->part.end(); ++part)
if ((*part).soap__body_)
{ output_body = (*part).soap__body_;
break;
}
}
if (ext_output->mime__content_)
{ o->output = new Message();
o->output->name = NULL;
o->output->URI = NULL;
o->output->encodingStyle = NULL;
o->output->body_parts = NULL;
o->output->part = NULL;
o->output->multipartRelated = NULL;
o->output->content = ext_output->mime__content_;
o->output->message = output->messagePtr();
o->output->layout = NULL;
o->output->documentation = output->documentation;
o->output->ext_documentation = ext_output->documentation;
}
else if (output_body)
{ o->output = new Message();
o->output->name = (*operation).name; // RPC uses operation/@name with suffix 'Response' as set below
o->output->use = output_body->use;
// the code below is a hack around the RPC encoded response message element tag mismatch with Axis:
if (!output_body->namespace_ || output_body->use == encoded)
o->output->URI = o->input->URI; // encoded seems (?) to require the request's namespace
else
o->output->URI = output_body->namespace_;
o->output->encodingStyle = output_body->encodingStyle;
o->output->message = output->messagePtr();
o->output->part = NULL;
o->output->multipartRelated = ext_output->mime__multipartRelated_;
o->output->content = NULL;
if (ext_output->mime__multipartRelated_ && !ext_output->mime__multipartRelated_->part.empty())
o->output->header = ext_output->mime__multipartRelated_->part.front().soap__header_;
else
o->output->header = ext_output->soap__header_;
if (ext_output->mime__multipartRelated_ && !ext_output->mime__multipartRelated_->part.empty() && ext_output->mime__multipartRelated_->part.front().soap__body_)
o->output->body_parts = ext_output->mime__multipartRelated_->part.front().soap__body_->parts;
else
o->output->body_parts = output_body->parts;
if (ext_output->dime__message_)
o->output->layout = ext_output->dime__message_->layout;
else
o->output->layout = NULL;
o->output->documentation = output->documentation;
o->output->ext_documentation = ext_output->documentation;
char *s = (char*)soap_malloc(definitions.soap, strlen(o->output->name) + 9);
strcpy(s, o->output->name);
strcat(s, "Response");
if (soap__operation_style == document)
o->output_name = types.oname("__", o->URI, s);
else
o->output_name = types.oname(NULL, o->output->URI, s);
}
}
else
{ o->output_name = NULL;
o->output = NULL;
}
// collect input headers and headerfaults
if (ext_input)
{ const vector<soap__header> *soap__header_ = NULL;
// check if soap header is in mime:multipartRelated
if (ext_input->mime__multipartRelated_)
{ for (vector<mime__part>::const_iterator part = ext_input->mime__multipartRelated_->part.begin(); part != ext_input->mime__multipartRelated_->part.end(); ++part)
if (!(*part).soap__header_.empty())
{ soap__header_ = &(*part).soap__header_;
break;
}
}
if (!soap__header_)
soap__header_ = &ext_input->soap__header_;
for (vector<soap__header>::const_iterator header = soap__header_->begin(); header != soap__header_->end(); ++header)
{ Message *h = new Message();
h->message = (*header).messagePtr();
h->body_parts = NULL;
h->part = (*header).partPtr();
h->URI = (*header).namespace_;
if (h->part && h->part->element)
h->name = types.aname(NULL, NULL, h->part->element);
else if (h->URI && h->part && h->part->name && h->part->type)
h->name = types.aname(NULL, h->URI, h->part->name);
else
{ fprintf(stderr, "Error in SOAP Header part definition: input part '%s' missing?\n", h->part && h->part->name ? h->part->name : "?");
h->name = "";
}
h->encodingStyle = (*header).encodingStyle;
h->use = (*header).use;
h->multipartRelated = NULL;
h->content = NULL;
h->layout = NULL;
h->ext_documentation = NULL; // TODO: add document content
h->documentation = NULL; // TODO: add document content
s->header[h->name] = h;
for (vector<soap__headerfault>::const_iterator headerfault = (*header).headerfault.begin(); headerfault != (*header).headerfault.end(); ++headerfault)
{ // TODO: complete headerfault processing. This is rarely used.
}
}
}
// collect output headers and headerfaults
if (ext_output)
{ const vector<soap__header> *soap__header_ = NULL;
// check if soap header is in mime:multipartRelated
if (ext_output->mime__multipartRelated_)
{ for (vector<mime__part>::const_iterator part = ext_output->mime__multipartRelated_->part.begin(); part != ext_output->mime__multipartRelated_->part.end(); ++part)
if (!(*part).soap__header_.empty())
{ soap__header_ = &(*part).soap__header_;
break;
}
}
if (!soap__header_)
soap__header_ = &ext_output->soap__header_;
for (vector<soap__header>::const_iterator header = soap__header_->begin(); header != soap__header_->end(); ++header)
{ Message *h = new Message();
h->message = (*header).messagePtr();
h->body_parts = NULL;
h->part = (*header).partPtr();
h->URI = (*header).namespace_;
if (h->part && h->part->element)
h->name = types.aname(NULL, NULL, h->part->element);
else if (h->URI && h->part && h->part->name && h->part->type)
h->name = types.aname(NULL, h->URI, h->part->name);
else
{ fprintf(stderr, "Error in SOAP Header part definition: output part '%s' missing?\n", h->part && h->part->name ? h->part->name : "?");
h->name = "";
}
h->encodingStyle = (*header).encodingStyle;
h->use = (*header).use;
h->multipartRelated = NULL;
h->content = NULL;
h->layout = NULL;
h->ext_documentation = NULL; // TODO: add document content
h->documentation = NULL; // TODO: add document content
s->header[h->name] = h;
for (vector<soap__headerfault>::const_iterator headerfault = (*header).headerfault.begin(); headerfault != (*header).headerfault.end(); ++headerfault)
{ // TODO: complete headerfault processing. This is rarely used.
}
}
}
// collect faults
for (vector<wsdl__ext_fault>::const_iterator ext_fault = (*operation).fault.begin(); ext_fault != (*operation).fault.end(); ++ext_fault)
{ if ((*ext_fault).soap__fault_ && (*ext_fault).messagePtr())
{ Message *f = new Message();
f->message = (*ext_fault).messagePtr();
f->body_parts = NULL;
f->part = NULL;
f->encodingStyle = (*ext_fault).soap__fault_->encodingStyle;
f->URI = (*ext_fault).soap__fault_->namespace_;
f->use = (*ext_fault).soap__fault_->use;
f->multipartRelated = NULL;
f->content = NULL;
f->layout = NULL;
f->ext_documentation = (*ext_fault).documentation;
f->name = types.aname("_", f->URI, f->message->name);
f->documentation = f->message->documentation;
o->fault.push_back(f);
s->fault[f->name] = f;
}
else
fprintf(stderr, "Error: no wsdl:definitions/binding/operation/fault/soap:fault\n");
}
s->operation.push_back(o);
}
else
fprintf(stderr, "Warning: no SOAP RPC operation namespace, operations will be ignored\n");
}
else
fprintf(stderr, "Error: no wsdl:definitions/binding/operation/input/soap:body\n");
}
else
fprintf(stderr, "Error: no wsdl:definitions/portType/operation/input\n");
}
else
fprintf(stderr, "Error: no wsdl:definitions/portType/operation\n");
}
}
}
void Definitions::compile(const wsdl__definitions& definitions)
{ // compile the definitions and generate gSOAP header file
const char *defs;
if (definitions.name)
defs = types.aname(NULL, NULL, definitions.name);
else
defs = "Service";
ident();
fprintf(stream, "/* NOTE:\n\n - Compile this file with soapcpp2 to complete the code generation process.\n - Use soapcpp2 option -I to specify paths for #import\n To build with STL, 'stlvector.h' is imported from 'import' dir in package.\n - Use wsdl2h options -c and -s to generate pure C code or C++ code without STL.\n - Use 'typemap.dat' to control schema namespace bindings and type mappings.\n It is strongly recommended to customize the names of the namespace prefixes\n generated by wsdl2h. To do so, modify the prefix bindings in the Namespaces\n section below and add the modified lines to 'typemap.dat' to rerun wsdl2h.\n - Use Doxygen (www.doxygen.org) to browse this file.\n - Use wsdl2h option -l to view the software license terms.\n\n*/\n");
banner(definitions.targetNamespace?definitions.targetNamespace:"targetNamespace");
// copy documentation from WSDL definitions
if (definitions.documentation)
{ fprintf(stream, "/* WSDL Documentation:\n\n");
text(definitions.documentation);
fprintf(stream, "*/\n\n");
}
if (lflag)
{ banner("License");
fprintf(stream, "/*\n%s*/\n\n", licensenotice);
}
// gsoap compiler options: 'w' disables WSDL/schema output to avoid file collisions
if (cflag)
fprintf(stream, "//gsoapopt cw\n");
else
fprintf(stream, "//gsoapopt w\n");
banner("Import");
if (dflag)
{ if (import_path)
fprintf(stream, "#import \"%s/dom.h\"\n", import_path);
else
fprintf(stream, "#import \"dom.h\"\n");
}
if (!cflag && !sflag)
{ if (import_path)
fprintf(stream, "#import \"%s/stlvector.h\"\n", import_path);
else
fprintf(stream, "#import \"stlvector.h\"\n");
}
if (mflag)
{ if (import_path)
fprintf(stream, "#import \"%s/", import_path);
else
fprintf(stream, "#import \"");
fprintf(stream, "xsd.h\"\t// import primitive XSD types.\n", import_path);
}
for (SetOfString::const_iterator u = exturis.begin(); u != exturis.end(); ++u)
{ bool found = false;
size_t n = strlen(*u);
for (SetOfString::const_iterator i = definitions.builtinTypes().begin(); i != definitions.builtinTypes().end(); ++i)
{ if (**i == '"' && !strncmp(*u, *i + 1, n))
{ found = true;
break;
}
}
if (!found)
for (SetOfString::const_iterator j = definitions.builtinElements().begin(); j != definitions.builtinElements().end(); ++j)
{ if (**j == '"' && !strncmp(*u, *j + 1, n))
{ found = true;
break;
}
}
if (!found)
for (SetOfString::const_iterator k = definitions.builtinAttributes().begin(); k != definitions.builtinAttributes().end(); ++k)
{ if (**k == '"' && !strncmp(*u, *k + 1, n))
{ found = true;
break;
}
}
if (found)
{ if (import_path)
fprintf(stream, "#import \"%s/%s.h\"\t// %s = <%s>\n", import_path, types.nsprefix(NULL, *u), types.nsprefix(NULL, *u), *u);
else
fprintf(stream, "#import \"%s.h\"\t// %s = <%s>\n", types.nsprefix(NULL, *u), types.nsprefix(NULL, *u), *u);
}
}
banner("Schema Namespaces");
// determine if we must use SOAP 1.2, this is a bit of a hack due to the lack of WSDL1.1/SOAP1.2 support and better alternatives
for (Namespace *p = definitions.soap->local_namespaces; p && p->id; p++)
{ // p->out is set to the actual namespace name that matches the p->in pattern
if (p->out && !strcmp(p->id, "soap") && !strcmp(p->out, "http://schemas.xmlsoap.org/wsdl/soap12/"))
{ fprintf(stream, "// This service uses SOAP 1.2 namespaces:\n");
fprintf(stream, schemaformat, "SOAP-ENV", "namespace", "http://www.w3.org/2003/05/soap-envelope");
fprintf(stream, schemaformat, "SOAP-ENC", "namespace", "http://www.w3.org/2003/05/soap-encoding");
break;
}
}
if (definitions.types)
{ fprintf(stream, "\n/* NOTE:\n\nIt is strongly recommended to customize the names of the namespace prefixes\ngenerated by wsdl2h. To do so, modify the prefix bindings below and add the\nmodified lines to typemap.dat to rerun wsdl2h:\n\n");
if (definitions.targetNamespace && *definitions.targetNamespace)
fprintf(stream, "%s = \"%s\"\n", types.nsprefix(service_prefix, definitions.targetNamespace), definitions.targetNamespace);
for (vector<xs__schema*>::const_iterator schema1 = definitions.types->xs__schema_.begin(); schema1 != definitions.types->xs__schema_.end(); ++schema1)
if (!definitions.targetNamespace || strcmp((*schema1)->targetNamespace, definitions.targetNamespace))
fprintf(stream, "%s = \"%s\"\n", types.nsprefix(NULL, (*schema1)->targetNamespace), (*schema1)->targetNamespace);
fprintf(stream, "\n*/\n\n");
for (vector<xs__schema*>::const_iterator schema2 = definitions.types->xs__schema_.begin(); schema2 != definitions.types->xs__schema_.end(); ++schema2)
fprintf(stream, schemaformat, types.nsprefix(NULL, (*schema2)->targetNamespace), "namespace", (*schema2)->targetNamespace);
for (vector<xs__schema*>::const_iterator schema3 = definitions.types->xs__schema_.begin(); schema3 != definitions.types->xs__schema_.end(); ++schema3)
{ if ((*schema3)->elementFormDefault == (*schema3)->attributeFormDefault)
fprintf(stream, schemaformat, types.nsprefix(NULL, (*schema3)->targetNamespace), "form", (*schema3)->elementFormDefault == qualified ? "qualified" : "unqualified");
else
{ fprintf(stream, schemaformat, types.nsprefix(NULL, (*schema3)->targetNamespace), "elementForm", (*schema3)->elementFormDefault == qualified ? "qualified" : "unqualified");
fprintf(stream, schemaformat, types.nsprefix(NULL, (*schema3)->targetNamespace), "attributeForm", (*schema3)->attributeFormDefault == qualified ? "qualified" : "unqualified");
}
}
}
banner("Schema Types");
// generate the prototypes first: these should allow use before def, e.g. class names then generate the defs
// check if xsd:anyType is used
if (!cflag && !pflag)
{ for (SetOfString::const_iterator i = definitions.builtinTypes().begin(); i != definitions.builtinTypes().end(); ++i)
{ if (!cflag && !strcmp(*i, "xs:anyType"))
{ pflag = 1;
break;
}
}
}
// define xsd:anyType first, if used
if (!cflag && pflag)
{ const char *s, *t;
t = types.cname(NULL, NULL, "xs:anyType");
s = types.deftypemap[t];
if (s)
{ if (*s)
{ if (!mflag)
fprintf(stream, "%s\n", s);
}
s = types.usetypemap[t];
if (s)
{ if (mflag)
fprintf(stream, "// xsd.h must define type: %s\n", s);
types.knames.insert(s);
}
}
else
{ fprintf(stderr, "Error: no xsd__anyType defined in type map\n");
pflag = 0;
}
}
// produce built-in primitive types, limited to the ones that are used only
for (SetOfString::const_iterator i = definitions.builtinTypes().begin(); i != definitions.builtinTypes().end(); ++i)
{ const char *s, *t;
if (!cflag && pflag && !strcmp(*i, "xs:anyType"))
continue;
t = types.cname(NULL, NULL, *i);
s = types.deftypemap[t];
if (s)
{ if (*s)
{ if (**i == '"')
fprintf(stream, "\n/// Imported type %s from typemap %s.\n", *i, mapfile?mapfile:"");
else
fprintf(stream, "\n/// Built-in type \"%s\".\n", *i);
if (mflag)
fprintf(stream, "// (declaration removed by option -m) ");
types.format(s);
}
s = types.usetypemap[t];
if (s && *s)
{ if (mflag && **i != '"')
fprintf(stream, "\n// xsd.h must define type: %s\n", s);
if (types.knames.find(s) == types.knames.end())
types.knames.insert(s);
}
}
else
{ s = types.tname(NULL, NULL, "xsd:string");
if (!mflag)
{ if (**i == '"')
fprintf(stream, "\n// Imported type %s defined by %s\n", *i, t);
else
{ fprintf(stream, "\n/// Primitive built-in type \"%s\"\n", *i);
fprintf(stream, "typedef %s %s;\n", s, t);
}
}
else if (**i == '"')
fprintf(stream, "\n// Imported type %s defined by %s\n", *i, t);
else
fprintf(stream, "\n// xsd.h must define type: %s\n", t);
types.deftname(TYPEDEF, NULL, strchr(s, '*') != NULL, NULL, NULL, *i);
}
if (pflag && !strncmp(*i, "xs:", 3)) // only xsi types are polymorph
{ s = types.aname(NULL, NULL, *i);
if (!mflag)
{ fprintf(stream, "\n/// Class wrapper for built-in type \"%s\" derived from xsd__anyType\n", *i);
fprintf(stream, "class %s : public xsd__anyType\n{ public:\n", s);
fprintf(stream, elementformat, types.tname(NULL, NULL, *i), "__item;");
fprintf(stream, "\n};\n");
}
types.knames.insert(s);
}
}
// produce built-in primitive elements, limited to the ones that are used only
for (SetOfString::const_iterator j = definitions.builtinElements().begin(); j != definitions.builtinElements().end(); ++j)
{ const char *s, *t;
t = types.cname("_", NULL, *j);
s = types.deftypemap[t];
if (s)
{ if (*s)
{ if (**j == '"')
fprintf(stream, "\n/// Imported element %s from typemap %s.\n", *j, mapfile?mapfile:"");
else
fprintf(stream, "\n/// Built-in element \"%s\".\n", *j);
if (mflag)
fprintf(stream, "// (declaration removed by option -m) ");
types.format(s);
}
s = types.usetypemap[t];
if (s && *s)
{ if (mflag && **j != '"')
fprintf(stream, "\n// xsd.h must define element: %s\n", s);
if (types.knames.find(s) == types.knames.end())
types.knames.insert(s);
}
}
else
{ if (!mflag)
{ if (**j == '"')
fprintf(stream, "\n// Imported element %s declared as %s\n", *j, t);
else
{ fprintf(stream, "\n/// Built-in element \"%s\".\n", *j);
fprintf(stream, "typedef _XML %s;\n", t);
}
}
else if (**j == '"')
fprintf(stream, "\n// Imported element %s declared as %s\n", *j, t);
else
fprintf(stream, "\n// xsd.h must define element: %s\n", t);
types.deftname(TYPEDEF, NULL, true, NULL, NULL, *j); // already pointer
}
}
// produce built-in primitive attributes, limited to the ones that are used only
for (SetOfString::const_iterator k = definitions.builtinAttributes().begin(); k != definitions.builtinAttributes().end(); ++k)
{ const char *s, *t;
t = types.cname("_", NULL, *k);
s = types.deftypemap[t];
if (s)
{ if (*s)
{ if (**k == '"')
fprintf(stream, "\n/// Imported attribute %s from typemap %s.\n", *k, mapfile?mapfile:"");
else
fprintf(stream, "\n/// Built-in attribute \"%s\".\n", *k);
if (mflag)
fprintf(stream, "// (declaration removed by option -m) ");
types.format(s);
}
s = types.usetypemap[t];
if (s && *s)
{ if (mflag && **k != '"')
fprintf(stream, "\n// xsd.h must define attribute: %s\n", s);
if (types.knames.find(s) == types.knames.end())
types.knames.insert(s);
}
}
else
{ s = types.tname(NULL, NULL, "xsd:string");
if (!mflag)
{ if (**k == '"')
fprintf(stream, "\n// Imported attribute %s declared as %s\n", *k, t);
else
{ fprintf(stream, "\n/// Built-in attribute \"%s\".\n", *k);
fprintf(stream, "typedef %s %s;\n", s, t);
}
}
else if (**k == '"')
fprintf(stream, "// Imported attribute %s declared as %s\n", *k, t);
else
fprintf(stream, "// xsd.h must define attribute: %s\n", t);
types.deftname(TYPEDEF, NULL, strchr(s, '*') != NULL, NULL, NULL, *k);
}
}
// produce types
// define class/struct types first
if (definitions.types)
{ comment("Definitions", defs, "types", definitions.types->documentation);
fprintf(stream, "\n");
for (vector<xs__schema*>::const_iterator schema4 = definitions.types->xs__schema_.begin(); schema4 != definitions.types->xs__schema_.end(); ++schema4)
{ for (vector<xs__complexType>::const_iterator complexType = (*schema4)->complexType.begin(); complexType != (*schema4)->complexType.end(); ++complexType)
types.define((*schema4)->targetNamespace, NULL, *complexType);
for (vector<xs__element>::const_iterator element = (*schema4)->element.begin(); element != (*schema4)->element.end(); ++element)
{ if (!(*element).type)
{ if ((*element).complexTypePtr())
types.define((*schema4)->targetNamespace, (*element).name, *(*element).complexTypePtr());
else
{ fprintf(stream, "\n/// Element \"%s\":%s.\n", (*schema4)->targetNamespace, (*element).name);
if (gflag)
{ const char *t = types.deftname(TYPEDEF, NULL, false, "_", (*schema4)->targetNamespace, (*element).name);
fprintf(stream, "typedef _XML %s;\n", t);
}
else
{ const char *s = types.cname("_", (*schema4)->targetNamespace, (*element).name);
types.ptrtypemap[s] = types.usetypemap[s] = "_XML";
fprintf(stream, "/// Note: use wsdl2h option -g to generate this global element declaration.\n");
}
}
}
}
}
// visit types with lowest base level first
int baseLevel = 1;
bool found;
do
{ found = (baseLevel == 1);
for (vector<xs__schema*>::iterator schema = definitions.types->xs__schema_.begin(); schema != definitions.types->xs__schema_.end(); ++schema)
{ for (vector<xs__simpleType>::iterator simpleType = (*schema)->simpleType.begin(); simpleType != (*schema)->simpleType.end(); ++simpleType)
{ if ((*simpleType).baseLevel() == baseLevel)
{ found = true;
types.gen((*schema)->targetNamespace, NULL, *simpleType, false);
}
}
for (vector<xs__element>::iterator element = (*schema)->element.begin(); element != (*schema)->element.end(); ++element)
{ if (!(*element).type && (*element).simpleTypePtr() && (*element).simpleTypePtr()->baseLevel() == baseLevel)
{ found = true;
if ((*element).type)
fprintf(stream, "/// Element \"%s\":%s of simpleType %s.\n", (*schema)->targetNamespace, (*element).name, (*element).type);
types.document((*element).annotation);
types.gen((*schema)->targetNamespace, (*element).name, *(*element).simpleTypePtr(), false);
}
if (!(*element).type && (*element).complexTypePtr() && (*element).complexTypePtr()->baseLevel() == baseLevel)
found = true;
}
for (vector<xs__attribute>::const_iterator attribute = (*schema)->attribute.begin(); attribute != (*schema)->attribute.end(); ++attribute)
{ if (!(*attribute).type && (*attribute).simpleTypePtr() && (*attribute).simpleTypePtr()->baseLevel() == baseLevel)
{ found = true;
if ((*attribute).type)
fprintf(stream, "/// Attribute \"%s\":%s of simpleType %s.\n", (*schema)->targetNamespace, (*attribute).name, (*attribute).type);
types.document((*attribute).annotation);
types.gen((*schema)->targetNamespace, (*attribute).name, *(*attribute).simpleTypePtr(), false); // URI = NULL won't generate type in schema (type without namespace qualifier)
}
}
for (vector<xs__complexType>::iterator complexType = (*schema)->complexType.begin(); complexType != (*schema)->complexType.end(); ++complexType)
{ if ((*complexType).baseLevel() == baseLevel)
found = true;
}
}
++baseLevel;
} while (found);
// generate complex type defs. Problem: what if a simpleType restriction/extension depends on a complexType simpleContent restriction/extension?
int maxLevel = baseLevel;
for (baseLevel = 1; baseLevel < maxLevel; ++baseLevel)
{ for (vector<xs__schema*>::iterator schema = definitions.types->xs__schema_.begin(); schema != definitions.types->xs__schema_.end(); ++schema)
{ for (vector<xs__complexType>::iterator complexType = (*schema)->complexType.begin(); complexType != (*schema)->complexType.end(); ++complexType)
{ if ((*complexType).baseLevel() == baseLevel)
types.gen((*schema)->targetNamespace, NULL, *complexType, false);
}
for (vector<xs__element>::iterator element = (*schema)->element.begin(); element != (*schema)->element.end(); ++element)
{ if (!(*element).type && (*element).complexTypePtr() && (*element).complexTypePtr()->baseLevel() == baseLevel)
{ fprintf(stream, "\n\n/// Element \"%s\":%s of complexType.\n", (*schema)->targetNamespace, (*element).name);
types.document((*element).annotation);
types.gen((*schema)->targetNamespace, (*element).name, *(*element).complexTypePtr(), false);
}
}
}
}
// option to consider: generate local complexTypes iteratively
/*
for (MapOfStringToType::const_iterator local = types.locals.begin(); local != types.locals.end(); ++local)
{ types.gen(NULL, (*local).first, *(*local).second);
}
*/
for (vector<xs__schema*>::iterator schema = definitions.types->xs__schema_.begin(); schema != definitions.types->xs__schema_.end(); ++schema)
{ for (vector<xs__element>::iterator element = (*schema)->element.begin(); element != (*schema)->element.end(); ++element)
{ if ((*element).name && (*element).type)
{ fprintf(stream, "\n/// Element \"%s\":%s of type %s.\n", (*schema)->targetNamespace, (*element).name, (*element).type);
types.document((*element).annotation);
if (!types.is_defined("_", (*schema)->targetNamespace, (*element).name))
{ const char *s = types.tname(NULL, NULL, (*element).type);
const char *t = types.deftname(TYPEDEF, NULL, false, "_", (*schema)->targetNamespace, (*element).name);
if (gflag)
{ if (strncmp(s, "char", 4) && strchr(s, '*')) // don't want pointer typedef, unless char*
{ size_t n = strlen(s);
char *r = (char*)malloc(n);
strncpy(r, s, n - 1);
r[n - 1] = '\0';
fprintf(stream, "typedef %s %s;\n", r, t);
free(r);
}
else
fprintf(stream, "typedef %s %s;\n", s, t);
}
else
fprintf(stream, "/// Note: use wsdl2h option -g to generate this global element declaration.\n");
}
else
{ const char *s = types.cname("_", (*schema)->targetNamespace, (*element).name);
const char *t = types.deftypemap[s];
if (t && *t)
{ fprintf(stream, "/// Imported element %s from typemap %s.\n", s, mapfile?mapfile:"");
types.format(t);
}
else
fprintf(stream, "// '%s' element definition intentionally left blank.\n", types.cname("_", (*schema)->targetNamespace, (*element).name));
}
}
}
for (vector<xs__attribute>::iterator attribute = (*schema)->attribute.begin(); attribute != (*schema)->attribute.end(); ++attribute)
{ if ((*attribute).name && (*attribute).type)
{ fprintf(stream, "\n/// Attribute \"%s\":%s of simpleType %s.\n", (*schema)->targetNamespace, (*attribute).name, (*attribute).type);
types.document((*attribute).annotation);
if (!types.is_defined("_", (*schema)->targetNamespace, (*attribute).name))
{ const char *s = types.tname(NULL, NULL, (*attribute).type);
const char *t = types.deftname(TYPEDEF, NULL, false, "_", (*schema)->targetNamespace, (*attribute).name);
if (gflag)
{ if (strncmp(s, "char", 4) && strchr(s, '*')) // don't want pointer typedef, unless char*
{ size_t n = strlen(s);
char *r = (char*)malloc(n);
strncpy(r, s, n - 1);
r[n - 1] = '\0';
fprintf(stream, "typedef %s %s;\n", r, t);
free(r);
}
else
fprintf(stream, "typedef %s %s;\n", s, t);
}
else
fprintf(stream, "/// Note: use wsdl2h option -g to generate this global attribute declaration.\n");
}
else
{ const char *s = types.cname("_", (*schema)->targetNamespace, (*attribute).name);
const char *t = types.deftypemap[s];
if (t && *t)
{ fprintf(stream, "/// Imported attribute %s from typemap %s.\n", s, mapfile?mapfile:"");
types.format(t);
}
else
fprintf(stream, "// '%s' attribute definition intentionally left blank.\n", types.cname("_", (*schema)->targetNamespace, (*attribute).name));
}
}
}
}
}
collect(definitions);
if (!services.empty())
{ banner("Services");
for (MapOfStringToService::const_iterator service1 = services.begin(); service1 != services.end(); ++service1)
{ Service *sv = (*service1).second;
if (sv && sv->prefix)
{ fprintf(stream, "\n");
if (sv->name)
fprintf(stream, serviceformat, sv->prefix, "name", sv->name, "");
if (sv->type)
fprintf(stream, serviceformat, sv->prefix, "type", sv->type, "");
for (SetOfString::const_iterator port = sv->location.begin(); port != sv->location.end(); ++port)
fprintf(stream, serviceformat, sv->prefix, "port", (*port), "");
if (sv->URI)
fprintf(stream, serviceformat, sv->prefix, "namespace", sv->URI, "");
if (sv->transport)
fprintf(stream, serviceformat, sv->prefix, "transport", sv->transport, "");
}
}
fprintf(stream, "\n/** @mainpage %s Definitions\n", definitions.name?definitions.name:"Service");
if (definitions.documentation)
{ section(defs, "_documentation Documentation", NULL);
text(definitions.documentation);
}
if (definitions.types && definitions.types->documentation)
{ section(defs, "_types Types", NULL);
text(definitions.types->documentation);
}
section(defs, "_bindings Bindings", NULL);
for (MapOfStringToService::const_iterator service2 = services.begin(); service2 != services.end(); ++service2)
{ Service *sv = (*service2).second;
if (sv && sv->name)
fprintf(stream, " - @ref %s\n", sv->name);
}
fprintf(stream, "\n*/\n");
for (MapOfStringToService::const_iterator service3 = services.begin(); service3 != services.end(); ++service3)
{ Service *sv = (*service3).second;
if (sv && sv->name)
{ fprintf(stream, "\n/**\n");
page(sv->name, " Binding", sv->name);
for (MapOfStringToString::const_iterator service_doc = sv->service_documentation.begin(); service_doc != sv->service_documentation.end(); ++service_doc)
{ const char *name = types.aname(NULL, NULL, (*service_doc).first);
section(name, "_service Service Documentation", (*service_doc).first);
text((*service_doc).second);
}
for (MapOfStringToString::const_iterator port_doc = sv->port_documentation.begin(); port_doc != sv->port_documentation.end(); ++port_doc)
{ const char *name = types.aname(NULL, NULL, (*port_doc).first);
section(name, "_port Port Documentation", (*port_doc).first);
text((*port_doc).second);
}
for (MapOfStringToString::const_iterator binding_doc = sv->binding_documentation.begin(); binding_doc != sv->binding_documentation.end(); ++binding_doc)
{ const char *name = types.aname(NULL, NULL, (*binding_doc).first);
section(name, "_binding Binding Documentation", (*binding_doc).first);
text((*binding_doc).second);
}
section(sv->name, "_operations Operations of Binding ", sv->name);
for (vector<Operation*>::const_iterator op = sv->operation.begin(); op != sv->operation.end(); ++op)
{ if (*op && (*op)->input_name)
fprintf(stream, " - @ref %s\n", (*op)->input_name);
}
section((*sv).name, "_ports Endpoints of Binding ", sv->name);
for (SetOfString::const_iterator port = sv->location.begin(); port != sv->location.end(); ++port)
fprintf(stream, " - %s\n", *port);
fprintf(stream, "\n*/\n");
}
}
}
generate();
fprintf(stream, "\n/* End of %s */\n", outfile?outfile:"file");
}
void Definitions::generate()
{ MapOfStringToMessage headers;
MapOfStringToMessage faults;
for (MapOfStringToService::const_iterator service1 = services.begin(); service1 != services.end(); ++service1)
{ if ((*service1).second)
{ for (MapOfStringToMessage::const_iterator header = (*service1).second->header.begin(); header != (*service1).second->header.end(); ++header)
headers[(*header).first] = (*header).second;
for (MapOfStringToMessage::const_iterator fault = (*service1).second->fault.begin(); fault != (*service1).second->fault.end(); ++fault)
faults[(*fault).first] = (*fault).second;
}
}
// Generate SOAP Header definition
if (!headers.empty())
{ banner("SOAP Header");
fprintf(stream, "/**\n\nThe SOAP Header is part of the gSOAP context and its content is accessed\nthrough the soap.header variable. You may have to set the soap.actor variable\nto serialize SOAP Headers with SOAP-ENV:actor or SOAP-ENV:role attributes.\n\n*/\n");
fprintf(stream, "struct SOAP_ENV__Header\n{\n");
for (MapOfStringToMessage::const_iterator header = headers.begin(); header != headers.end(); ++header)
{ if ((*header).second->URI && !types.uris[(*header).second->URI])
fprintf(stream, schemaformat, types.nsprefix(NULL, (*header).second->URI), "namespace", (*header).second->URI);
comment("Header", (*header).first, "WSDL", (*header).second->ext_documentation);
comment("Header", (*header).first, "SOAP", (*header).second->documentation);
if ((*header).second->part && (*header).second->part->elementPtr())
{ fprintf(stream, "/// \"%s\" SOAP Header part element\n", (*header).second->part->name);
types.gen(NULL, *(*header).second->part->elementPtr());
}
else if ((*header).second->part && (*header).second->part->type)
{ fprintf(stream, elementformat, "mustUnderstand", "// must be understood by receiver");
fprintf(stream, "\n");
fprintf(stream, elementformat, types.pname(true, NULL, NULL, (*header).second->part->type), (*header).first);
fprintf(stream, ";\n");
}
else
{ fprintf(stream, elementformat, "mustUnderstand", "// must be understood by receiver");
fprintf(stream, "\n");
if ((*header).second->part && (*header).second->part->element)
fprintf(stream, pointerformat, types.pname(true, NULL, NULL, (*header).second->part->element), (*header).first);
else
fprintf(stream, pointerformat, (*header).first, (*header).first);
fprintf(stream, ";\t///< TODO: Check element type (imported type)\n");
}
}
fprintf(stream, "\n};\n");
}
// Generate Fault detail element definitions
for (MapOfStringToMessage::const_iterator fault = faults.begin(); fault != faults.end(); ++fault)
{ banner("SOAP Fault Detail");
fprintf(stream, "/// The SOAP Fault Detail element contains one of the following types serialized\n// in the __type and fault fields of the SOAP_ENV__Detail struct (see docs)\n");
if ((*fault).second->URI && !types.uris[(*fault).second->URI])
fprintf(stream, schemaformat, types.nsprefix(NULL, (*fault).second->URI), "namespace", (*fault).second->URI);
comment("Fault", (*fault).first, "WSDL", (*fault).second->ext_documentation);
comment("Fault", (*fault).first, "SOAP", (*fault).second->documentation);
if (cflag)
fprintf(stream, "struct %s\n{\n", (*fault).first);
else
fprintf(stream, "class %s\n{ public:", (*fault).first);
(*fault).second->generate(types, ";", false, true, false);
if (!cflag)
{ fprintf(stream, "\n");
fprintf(stream, pointerformat, "struct soap", "soap");
fprintf(stream, ";");
}
fprintf(stream, "\n};\n");
}
/* The SOAP Fault struct below is autogenerated by soapcpp2 (kept here for future mods)
if (!mflag && !faults.empty())
{ fprintf(stream, "struct SOAP_ENV__Code\n{\n");
fprintf(stream, elementformat, "_QName", "SOAP_ENV__Value");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "char", "SOAP_ENV__Node");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "char", "SOAP_ENV__Role");
fprintf(stream, ";\n};\n");
fprintf(stream, "struct SOAP_ENV__Detail\n{\n");
fprintf(stream, elementformat, "int", "__type");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "void", "fault");
fprintf(stream, ";\n");
fprintf(stream, elementformat, "_XML", "__any");
fprintf(stream, ";\n};\n");
fprintf(stream, "struct SOAP_ENV__Fault\n{\n");
fprintf(stream, elementformat, "_QName", "faultcode");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "char", "faultstring");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "char", "faultactor");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "struct SOAP_ENV__Detail", "detail");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "struct SOAP_ENV__Code", "SOAP_ENV__Code");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "char", "SOAP_ENV__Reason");
fprintf(stream, ";\n");
fprintf(stream, pointerformat, "struct SOAP_ENV__Detail", "SOAP_ENV__Detail");
fprintf(stream, ";\n};\n");
}
*/
for (MapOfStringToService::const_iterator service2 = services.begin(); service2 != services.end(); ++service2)
if ((*service2).second)
(*service2).second->generate(types);
}
////////////////////////////////////////////////////////////////////////////////
//
// Service methods
//
////////////////////////////////////////////////////////////////////////////////
Service::Service()
{ prefix = NULL;
URI = NULL;
name = NULL;
type = NULL;
transport = NULL;
}
void Service::generate(Types& types)
{ if (name)
banner(name);
for (vector<Operation*>::const_iterator op2 = operation.begin(); op2 != operation.end(); ++op2)
{ if (*op2 && (*op2)->input)
{ bool flag = false, anonymous = (*op2)->parameterOrder != NULL;
banner((*op2)->input_name);
if ((*op2)->output && (*op2)->output_name)
{ if ((*op2)->style == document)
flag = (*op2)->output->message && (*op2)->output->message->part.size() == 1;
else if (!wflag)
flag = (*op2)->output->message && (*op2)->output->use == encoded && (*op2)->output->message->part.size() == 1 && !(*(*op2)->output->message->part.begin()).simpleTypePtr() && !(*(*op2)->output->message->part.begin()).complexTypePtr();
if (flag && (*op2)->input->message && (*(*op2)->output->message->part.begin()).element)
for (vector<wsdl__part>::const_iterator part = (*op2)->input->message->part.begin(); part != (*op2)->input->message->part.end(); ++part)
if ((*part).element && !strcmp((*part).element, (*(*op2)->output->message->part.begin()).element))
flag = false;
if (!flag)
{ fprintf(stream, "/// Operation response struct \"%s\" of service binding \"%s\" operation \"%s\"\n", (*op2)->output_name, name, (*op2)->input_name);
fprintf(stream, "struct %s\n{", (*op2)->output_name);
(*op2)->output->generate(types, ";", anonymous, true, false);
fprintf(stream, "\n};\n");
}
}
fprintf(stream, "\n/// Operation \"%s\" of service binding \"%s\"\n\n/**\n\nOperation details:\n\n", (*op2)->input_name, name);
if ((*op2)->documentation)
text((*op2)->documentation);
if ((*op2)->operation_documentation)
text((*op2)->operation_documentation);
if ((*op2)->input->documentation)
{ fprintf(stream, "Input request:\n");
text((*op2)->input->documentation);
}
if ((*op2)->input->ext_documentation)
{ fprintf(stream, "Input request:\n");
text((*op2)->input->ext_documentation);
}
if ((*op2)->output)
{ if ((*op2)->output->documentation)
{ fprintf(stream, "Output response:\n");
text((*op2)->output->documentation);
}
if ((*op2)->output->ext_documentation)
{ fprintf(stream, "Output response:\n");
text((*op2)->output->ext_documentation);
}
}
if ((*op2)->output)
{ if ((*op2)->output->content)
{ fprintf(stream, " - Response has MIME content '%s'\n", ((*op2)->output->content->type?(*op2)->output->content->type:"?"));
fprintf(stream, " TODO: this form of MIME content response is not automatically handled.\n Use one-way request and implement code to parse response.\n");
}
}
else
fprintf(stream, " - One-way message\n");
if ((*op2)->style == document)
fprintf(stream, " - SOAP document/literal style\n");
else
{ if ((*op2)->input->use == literal)
fprintf(stream, " - SOAP RPC literal style\n");
else if ((*op2)->input->encodingStyle)
fprintf(stream, " - SOAP RPC encodingStyle=\"%s\"\n", (*op2)->input->encodingStyle);
else
fprintf(stream, " - SOAP RPC encoded\n");
}
if ((*op2)->output)
{ if ((*op2)->input->use != (*op2)->output->use)
{ if ((*op2)->output->use == literal)
fprintf(stream, " - SOAP RPC literal response\n");
else if ((*op2)->output->encodingStyle)
fprintf(stream, " - SOAP RPC response encodingStyle=\"%s\"\n", (*op2)->output->encodingStyle);
else
fprintf(stream, " - SOAP RPC encoded response\n");
}
}
if ((*op2)->soapAction)
{ if (*(*op2)->soapAction)
fprintf(stream, " - SOAP action=\"%s\"\n", (*op2)->soapAction);
}
for (vector<Message*>::const_iterator message = (*op2)->fault.begin(); message != (*op2)->fault.end(); ++message)
{ if ((*message)->message && (*message)->message->name)
fprintf(stream, " - SOAP Fault: %s\n", (*message)->name);
}
if (!(*op2)->input->header.empty())
fprintf(stream, " - Request message has mandatory header part(s):\n");
for (vector<soap__header>::const_iterator inputheader = (*op2)->input->header.begin(); inputheader != (*op2)->input->header.end(); ++inputheader)
{ if ((*inputheader).part)
{ if ((*inputheader).use == encoded && (*inputheader).namespace_)
fprintf(stream, " - %s\n", types.aname(NULL, (*inputheader).namespace_, (*inputheader).part));
else if ((*inputheader).partPtr() && (*inputheader).partPtr()->element)
fprintf(stream, " - %s\n", types.aname(NULL, NULL, (*inputheader).partPtr()->element));
}
}
if ((*op2)->input->multipartRelated)
{ int k = 2;
fprintf(stream, " - Request message has MIME multipart/related attachments:\n");
for (vector<mime__part>::const_iterator part = (*op2)->input->multipartRelated->part.begin(); part != (*op2)->input->multipartRelated->part.end(); ++part)
{ if ((*part).soap__body_)
{ fprintf(stream, " -# MIME attachment with SOAP Body and mandatory header part(s):\n");
for (vector<soap__header>::const_iterator header = (*part).soap__header_.begin(); header != (*part).soap__header_.end(); ++header)
{ if ((*header).part)
{ if ((*header).use == encoded && (*header).namespace_)
fprintf(stream, " - %s\n", types.aname(NULL, (*header).namespace_, (*header).part));
else if ((*header).partPtr() && (*header).partPtr()->element)
fprintf(stream, " - %s\n", types.aname(NULL, NULL, (*header).partPtr()->element));
}
}
}
else
{ fprintf(stream, " -# MIME attachment %d:\n", k++);
for (vector<mime__content>::const_iterator content = (*part).content.begin(); content != (*part).content.end(); ++content)
fprintf(stream, " - part=\"%s\" type=\"%s\"\n", (*content).part?(*content).part:"", (*content).type?(*content).type:"");
}
}
}
if ((*op2)->input->layout)
fprintf(stream, " - Request message has DIME attachments in compliance with %s\n", (*op2)->input->layout);
if ((*op2)->output)
{ if (!(*op2)->output->header.empty())
fprintf(stream, " - Response message has mandatory header part(s):\n");
for (vector<soap__header>::const_iterator outputheader = (*op2)->output->header.begin(); outputheader != (*op2)->output->header.end(); ++outputheader)
{ if ((*outputheader).part)
{ if ((*outputheader).use == encoded && (*outputheader).namespace_)
fprintf(stream, " - %s\n", types.aname(NULL, (*outputheader).namespace_, (*outputheader).part));
else if ((*outputheader).partPtr() && (*outputheader).partPtr()->element)
fprintf(stream, " - %s\n", types.aname(NULL, NULL, (*outputheader).partPtr()->element));
}
}
}
if ((*op2)->output && (*op2)->output_name && (*op2)->output->multipartRelated)
{ int k = 2;
fprintf(stream, " - Response message has MIME multipart/related attachments\n");
for (vector<mime__part>::const_iterator part = (*op2)->output->multipartRelated->part.begin(); part != (*op2)->output->multipartRelated->part.end(); ++part)
{ if ((*part).soap__body_)
{ fprintf(stream, " -# MIME attachment with SOAP Body and mandatory header part(s):\n");
for (vector<soap__header>::const_iterator header = (*part).soap__header_.begin(); header != (*part).soap__header_.end(); ++header)
{ if ((*header).part)
{ if ((*header).use == encoded && (*header).namespace_)
fprintf(stream, " - %s\n", types.aname(NULL, (*header).namespace_, (*header).part));
else if ((*header).partPtr() && (*header).partPtr()->element)
fprintf(stream, " - %s\n", types.aname(NULL, NULL, (*header).partPtr()->element));
}
}
}
else
{ fprintf(stream, " -# MIME attachment %d:\n", k++);
for (vector<mime__content>::const_iterator content = (*part).content.begin(); content != (*part).content.end(); ++content)
fprintf(stream, " - part=\"%s\" type=\"%s\"\n", (*content).part?(*content).part:"", (*content).type?(*content).type:"");
}
}
}
if ((*op2)->output && (*op2)->output_name && (*op2)->output->layout)
fprintf(stream, " - Response message has DIME attachments in compliance with %s\n", (*op2)->output->layout);
fprintf(stream, "\nC stub function (defined in soapClient.c[pp] after running soapcpp2):\n@code\n int soap_call_%s(struct soap *soap,\n NULL, // char *endpoint = NULL selects default endpoint for this operation\n NULL, // char *action = NULL selects default action for this operation\n // request parameters:", (*op2)->input_name);
(*op2)->input->generate(types, ",", false, false, false);
fprintf(stream, "\n // response parameters:");
if ((*op2)->output && (*op2)->output_name)
{ if (flag)
{ if ((*op2)->style == document)
{ // Shortcut: do not generate wrapper struct
(*op2)->output->generate(types, "", false, false, true);
}
else if ((*(*op2)->output->message->part.begin()).name)
{ fprintf(stream, "\n");
fprintf(stream, anonymous ? anonformat : paraformat, types.tname(NULL, NULL, (*(*op2)->output->message->part.begin()).type), cflag ? "*" : "&", types.aname(NULL, NULL, (*(*op2)->output->message->part.begin()).name), "");
}
}
else
fprintf(stream, "\n struct %s%s", (*op2)->output_name, cflag ? "*" : "&");
}
fprintf(stream, "\n );\n@endcode\n\n");
if (!cflag)
{ fprintf(stream, "C++ proxy class (defined in soap%sProxy.h):\n", name);
fprintf(stream, " class %s;\n\n", name);
}
fprintf(stream, "*/\n\n");
(*op2)->generate(types);
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// Operation methods
//
////////////////////////////////////////////////////////////////////////////////
void Operation::generate(Types &types)
{ bool flag = false, anonymous = parameterOrder != NULL;
const char *method_name = strstr(input_name + 1, "__") + 2;
if (!method_name)
method_name = input_name;
if (style == document)
fprintf(stream, serviceformat, prefix, "method-style", method_name, "document");
else
fprintf(stream, serviceformat, prefix, "method-style", method_name, "rpc");
if (input->use == literal)
fprintf(stream, serviceformat, prefix, "method-encoding", method_name, "literal");
else if (input->encodingStyle)
fprintf(stream, serviceformat, prefix, "method-encoding", method_name, input->encodingStyle);
else
fprintf(stream, serviceformat, prefix, "method-encoding", method_name, "encoded");
if (output)
{ if (input->use != output->use)
{ if (output->use == literal)
fprintf(stream, serviceformat, prefix, "method-response-encoding", method_name, "literal");
else if (output->encodingStyle)
fprintf(stream, serviceformat, prefix, "method-response-encoding", method_name, output->encodingStyle);
else
fprintf(stream, serviceformat, prefix, "method-response-encoding", method_name, "encoded");
}
if (style == rpc && input->URI && output->URI && strcmp(input->URI, output->URI))
fprintf(stream, schemaformat, types.nsprefix(NULL, output->URI), "namespace", output->URI);
}
if (soapAction)
{ if (*soapAction)
fprintf(stream, serviceformat, prefix, "method-action", method_name, soapAction);
else
fprintf(stream, serviceformat, prefix, "method-action", method_name, "\"\"");
}
for (vector<Message*>::const_iterator message = fault.begin(); message != fault.end(); ++message)
{ if ((*message)->message && (*message)->message->name)
fprintf(stream, serviceformat, prefix, "method-fault", method_name, (*message)->name);
}
if (input->multipartRelated)
{ for (vector<mime__part>::const_iterator inputmime = input->multipartRelated->part.begin(); inputmime != input->multipartRelated->part.end(); ++inputmime)
{ for (vector<soap__header>::const_iterator inputheader = (*inputmime).soap__header_.begin(); inputheader != (*inputmime).soap__header_.end(); ++inputheader)
{ if ((*inputheader).part)
{ if ((*inputheader).use == encoded && (*inputheader).namespace_)
fprintf(stream, serviceformat, prefix, "method-input-header-part", method_name, types.aname(NULL, (*inputheader).namespace_, (*inputheader).part));
else if ((*inputheader).partPtr() && (*inputheader).partPtr()->element)
fprintf(stream, serviceformat, prefix, "method-input-header-part", method_name, types.aname(NULL, NULL, (*inputheader).partPtr()->element));
}
}
for (vector<mime__content>::const_iterator content = (*inputmime).content.begin(); content != (*inputmime).content.end(); ++content)
if ((*content).type)
fprintf(stream, serviceformat, prefix, "method-input-mime-type", method_name, (*content).type);
}
}
// TODO: add headerfault directives
for (vector<soap__header>::const_iterator inputheader = input->header.begin(); inputheader != input->header.end(); ++inputheader)
{ if ((*inputheader).part)
{ if ((*inputheader).use == encoded && (*inputheader).namespace_)
fprintf(stream, serviceformat, prefix, "method-input-header-part", method_name, types.aname(NULL, (*inputheader).namespace_, (*inputheader).part));
else if ((*inputheader).partPtr() && (*inputheader).partPtr()->element)
fprintf(stream, serviceformat, prefix, "method-input-header-part", method_name, types.aname(NULL, NULL, (*inputheader).partPtr()->element));
}
}
if (output)
{ if (output->multipartRelated)
{ for (vector<mime__part>::const_iterator outputmime = output->multipartRelated->part.begin(); outputmime != output->multipartRelated->part.end(); ++outputmime)
{ for (vector<soap__header>::const_iterator outputheader = (*outputmime).soap__header_.begin(); outputheader != (*outputmime).soap__header_.end(); ++outputheader)
{ if ((*outputheader).part)
{ if ((*outputheader).use == encoded && (*outputheader).namespace_)
fprintf(stream, serviceformat, prefix, "method-output-header-part", method_name, types.aname(NULL, (*outputheader).namespace_, (*outputheader).part));
else if ((*outputheader).partPtr() && (*outputheader).partPtr()->element)
fprintf(stream, serviceformat, prefix, "method-output-header-part", method_name, types.aname(NULL, NULL, (*outputheader).partPtr()->element));
}
}
for (vector<mime__content>::const_iterator content = (*outputmime).content.begin(); content != (*outputmime).content.end(); ++content)
if ((*content).type)
fprintf(stream, serviceformat, prefix, "method-output-mime-type", method_name, (*content).type);
}
}
for (vector<soap__header>::const_iterator outputheader = output->header.begin(); outputheader != output->header.end(); ++outputheader)
{ if ((*outputheader).part)
{ if ((*outputheader).use == encoded && (*outputheader).namespace_)
fprintf(stream, serviceformat, prefix, "method-output-header-part", method_name, types.aname(NULL, (*outputheader).namespace_, (*outputheader).part));
else if ((*outputheader).partPtr() && (*outputheader).partPtr()->element)
fprintf(stream, serviceformat, prefix, "method-output-header-part", method_name, types.aname(NULL, NULL, (*outputheader).partPtr()->element));
}
}
}
if (output_name)
{ if (style == document)
flag = output->message && output->message->part.size() == 1;
else if (!wflag)
flag = output->message && output->use == encoded && output->message->part.size() == 1 && !(*output->message->part.begin()).simpleTypePtr() && !(*output->message->part.begin()).complexTypePtr();
if (flag && input->message && (*output->message->part.begin()).element)
for (vector<wsdl__part>::const_iterator part = input->message->part.begin(); part != input->message->part.end(); ++part)
if ((*part).element && !strcmp((*part).element, (*output->message->part.begin()).element))
flag = false;
}
fprintf(stream, "int %s(", input_name);
input->generate(types, ",", anonymous, true, false);
if (output_name)
{ if (flag)
{ if (style == document)
{ // Shortcut: do not generate wrapper struct
output->generate(types, "", anonymous, true, true);
}
else if ((*output->message->part.begin()).name)
{ fprintf(stream, "\n");
fprintf(stream, anonymous ? anonformat : paraformat, types.tname(NULL, NULL, (*output->message->part.begin()).type), cflag ? "*" : "&", types.aname(NULL, NULL, (*output->message->part.begin()).name), "");
fprintf(stream, "\t///< Response parameter");
}
}
else
{ fprintf(stream, "\n struct %-28s%s", output_name, cflag ? "*" : "&");
fprintf(stream, "\t///< Response struct parameter");
}
fprintf(stream, "\n);\n");
}
else
fprintf(stream, "\n void\t///< One-way message: no response parameter\n);\n");
}
////////////////////////////////////////////////////////////////////////////////
//
// Message methods
//
////////////////////////////////////////////////////////////////////////////////
void Message::generate(Types &types, const char *sep, bool anonymous, bool remark, bool response)
{ if (message)
{ for (vector<wsdl__part>::const_iterator part = message->part.begin(); part != message->part.end(); ++part)
{ if (!(*part).name)
fprintf(stderr, "Error: no part name in message '%s'\n", message->name?message->name:"");
else if (!body_parts || soap_strsearch(body_parts, (*part).name))
{ if (remark && (*part).documentation)
comment("", (*part).name, "parameter", (*part).documentation);
else
fprintf(stream, "\n");
if ((*part).element)
{ if ((*part).elementPtr())
{ const char *name, *type, *URI;
name = (*part).elementPtr()->name;
/* comment out to use a type that refers to an element defined with typedef */
if ((*part).elementPtr()->type)
type = (*part).elementPtr()->type;
else
/* */
type = name;
if ((*part).elementPtr()->schemaPtr())
URI = (*part).elementPtr()->schemaPtr()->targetNamespace;
else
URI = NULL;
if (response)
{ const char *t = types.tname(NULL, URI, type);
fprintf(stream, anonymous ? anonformat : paraformat, t, strchr(t, '*') != NULL ? " " : cflag ? "*" : "&", types.aname(NULL, URI, name), sep);
if (remark)
fprintf(stream, "\t///< Response parameter");
}
else
{ fprintf(stream, anonymous ? anonformat : paraformat, types.pname(false, NULL, URI, type), " ", types.aname(NULL, URI, name), sep);
if (remark && *sep == ',')
fprintf(stream, "\t///< Request parameter");
}
}
else
{ fprintf(stream, anonymous ? anonformat : paraformat, types.pname(false, NULL, NULL, (*part).element), " ", types.aname(NULL, NULL, (*part).element), sep);
if (remark)
fprintf(stream, "\t///< TODO: Check element type (imported type)");
}
}
else if ((*part).type)
{ if (use == literal)
fprintf(stderr, "Warning: part '%s' uses literal style and must refer to an element rather than a type\n", (*part).name);
if (response)
{ const char *t = types.tname(NULL, NULL, (*part).type);
fprintf(stream, anonymous ? anonformat : paraformat, t, strchr(t, '*') != NULL ? " " : cflag ? "*" : "&", types.aname(NULL, NULL, (*part).name), sep);
if (remark)
fprintf(stream, "\t///< Response parameter");
}
else
{ fprintf(stream, anonymous ? anonformat : paraformat, types.pname(false, NULL, NULL, (*part).type), " ", types.aname(NULL, NULL, (*part).name), sep);
if (remark && *sep == ',')
fprintf(stream, "\t///< Request parameter");
}
}
else
fprintf(stderr, "Error: no wsdl:definitions/message/part/@type in part '%s'\n", (*part).name);
}
}
}
else
fprintf(stderr, "Error: no wsdl:definitions/message\n");
}
////////////////////////////////////////////////////////////////////////////////
//
// Miscellaneous
//
////////////////////////////////////////////////////////////////////////////////
static void comment(const char *start, const char *middle, const char *end, const char *text)
{ if (text)
{ if (strchr(text, '\r') || strchr(text, '\n'))
fprintf(stream, "\n/** %s %s %s documentation:\n%s\n*/\n\n", start, middle, end, text);
else
fprintf(stream, "\n/// %s %s %s: %s\n", start, middle, end, text);
}
}
static void page(const char *page, const char *title, const char *text)
{ if (text)
fprintf(stream, "\n@page %s%s \"%s\"\n", page, title, text);
else
fprintf(stream, "\n@page %s%s\n", page, title);
}
static void section(const char *section, const char *title, const char *text)
{ if (text)
fprintf(stream, "\n@section %s%s \"%s\"\n", section, title, text);
else
fprintf(stream, "\n@section %s%s\n", section, title);
}
static void banner(const char *text)
{ int i;
fprintf(stream, "\n/");
for (i = 0; i < 78; i++)
fputc('*', stream);
fprintf(stream, "\\\n *%76s*\n * %-75s*\n *%76s*\n\\", "", text, "");
for (i = 0; i < 78; i++)
fputc('*', stream);
fprintf(stream, "/\n\n");
}
static void ident()
{ time_t t = time(NULL), *p = &t;
char tmp[256];
int i;
strftime(tmp, 256, "%Y-%m-%d %H:%M:%S GMT", gmtime(p));
fprintf(stream, "/* %s\n Generated by wsdl2h "VERSION" from ", outfile?outfile:"");
if (infiles)
{ for (i = 0; i < infiles; i++)
fprintf(stream, "%s ", infile[i]);
}
else
fprintf(stream, "(stdin) ");
fprintf(stream, "and %s\n %s\n Copyright (C) 2001-2005 Robert van Engelen, Genivia Inc. All Rights Reserved.\n This part of the software is released under one of the following licenses:\n GPL or Genivia's license for commercial use.\n*/\n\n", mapfile, tmp);
}
static void text(const char *text)
{ const char *s = text;
if (!s)
return;
while (*s)
{ switch (*s)
{ case '\n':
case '\t':
case ' ':
fputc(*s, stream);
break;
case '/':
fputc(*s, stream);
if (s[1] == '*')
fputc(' ', stream);
break;
case '*':
fputc(*s, stream);
if (s[1] == '/')
fputc(' ', stream);
break;
default:
if (*s > 32)
fputc(*s, stream);
}
s++;
}
fputc('\n', stream);
}