Skip to content

Instantly share code, notes, and snippets.

@eed3si9n
Created May 27, 2011 02:39
Show Gist options
  • Save eed3si9n/994540 to your computer and use it in GitHub Desktop.
Save eed3si9n/994540 to your computer and use it in GitHub Desktop.
wsdl 2.0
/*
* Copyright (c) 2011 e.e d3si9n
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package scalaxb.compiler.wsdl20
import scalaxb.compiler.{Logger, Config, Snippet, ReferenceNotFound, Module}
import Module.{NL, indent}
trait GenSource {
import wsdl20._
import scalaxb.{DataRecord}
import scala.collection.mutable
import scala.xml.Node
val MEP_IN_ONLY = "http://www.w3.org/ns/wsdl/in-only"
val MEP_ROBUST_IN_ONLY = "http://www.w3.org/ns/wsdl/robust-in-only"
val MEP_IN_OUT = "http://www.w3.org/ns/wsdl/in-out"
val WSDL_SOAP = "http://www.w3.org/ns/wsdl/soap"
val WSDL_HTTP = "http://www.w3.org/ns/wsdl/http"
val SOAP_MEP_REQUEST_RESPONSE = "http://www.w3.org/2003/05/soap/mep/request-response"
val SOAP_MEP_SOAP_RESPONSE = "http://www.w3.org/2003/05/soap/mep/soap-response"
def logger: Logger
def log(msg: String) = logger.log(msg)
def context: WsdlContext
def scope: scala.xml.NamespaceBinding
def schemas = context.xsdcontext.schemas.toList
def xsdgenerator: scalaxb.compiler.xsd.GenSource
def generate(description: XDescriptionType): Snippet = {
log("wsdl20: GenSource.generate")
val interfaces = description.xdescriptiontypeoption collect {
case DataRecord(_, _, x: XInterfaceType) => x
}
val soapBindings = description.xdescriptiontypeoption collect {
case DataRecord(_, _, x: XBindingType) if x.interface.isDefined &&
(x.typeValue.toString == WSDL_SOAP) => x
}
mergeSnippets(
(interfaces map {makeInterface}) ++
(soapBindings map {makeSoapBinding})
)
}
def makeInterface(intf: XInterfaceType): Snippet = {
val name = intf.name.capitalize
log("wsdl20#makeInterface: " + name)
val operations = intf.xinterfacetypeoption collect {
case DataRecord(_, _, x: XInterfaceOperationType) => makeOperation(x)
}
val definition = <source>
trait {name} {{
{operations.mkString(NL + " ")}
}}
</source>
Snippet(definition)
}
def makeOperation(op: XInterfaceOperationType): String = {
val pattern = op.pattern map {_.toString} getOrElse {MEP_IN_OUT}
lazy val output = opOutput(op)
lazy val outfault = opOutfault(op)
lazy val faultTypeName = faultRefToTypeName(outfault.head.ref.toString)
lazy val outputTypeName = elementRefToTypeName(output.head.element)
lazy val arg = opInput(op) map { x =>
if (isOperationIRIStyleQualified(op)) buildIRIStyleArg(x.element)
else "%s: %s".format(inputLabel(x), elementRefToTypeName(x.element))
} getOrElse {""}
val retval = pattern match {
case MEP_IN_OUT if outfault.isDefined =>
"def %s(%s): Either[scalaxb.Fault[%s], %s]".format(op.name, arg, faultTypeName, outputTypeName)
case MEP_IN_OUT if outfault.isEmpty =>
"def %s(%s): Either[scalaxb.Fault[Any], %s]".format(op.name, arg, outputTypeName)
case MEP_IN_ONLY =>
"def %s(%s): Unit".format(op.name, arg)
case MEP_ROBUST_IN_ONLY if outfault.isDefined =>
"def %s(%s): Option[scalaxb.Fault[%s]]".format(op.name, arg, faultTypeName)
case MEP_ROBUST_IN_ONLY if outfault.isEmpty =>
"def %s(%s): Option[scalaxb.Fault[Any]]".format(op.name, arg)
case _ => error("unsupported.")
}
log("wsdl20#makeOperation: " + retval)
retval
}
def isOperationIRIStyleQualified(op: XInterfaceOperationType): Boolean = opInput(op).get.element map { x =>
import scalaxb.compiler.xsd.{ReferenceTypeSymbol, ComplexTypeDecl}
val elem = xsdgenerator.elements(splitTypeName(x))
elem.typeSymbol match {
case ReferenceTypeSymbol(decl: ComplexTypeDecl) => xsdgenerator.isQualifyAsIRIStyle(decl)
case _ => false
}
} getOrElse{false}
def isOperationBoundToIRIStyle(op: XInterfaceOperationType): Boolean =
(for {
binding <- context.bindings.valuesIterator.toList if binding.typeValue.toString == WSDL_SOAP
dataRecord2 <- binding.xbindingtypeoption if dataRecord2.value.isInstanceOf[XBindingOperationType]
bindingOp = dataRecord2.value.asInstanceOf[XBindingOperationType] if
context.operations(splitTypeName(bindingOp.ref.toString)) == op &&
bindingOp.attributes.get("@{%s}mep".format(WSDL_SOAP)).map(_.value == SOAP_MEP_SOAP_RESPONSE).getOrElse {false}
} yield true).size > 0
def opInput(op: XInterfaceOperationType): Option[XMessageRefType] = (op.xinterfaceoperationtypeoption collect {
case DataRecord(_, Some("input"), x: XMessageRefType) => x
}).headOption
def inputLabel(x: XMessageRefType) = x.messageLabel getOrElse {"in"}
def opOutput(op: XInterfaceOperationType): Option[XMessageRefType] = (op.xinterfaceoperationtypeoption collect {
case DataRecord(_, Some("output"), x: XMessageRefType) => x
}).headOption
def buildIRIStyleArg(ref: Option[String]): String = ref map { x =>
import scalaxb.compiler.xsd.{ReferenceTypeSymbol, ComplexTypeDecl}
val elem = xsdgenerator.elements(splitTypeName(x))
elem.typeSymbol match {
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
val flatParticles = xsdgenerator.flattenElements(decl, 0)
val paramList = flatParticles map { xsdgenerator.buildParam }
paramList.map(_.toScalaCode).mkString("," + NL + indent(2))
case _ => error("unexpected type: " + elem.typeSymbol)
}
} getOrElse {""}
// @see http://www.w3.org/TR/2007/REC-wsdl20-adjuncts-20070626/#_http_operation_location_query_constr
def buildInputArgs(input: XMessageRefType, httpLocation: Option[String]): String = {
import scalaxb.compiler.xsd.{ReferenceTypeSymbol, ComplexTypeDecl}
import scala.collection.mutable
val elem = xsdgenerator.elements(splitTypeName(input.element.get))
val decl = elem.typeSymbol match {
case ReferenceTypeSymbol(decl: ComplexTypeDecl) => decl
case _ => error("unexpected type: " + elem.typeSymbol.toString)
}
val flatParticles = xsdgenerator.flattenElements(decl, 0)
val cited = mutable.ListBuffer.empty[String]
val locationString: String = httpLocation map { x =>
var s = "\"%s\"".format(x)
flatParticles foreach { p =>
if (s contains ("{%s}".format(p.name))) {
val param = xsdgenerator.buildParam(p)
s = s.replaceAll("\\{%s\\}".format(p.name), "\" + %s.toString + \"".format(param.toParamName))
cited += p.name
}
}
if (s.startsWith("\"\" + ")) {
s = s.slice(5, s.length)
}
if (s.endsWith(" + \"\"")) {
s = s.slice(0, s.length - 5)
}
"Some(%s)".format(s)
} getOrElse{"None"}
// If the HTTP request method used does not allow HTTP message body (e.g. "GET" and "DELETE"),
// and if the value of the {http location ignore uncited} property is "false", then the following rules apply.
val uncited = flatParticles filter { p => !cited.contains(p.name) }
val uncitedString: String =
if (uncited.isEmpty) ""
else (uncited map { p =>
val param = xsdgenerator.buildParam(p)
"\"" + p.name + "\"" + " -> " + param.toParamName
}).mkString("", ", ", ": _*")
"""%s, Map(%s)""".format(locationString, uncitedString)
}
def opOutfault(op: XInterfaceOperationType) = (op.xinterfaceoperationtypeoption collect {
case DataRecord(_, Some("outfault"), x: XMessageRefFaultType) => x
}).headOption
def elementRefToTypeName(ref: Option[String]): String = ref map { x =>
val elem = xsdgenerator.elements(splitTypeName(x))
xsdgenerator.buildTypeName(elem.typeSymbol, true)
} getOrElse {"Any"}
def faultRefToTypeName(ref: String): String = context.faults.get(splitTypeName(ref)) headOption match {
case Some(XInterfaceFaultType(_, _, _, Some(element), _)) =>
val (ns, name) = splitTypeName(element)
val elem = xsdgenerator.elements(ns, name)
xsdgenerator.buildTypeName(elem.typeSymbol, true)
case _ => "Any"
}
// http://www.w3.org/TR/2007/REC-wsdl20-adjuncts-20070626/#soap-binding
// http://www.w3.org/TR/2007/REC-soap12-part2-20070427/
def makeSoapBinding(binding: XBindingType): Snippet = {
val name = binding.name capitalize
val interfaceType = binding.interface map { x =>
val (ns, intfName) = splitTypeName(x.toString)
intfName.capitalize
} getOrElse {"Any"}
val bindingOps = binding.xbindingtypeoption collect {
case DataRecord(_, _, x: XBindingOperationType) => makeSoapOpBinding(x)
}
val opString = bindingOps.mkString(NL + " ")
val bindingTrait = <source>
trait {name}s {{ this: scalaxb.SoapClients =>
val intf: {interfaceType}
trait {name} extends {interfaceType} {{
{opString}
}}
}}
</source>
Snippet(<source></source>, bindingTrait)
}
def makeSoapOpBinding(binding: XBindingOperationType): String = {
val op = context.operations.get(splitTypeName(binding.ref.toString)).get
val pattern = op.pattern map {_.toString} getOrElse {MEP_IN_OUT}
val soapMep = binding.attributes.get("@{%s}mep".format(WSDL_SOAP)) map {_.value.toString} getOrElse {
SOAP_MEP_REQUEST_RESPONSE
}
val httpLocation: Option[String] = binding.attributes.get("@{%s}location".format(WSDL_HTTP)) map {_.value.toString}
val httpMethod: String = binding.attributes.get("@{%s}method".format(WSDL_HTTP)) map {_.value.toString} getOrElse {
soapMep match {
case SOAP_MEP_REQUEST_RESPONSE => "POST"
case _ => "GET"
}}
val quotedMethod = "\"%s\"".format(httpMethod)
lazy val input = opInput(op)
lazy val output = opOutput(op)
lazy val inputLabel = input.head.messageLabel getOrElse {"in"}
lazy val inputElementNs = splitTypeName(input.head.element.get)._1 match {
case Some(x) => "Some(\"%s\")" format (x)
case _ => "None"
}
lazy val inputElementName = "Some(\"%s\")" format (splitTypeName(input.head.element.get)._2)
lazy val inputTypeName = elementRefToTypeName(input.head.element)
lazy val outputTypeName = elementRefToTypeName(output.head.element)
lazy val typedFault = opOutfault(op) map { f =>
"x.asFault[%s]".format(faultRefToTypeName(f.ref.toString)) } getOrElse {"x"}
lazy val invokeToXML = "scalaxb.toXML(%s, %s, %s, defaultScope)".format(inputLabel, inputElementNs, inputElementName)
lazy val inputArgs = buildInputArgs(input.head, httpLocation)
val opImpl = (pattern, soapMep) match {
case (MEP_IN_OUT, SOAP_MEP_REQUEST_RESPONSE) =>
"""soapClient.requestResponse(%s, %s) match {
| case Left(x) => Left(%s)
| case Right(x) => Right(scalaxb.fromXML[%s](x))
| }""".stripMargin.format(invokeToXML, quotedMethod, typedFault, outputTypeName)
case (MEP_IN_OUT, SOAP_MEP_SOAP_RESPONSE) =>
"""soapClient.soapResponse(%s, %s) match {
| case Left(x) => Left(%s)
| case Right(x) => Right(scalaxb.fromXML[%s](x))
| }""".stripMargin.format(inputArgs, quotedMethod, typedFault, outputTypeName)
case (MEP_IN_ONLY, SOAP_MEP_REQUEST_RESPONSE) =>
"""soapClient.requestResponse(%s, %s)""".stripMargin.format(invokeToXML, quotedMethod)
case (MEP_IN_ONLY, SOAP_MEP_SOAP_RESPONSE) =>
"""soapClient.soapResponse(%s, %s)""".stripMargin.format(inputArgs, quotedMethod)
case (MEP_ROBUST_IN_ONLY, SOAP_MEP_REQUEST_RESPONSE) =>
"""soapClient.requestResponse(%s, %s) match {
| case Left(x) => Some(%s)
| case Right(x) => None
| }""".stripMargin.format(invokeToXML, quotedMethod, typedFault)
case (MEP_ROBUST_IN_ONLY, SOAP_MEP_SOAP_RESPONSE) =>
"""soapClient.soapResponse(%s, %s) match {
| case Left(x) => Some(%s)
| case Right(x) => None
| }""".stripMargin.format(inputArgs, quotedMethod, typedFault)
case _ => error("unsupported: " + (pattern, soapMep))
}
val retval = makeOperation(op) + " = " + NL +
" " + opImpl
log(retval)
retval
}
def elements(namespace: Option[String], name: String) =
(for (schema <- schemas;
if schema.targetNamespace == namespace;
if schema.topElems.contains(name))
yield schema.topElems(name)) match {
case x :: xs => x
case Nil => throw new ReferenceNotFound("element" , namespace, name)
}
def mergeSnippets(snippets: Seq[Snippet]) =
Snippet(snippets flatMap {_.definition},
snippets flatMap {_.companion},
snippets flatMap {_.implicitValue})
def splitTypeName(ref: String) = Module.splitTypeName(ref, scope)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment