Created
May 27, 2011 02:39
-
-
Save eed3si9n/994540 to your computer and use it in GitHub Desktop.
wsdl 2.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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