Skip to content

Commit

Permalink
Implemented mapping rules for References to Top-level attribute decla…
Browse files Browse the repository at this point in the history
…rations
  • Loading branch information
realmfoo committed Oct 2, 2017
1 parent 8e5ca62 commit c8f5151
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 15 deletions.
2 changes: 1 addition & 1 deletion fixtures/stock.wsdl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<definitions name="StockQuote" targetNamespace="http://example.com/stockquote.wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://example.com/stockquote.wsdl" xmlns:xsd1="http://example.com/stockquote.xsd">
<types>
<schema targetNamespace="http://example.com/stockquote.xsd" xmlns="http://www.w3.org/2000/10/XMLSchema">
<schema targetNamespace="http://example.com/stockquote.xsd" xmlns="http://www.w3.org/2001/XMLSchema">
<element name="TradePriceRequest">
<complexType>
<all>
Expand Down
30 changes: 30 additions & 0 deletions fixtures/test.wsdl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ResponseStatus">
<s:sequence>
<s:element name="status" minOccurs="0" maxOccurs="unbounded">
<s:complexType>
<s:simpleContent>
<s:extension base="s:string">
<s:attribute name="code" use="required">
<s:simpleType>
<s:restriction base="s:string">
<s:enumeration value="UnrecognizedTrimName" />
<s:enumeration value="UnusedTrimName" />
</s:restriction>
</s:simpleType>
</s:attribute>
</s:extension>
</s:simpleContent>
</s:complexType>
</s:element>
</s:sequence>
<s:attribute ref="tns:responseCode"/>
</s:complexType>
<s:attribute name="responseCode">
<s:simpleType>
<s:restriction base="s:string">
<s:enumeration value="Successful" />
<s:enumeration value="Unsuccessful" />
<s:enumeration value="ConditionallySuccessful" />
</s:restriction>
</s:simpleType>
</s:attribute>
</s:schema>
</wsdl:types>
<wsdl:message name="GetInfoSoapIn">
Expand Down
8 changes: 5 additions & 3 deletions gowsdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ func (g *GoWSDL) Start() (map[string][]byte, error) {
return nil, err
}

// Process WSDL nodes
for _, schema := range g.wsdl.Types.Schemas {
newTraverser(schema, g.wsdl.Types.Schemas).traverse()
}

var wg sync.WaitGroup

wg.Add(1)
Expand Down Expand Up @@ -266,9 +271,6 @@ func (g *GoWSDL) genTypes() ([]byte, error) {
"goString": goString,
}

//TODO resolve element refs in place.
//g.resolveElementsRefs()

data := new(bytes.Buffer)
tmpl := template.Must(template.New("types").Funcs(funcMap).Parse(typesTmpl))
err := tmpl.Execute(data, g.wsdl.Types)
Expand Down
34 changes: 34 additions & 0 deletions gowsdl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gowsdl
import (
"bytes"
"errors"
"fmt"
"go/format"
"go/parser"
"go/printer"
Expand Down Expand Up @@ -59,6 +60,38 @@ func TestComplexTypeWithInlineSimpleType(t *testing.T) {
}
}

func TestAttributeRef(t *testing.T) {
g, err := NewGoWSDL("fixtures/test.wsdl", "myservice", false, true)
if err != nil {
t.Error(err)
}

resp, err := g.Start()
if err != nil {
t.Fatal(err)
}
actual, err := getTypeDeclaration(resp, "ResponseStatus")
if err != nil {
fmt.Println(string(resp["types"]))
t.Fatal(err)
}

expected := `type ResponseStatus struct {
XMLName xml.Name ` + "`" + `xml:"http://www.mnb.hu/webservices/ ResponseStatus"` + "`" + `
Status []struct {
Value string
Code string ` + "`" + `xml:"code,attr,omitempty"` + "`" + `
} ` + "`" + `xml:"status,omitempty"` + "`" + `
ResponseCode string ` + "`" + `xml:"responseCode,attr,omitempty"` + "`" + `
}`
if actual != expected {
t.Error("got " + actual + " want " + expected)
}
}

func TestVboxGeneratesWithoutSyntaxErrors(t *testing.T) {
files, err := filepath.Glob("fixtures/*.wsdl")
if err != nil {
Expand All @@ -85,6 +118,7 @@ func TestVboxGeneratesWithoutSyntaxErrors(t *testing.T) {

_, err = format.Source(data.Bytes())
if err != nil {
fmt.Println(string(data.Bytes()))
t.Error(err)
}
}
Expand Down
115 changes: 115 additions & 0 deletions traverser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package gowsdl

import (
"encoding/xml"
"strings"
)

type traverser struct {
c *XSDSchema
all []*XSDSchema
}

func newTraverser(c *XSDSchema, all []*XSDSchema) *traverser {
return &traverser{
c: c,
all: all,
}
}

func (t *traverser) traverse() {
for _, ct := range t.c.ComplexTypes {
t.traverseComplexType(ct)
}
for _, st := range t.c.SimpleType {
t.traverseSimpleType(st)
}
for _, elm := range t.c.Elements {
t.traverseElement(elm)
}
}

func (t *traverser) traverseElements(ct []*XSDElement) {
for _, elm := range ct {
t.traverseElement(elm)
}
}

func (t *traverser) traverseElement(elm *XSDElement) {
if elm.ComplexType != nil {
t.traverseComplexType(elm.ComplexType)
}
if elm.SimpleType != nil {
t.traverseSimpleType(elm.SimpleType)
}
}

func (t *traverser) traverseSimpleType(st *XSDSimpleType) {
}

func (t *traverser) traverseComplexType(ct *XSDComplexType) {
t.traverseElements(ct.Sequence)
t.traverseElements(ct.Choice)
t.traverseElements(ct.SequenceChoice)
t.traverseElements(ct.All)
t.traverseAttributes(ct.Attributes)
t.traverseAttributes(ct.ComplexContent.Extension.Attributes)
t.traverseAttributes(ct.SimpleContent.Extension.Attributes)
}

func (t *traverser) traverseAttributes(attrs []*XSDAttribute) {
for _, attr := range attrs {
t.traverseAttribute(attr)
}
}

func (t *traverser) traverseAttribute(attr *XSDAttribute) {
if attr.Ref != "" {
refAttr := t.getGlobalAttribute(attr.Ref)
if refAttr != nil && refAttr.Ref == "" {
t.traverseAttribute(refAttr)
attr.Name = refAttr.Name
attr.Type = refAttr.Type
if attr.Fixed == "" {
attr.Fixed = refAttr.Fixed
}
}
} else if attr.Type == "" {
if attr.SimpleType != nil {
t.traverseSimpleType(attr.SimpleType)
attr.Type = attr.SimpleType.Restriction.Base
}
}
}

func (t *traverser) getGlobalAttribute(name string) *XSDAttribute {
ref := t.qname(name)

for _, schema := range t.all {
if schema.TargetNamespace == ref.Space {
for _, attr := range schema.Attributes {
if attr.Name == ref.Local {
return attr
}
}
}
}

return nil
}

// qname resolves QName into xml.Name.
func (t *traverser) qname(name string) (qname xml.Name) {
x := strings.SplitN(name, ":", 2)
if len(x) == 1 {
qname.Local = x[0]
} else {
qname.Local = x[1]
qname.Space = x[0]
if ns, ok := t.c.Xmlns[qname.Space]; ok {
qname.Space = ns
}
}

return qname
}
9 changes: 3 additions & 6 deletions types_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@ var typesTmpl = `
{{define "Attributes"}}
{{range .}}
{{if .Doc}} {{.Doc | comment}} {{end}} {{if not .Type}}
{{ .Name | makeFieldPublic}} {{toGoType .SimpleType.Restriction.Base}} ` + "`" + `xml:"{{.Name}},attr,omitempty"` + "`" + `
{{else}}
{{ .Name | makeFieldPublic}} {{toGoType .Type}} ` + "`" + `xml:"{{.Name}},attr,omitempty"` + "`" + `
{{end}}
{{if .Doc}} {{.Doc | comment}} {{end}}
{{ .Name | makeFieldPublic}} {{toGoType .Type}} ` + "`" + `xml:"{{.Name}},attr,omitempty"` + "`" + `
{{end}}
{{end}}
Expand All @@ -45,7 +42,7 @@ var typesTmpl = `
{{end}}
{{define "ComplexTypeInline"}}
{{replaceReservedWords .Name | makePublic}} struct {
{{replaceReservedWords .Name | makePublic}} {{if eq .MaxOccurs "unbounded"}}[]{{end}}struct {
{{with .ComplexType}}
{{if ne .ComplexContent.Extension.Base ""}}
{{template "ComplexContent" .ComplexContent}}
Expand Down
95 changes: 95 additions & 0 deletions wsdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

package gowsdl

import "encoding/xml"

const wsdlNamespace = "http://schemas.xmlsoap.org/wsdl/"

// WSDL represents the global structure of a WSDL file.
type WSDL struct {
Xmlns map[string]string `xml:"-"`
Name string `xml:"name,attr"`
TargetNamespace string `xml:"targetNamespace,attr"`
Imports []*WSDLImport `xml:"import"`
Expand All @@ -17,6 +22,96 @@ type WSDL struct {
Service []*WSDLService `xml:"http://schemas.xmlsoap.org/wsdl/ service"`
}

// UnmarshalXML implements interface xml.Unmarshaler for XSDSchema.
func (w *WSDL) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
w.Xmlns = make(map[string]string)
for _, attr := range start.Attr {
if attr.Name.Space == "xmlns" {
w.Xmlns[attr.Name.Local] = attr.Value
continue
}

switch attr.Name.Local {
case "name":
w.Name = attr.Value
case "targetNamespace":
w.TargetNamespace = attr.Value
}
}

Loop:
for {
tok, err := d.Token()
if err != nil {
return err
}

switch t := tok.(type) {
case xml.StartElement:
switch {
case t.Name.Local == "import":
x := new(WSDLImport)
if err := d.DecodeElement(x, &t); err != nil {
return err
}
w.Imports = append(w.Imports, x)
case t.Name.Local == "documentation":
if err := d.DecodeElement(&w.Doc, &t); err != nil {
return err
}
case t.Name.Space == wsdlNamespace:
switch t.Name.Local {
case "types":
if err := d.DecodeElement(&w.Types, &t); err != nil {
return err
}
for prefix, namespace := range w.Xmlns {
for _, s := range w.Types.Schemas {
if _, ok := s.Xmlns[prefix]; !ok {
s.Xmlns[prefix] = namespace
}
}
}
case "message":
x := new(WSDLMessage)
if err := d.DecodeElement(x, &t); err != nil {
return err
}
w.Messages = append(w.Messages, x)
case "portType":
x := new(WSDLPortType)
if err := d.DecodeElement(x, &t); err != nil {
return err
}
w.PortTypes = append(w.PortTypes, x)
case "binding":
x := new(WSDLBinding)
if err := d.DecodeElement(x, &t); err != nil {
return err
}
w.Binding = append(w.Binding, x)
case "service":
x := new(WSDLService)
if err := d.DecodeElement(x, &t); err != nil {
return err
}
w.Service = append(w.Service, x)
default:
d.Skip()
continue Loop
}
default:
d.Skip()
continue Loop
}
case xml.EndElement:
break Loop
}
}

return nil
}

// WSDLImport is the struct used for deserializing WSDL imports.
type WSDLImport struct {
Namespace string `xml:"namespace,attr"`
Expand Down
Loading

0 comments on commit c8f5151

Please sign in to comment.