예제 #1
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
func (cfg *Config) genTypeSpec(t xsd.Type) (result []spec, err error) {
	var s []spec
	cfg.debugf("generating type spec for %q", xsd.XMLName(t).Local)

	switch t := t.(type) {
	case *xsd.SimpleType:
		s, err = cfg.genSimpleType(t)
	case *xsd.ComplexType:
		s, err = cfg.genComplexType(t)
	case xsd.Builtin:
		// Some built-ins, though built-in, require marshal/unmarshal methods
		// to be able to use them with the encoding/xml package.
		switch t {
		case xsd.Date, xsd.Time, xsd.DateTime, xsd.GDay, xsd.GMonth, xsd.GMonthDay, xsd.GYear, xsd.GYearMonth:
			s, err = cfg.genTimeSpec(t)
		case xsd.HexBinary, xsd.Base64Binary:
			s, err = cfg.genBinarySpec(t)
		case xsd.ENTITIES, xsd.IDREFS, xsd.NMTOKENS:
			s, err = cfg.genTokenListSpec(t)
		}
	default:
		cfg.logf("unexpected %T %s", t, xsd.XMLName(t).Local)
	}
	if err != nil || s == nil {
		return result, err
	}
	return append(result, s...), nil
}
예제 #2
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// Generate a type declaration for the bult-in list values, along with
// marshal/unmarshal methods
func (cfg *Config) genTokenListSpec(t xsd.Builtin) ([]spec, error) {
	cfg.debugf("generating Go source for token list %q", xsd.XMLName(t).Local)
	s := spec{
		name:    strings.ToLower(t.String()),
		expr:    builtinExpr(t),
		xsdType: t,
	}
	marshal, err := gen.Func("MarshalText").
		Receiver("x "+s.name).
		Returns("[]byte", "error").
		Body(`
			return []byte(strings.Join(x, " ")), nil
		`).Decl()

	if err != nil {
		return nil, fmt.Errorf("MarshalText %s: %v", s.name, err)
	}

	unmarshal, err := gen.Func("UnmarshalText").
		Receiver("x " + s.name).
		Args("text []byte").
		Returns("error").
		Body(`
			*x = bytes.Fields(text)
			return nil
		`).Decl()

	if err != nil {
		return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err)
	}

	s.methods = append(s.methods, marshal, unmarshal)
	return []spec{s}, nil
}
예제 #3
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
func (cfg *Config) genSimpleType(t *xsd.SimpleType) ([]spec, error) {
	var result []spec
	if t.List {
		return cfg.genSimpleListSpec(t)
	}
	if len(t.Union) > 0 {
		// We don't support unions because the code that needs
		// to be generated to check which of the member types
		// the value would be is too complex.
		result = append(result, spec{
			name:    cfg.typeName(t.Name),
			expr:    builtinExpr(xsd.String),
			xsdType: t,
		})
		return result, nil
	}
	base, err := cfg.expr(t.Base)
	if err != nil {
		return nil, fmt.Errorf("simpleType %s: base type %s: %v",
			t.Name.Local, xsd.XMLName(t.Base).Local, err)
	}
	result = append(result, spec{
		name:    cfg.typeName(t.Name),
		expr:    base,
		xsdType: t,
	})
	return result, nil
}
예제 #4
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// Flatten out our tree of dependent types. If a type is marked as
// private by a user filter and not used as a struct field or embedded
// struct, it is ommitted from the output.
func (cfg *Config) flatten(types map[xml.Name]xsd.Type) []xsd.Type {
	var result []xsd.Type
	push := func(t xsd.Type) {
		result = append(result, t)
	}
	for _, t := range types {
		if cfg.filterTypes != nil && cfg.filterTypes(t) {
			continue
		}
		if t := cfg.flatten1(t, push); t != nil {
			result = append(result, t)
		}
	}
	// Remove duplicates
	seen := make(map[xml.Name]bool)
	var a []xsd.Type
	for _, v := range result {
		name := xsd.XMLName(v)
		if _, ok := seen[name]; !ok {
			seen[name] = true
			a = append(a, v)
		}
	}
	cfg.debugf("discovered %d types", len(a))
	return a
}
예제 #5
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// Generate a type declaration for the built-in binary values, along with
// marshal/unmarshal methods for them.
func (cfg *Config) genBinarySpec(t xsd.Builtin) ([]spec, error) {
	cfg.debugf("generating Go source for binary type %q", xsd.XMLName(t).Local)
	s := spec{
		expr:    builtinExpr(t),
		name:    xsd.XMLName(t).Local,
		xsdType: t,
	}
	marshal := gen.Func("MarshalText").Receiver("b "+s.name).Returns("[]byte", "error")
	unmarshal := gen.Func("UnmarshalText").Receiver("b " + s.name).Args("text []byte").
		Returns("err error")

	switch t {
	case xsd.HexBinary:
		unmarshal.Body(`
			*b, err = hex.DecodeString(string(text))
			return err
		`)
		marshal.Body(`
			n := hex.EncodedLen([]byte(b))
			buf := make([]byte, n)
			hex.Encode(buf, []byte(b))
			return buf, nil
		`)
	case xsd.Base64Binary:
		unmarshal.Body(`
			*b, err = base64.StdEncoding.DecodeString(string(text))
			return err
		`)
		marshal.Body(`
			var buf bytes.Buffer
			enc := base64.NewEncoder(base64.StdEncoding, &buf)
			enc.Write([]byte(b))
			enc.Close()
			return buf.Bytes()
		`)
	}
	marshalFn, err := marshal.Decl()
	if err != nil {
		return nil, fmt.Errorf("MarshalText %s: %v", s.name, err)
	}
	unmarshalFn, err := unmarshal.Decl()
	if err != nil {
		return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err)
	}
	s.methods = append(s.methods, unmarshalFn, marshalFn)
	return []spec{s}, nil
}
예제 #6
0
파일: config.go 프로젝트: DrGo/go-xml
// Return the identifier for non-builtin types, and the Go expression
// mapped to the built-in type.
func (cfg *Config) expr(t xsd.Type) (ast.Expr, error) {
	if t, ok := t.(xsd.Builtin); ok {
		ex := builtinExpr(t)
		if ex == nil {
			return nil, fmt.Errorf("Unknown built-in type %q", t.Name().Local)
		}
		return ex, nil
	}
	return ast.NewIdent(cfg.typeName(xsd.XMLName(t))), nil
}
예제 #7
0
파일: config.go 프로젝트: DrGo/go-xml
// SOAP arrays are declared as follows (unimportant fields ellided):
//
// 	<xs:complexType name="Array">
// 	  <xs:attribute name="arrayType" type="xs:string" />
// 	  <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" />
// 	</xs:complexType>
//
// Then schemas that want to declare a fixed-type soap array do so like this:
//
// 	<xs:complexType name="IntArray">
// 	  <xs:complexContent>
// 	    <xs:restriction base="soapenc:Array>
// 	      <xs:attribute ref="soapenc:arrayType" wsdl:arrayType="xs:int[]" />
// 	    </xs:restriction>
// 	  </xs:complexContent>
// 	</xs:complexType>
//
// XML Schema is wonderful, aint it?
func (cfg *Config) parseSOAPArrayType(s xsd.Schema, t xsd.Type) xsd.Type {
	const soapenc = "http://schemas.xmlsoap.org/soap/encoding/"
	const wsdl = "http://schemas.xmlsoap.org/wsdl/"
	var itemType xml.Name

	c, ok := t.(*xsd.ComplexType)
	if !ok {
		return t
	}
	var attr []xsd.Attribute
	for _, v := range c.Attributes {
		if v.Name.Local != "arrayType" {
			attr = append(attr, v)
			continue
		}
		for _, a := range v.Attr {
			if (a.Name != xml.Name{wsdl, "arrayType"}) {
				continue
			}
			itemType = v.Resolve(a.Value)
			break
		}
		break
	}
	if itemType.Local == "" {
		return c
	}
	itemType.Local = strings.TrimSpace(itemType.Local)
	itemType.Local = strings.TrimSuffix(itemType.Local, "[]")
	if b := s.FindType(itemType); b != nil {
		c = cfg.overrideWildcardType(c, b)
	} else {
		cfg.logf("could not lookup item type %q in namespace %q",
			itemType.Local, itemType.Space)
	}

	// Have to remove arrayType from the "base" type, without affecting
	// others inheriting from this type.
	basep, ok := c.Base.(*xsd.ComplexType)
	if !ok {
		cfg.logf("type %s derives from non-complexType %s", c.Name.Local, xsd.XMLName(c.Base).Local)
		return c
	}
	base := *basep
	base.Attributes = make([]xsd.Attribute, 0, len(basep.Attributes)-1)
	for _, v := range basep.Attributes {
		if v.Name.Local != "arrayType" {
			base.Attributes = append(base.Attributes, v)
		}
	}
	c.Base = &base
	c.Attributes = attr
	return c
}
예제 #8
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// Generate a type declaration for the built-in time values, along with
// marshal/unmarshal methods for them.
func (cfg *Config) genTimeSpec(t xsd.Builtin) ([]spec, error) {
	var timespec string
	cfg.debugf("generating Go source for time type %q", xsd.XMLName(t).Local)

	s := spec{
		expr:    ast.NewIdent("time.Time"),
		name:    builtinExpr(t).(*ast.Ident).Name,
		xsdType: t,
	}

	switch t {
	case xsd.GDay:
		timespec = "---02"
	case xsd.GMonth:
		timespec = "--01"
	case xsd.GMonthDay:
		timespec = "--01-02"
	case xsd.GYear:
		timespec = "2006"
	case xsd.GYearMonth:
		timespec = "2006-01"
	case xsd.Time:
		timespec = "15:04:05.999999999"
	case xsd.Date:
		timespec = "2006-01-02"
	case xsd.DateTime:
		timespec = "2006-01-02T15:04:05.999999999"
	}
	unmarshal, err := gen.Func("UnmarshalText").
		Receiver("t *"+s.name).
		Args("text []byte").
		Returns("error").
		Body(`
			return _unmarshalTime(text, (*time.Time)(t), %q)
		`, timespec).Decl()
	if err != nil {
		return nil, fmt.Errorf("could not generate unmarshal function for %s: %v", s.name, err)
	}
	marshal, err := gen.Func("MarshalText").
		Receiver("t *"+s.name).
		Returns("[]byte", "error").
		Body(`
			return []byte((*time.Time)(t).Format(%q)), nil
		`, timespec).Decl()
	if err != nil {
		return nil, fmt.Errorf("could not generate marshal function for %s: %v", s.name, err)
	}
	s.methods = append(s.methods, unmarshal, marshal)
	if helper := cfg.helper("_unmarshalTime"); helper != nil {
		//panic(fmt.Sprint("adding ", helper.Name.Name, " to functions for ", s.name))
		s.methods = append(s.methods, helper)
	}
	return []spec{s}, nil
}
예제 #9
0
파일: config.go 프로젝트: DrGo/go-xml
func (cfg *Config) overrideWildcardType(t *xsd.ComplexType, base xsd.Type) *xsd.ComplexType {
	var elem xsd.Element
	var found bool
	var replaced bool
Loop:
	for x := xsd.Type(t); xsd.Base(x) != nil; x = xsd.Base(x) {
		c, ok := x.(*xsd.ComplexType)
		if !ok {
			cfg.logf("warning: soap-encoded array %s extends %T %s",
				xsd.XMLName(x).Local, base, xsd.XMLName(base).Local)
			return t
		}
		for _, v := range c.Elements {
			if v.Wildcard {
				elem = v
				found = true
				break Loop
			}
		}
	}
	if !found {
		cfg.logf("could not override wildcard type for %s; not found in type hierarchy", t.Name.Local)
		return t
	}
	cfg.debugf("overriding wildcard element of %s type from %s to %s",
		t.Name.Local, xsd.XMLName(elem.Type).Local, xsd.XMLName(base).Local)
	elem.Type = base
	for i, v := range t.Elements {
		if v.Wildcard {
			t.Elements[i] = elem
			replaced = true
		}
	}
	if !replaced {
		t.Elements = append(t.Elements, elem)
	}
	return t
}
예제 #10
0
파일: config.go 프로젝트: DrGo/go-xml
// OnlyTypes defines a whitelist of fully-qualified type name patterns
// to include in the generated Go source. Only types in the whitelist,
// and types that they depend on, will be included in the Go source.
func OnlyTypes(patterns ...string) Option {
	pat := strings.Join(patterns, "|")
	reg, err := regexp.Compile(pat)

	return func(cfg *Config) Option {
		return replacePropertyFilter(&cfg.filterTypes, func(v interface{}) bool {
			t, ok := v.(xsd.Type)
			if !ok {
				panic(fmt.Sprintf("non-type %[1]T %[1]v passed to cfg.filterTypes", v))
			}
			if err != nil {
				cfg.logf("invalid regex %q passed to OnlyTypes: %v", pat, err)
				return false
			}
			return !reg.MatchString(xsd.XMLName(t).Local)
		})(cfg)
	}
}
예제 #11
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// Generate a type declaration for a <list> type, along with marshal/unmarshal
// methods.
func (cfg *Config) genSimpleListSpec(t *xsd.SimpleType) ([]spec, error) {
	cfg.debugf("generating Go source for simple list %q", xsd.XMLName(t).Local)
	expr, err := cfg.expr(t.Base)
	if err != nil {
		return nil, err
	}
	s := spec{
		name:    cfg.typeName(t.Name),
		expr:    expr,
		xsdType: t,
	}
	marshal, err := gen.Func("MarshalText").
		Receiver("x *"+s.name).
		Returns("[]byte", "error").
		Body(`
			return nil, nil
		`).Decl()

	if err != nil {
		return nil, fmt.Errorf("MarshalText %s: %v", s.name, err)
	}

	unmarshal, err := gen.Func("UnmarshalText").
		Receiver("x *" + s.name).
		Args("text []byte").
		Returns("error").
		Body(`
			return nil
		`).Decl()

	if err != nil {
		return nil, fmt.Errorf("UnmarshalText %s: %v", s.name, err)
	}

	s.methods = append(s.methods, marshal, unmarshal)
	return []spec{s}, nil
}
예제 #12
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
func (cfg *Config) genComplexType(t *xsd.ComplexType) ([]spec, error) {
	var result []spec
	var fields []ast.Expr

	if t.Extends {
		base, err := cfg.expr(t.Base)
		if err != nil {
			return nil, fmt.Errorf("%s base type %s: %v",
				t.Name.Local, xsd.XMLName(t.Base).Local, err)
		}
		switch b := t.Base.(type) {
		case *xsd.SimpleType:
			cfg.debugf("complexType %[1]s extends simpleType %[2]s. Naming"+
				" the chardata struct field after %[2]s", t.Name.Local, b.Name.Local)
			fields = append(fields, base, base, gen.String(`xml:",chardata"`))
		case xsd.Builtin:
			if b == xsd.AnyType {
				// extending anyType doesn't really make sense, but
				// we can just ignore it.
				cfg.debugf("complexType %s: don't know how to extend anyType, ignoring",
					t.Name.Local)
				break
			}
			// Name the field after the xsd type name.
			cfg.debugf("complexType %[1]s extends %[2]s, naming chardata struct field %[2]s",
				t.Name.Local, b)
			fields = append(fields, ast.NewIdent(b.String()), base, gen.String(`xml:",chardata"`))
		case *xsd.ComplexType:
			// Use struct embedding when extending a complex type
			cfg.debugf("complexType %s extends %s, embedding struct",
				t.Name.Local, b.Name.Local)
			fields = append(fields, nil, base, nil)
		}
	} else {
		// When restricting a complex type, all attributes are "inherited" from
		// the base type (but not elements!). In addition, any <xs:any> elements,
		// while not explicitly inherited, do not disappear.
		switch b := t.Base.(type) {
		case *xsd.ComplexType:
			t.Attributes = mergeAttributes(t, b)
			hasWildcard := false
			for _, el := range t.Elements {
				if el.Wildcard {
					hasWildcard = true
					break
				}
			}
			if hasWildcard {
				break
			}
			for _, el := range b.Elements {
				if el.Wildcard {
					t.Elements = append(t.Elements, el)
					break
				}
			}
		}
	}

	attributes, elements := cfg.filterFields(t)
	cfg.debugf("complexType %s: generating struct fields for %d elements and %d attributes",
		xsd.XMLName(t).Local, len(elements), len(attributes))
	hasDefault := false
	for _, attr := range attributes {
		hasDefault = hasDefault || (attr.Default != "")
		tag := fmt.Sprintf(`xml:"%s,attr"`, attr.Name.Local)
		base, err := cfg.expr(attr.Type)
		if err != nil {
			return nil, fmt.Errorf("%s attribute %s: %v", t.Name.Local, attr.Name.Local, err)
		}
		fields = append(fields, ast.NewIdent(cfg.public(attr.Name)), base, gen.String(tag))
	}
	for _, el := range elements {
		hasDefault = hasDefault || (el.Default != "")
		tag := fmt.Sprintf(`xml:"%s %s"`, el.Name.Space, el.Name.Local)
		base, err := cfg.expr(el.Type)
		if err != nil {
			return nil, fmt.Errorf("%s element %s: %v", t.Name.Local, el.Name.Local, err)
		}
		name := ast.NewIdent(cfg.public(el.Name))
		if el.Wildcard {
			tag = `xml:",any"`
			if el.Plural {
				name = ast.NewIdent("Items")
			} else {
				name = ast.NewIdent("Item")
			}
			if b, ok := el.Type.(xsd.Builtin); ok && b == xsd.AnyType {
				cfg.debugf("complexType %s: defaulting wildcard element to []string", t.Name.Local)
				base = builtinExpr(xsd.String)
			}
		}
		if el.Plural {
			base = &ast.ArrayType{Elt: base}
		}
		fields = append(fields, name, base, gen.String(tag))
	}
	expr := gen.Struct(fields...)
	s := spec{
		name:    cfg.typeName(t.Name),
		expr:    expr,
		xsdType: t,
	}
	if hasDefault {
		unmarshal, marshal, err := cfg.genMarshalComplexType(t)
		if err != nil {
			//NOTE(droyo) may want to log this instead of stopping the generator
			return result, err
		} else {
			if unmarshal != nil {
				s.methods = append(s.methods, unmarshal)
			}
			if marshal != nil {
				s.methods = append(s.methods, marshal)
			}
		}
	}
	result = append(result, s)
	return result, nil
}
예제 #13
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
// To reduce the size of the Go source generated, all intermediate types
// are "squashed"; every type should be based on a Builtin or another
// type that the user wants included in the Go source.
func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type)) xsd.Type {
	switch t := t.(type) {
	case *xsd.SimpleType:
		var (
			chain         []xsd.Type
			base, builtin xsd.Type
			ok            bool
		)
		// TODO: handle list/union types
		for base = xsd.Base(t); base != nil; base = xsd.Base(base) {
			if builtin, ok = base.(xsd.Builtin); ok {
				break
			}
			chain = append(chain, base)
		}
		for _, v := range chain {
			if v, ok := v.(*xsd.SimpleType); ok {
				v.Base = builtin
				push(v)
			}
		}
		t.Base = builtin
		return t
	case *xsd.ComplexType:
		// We can "unpack" a struct if it is extending a simple
		// or built-in type and we are ignoring all of its attributes.
		switch t.Base.(type) {
		case xsd.Builtin, *xsd.SimpleType:
			if b, ok := t.Base.(xsd.Builtin); ok && b == xsd.AnyType {
				break
			}
			attributes, _ := cfg.filterFields(t)
			if len(attributes) == 0 {
				cfg.debugf("complexType %s extends simpleType %s, but all attributes are filtered. unpacking.",
					t.Name.Local, xsd.XMLName(t.Base))
				switch b := t.Base.(type) {
				case xsd.Builtin:
					return b
				case *xsd.SimpleType:
					return cfg.flatten1(t.Base, push)
				}
			}
		}
		// We can flatten a struct field if its type does not
		// need additional methods for unmarshalling.
		for i, el := range t.Elements {
			el.Type = cfg.flatten1(el.Type, push)
			if b, ok := el.Type.(*xsd.SimpleType); ok {
				if !b.List && len(b.Union) == 0 {
					el.Type = xsd.Base(el.Type)
				}
			}
			t.Elements[i] = el
		}
		for i, attr := range t.Attributes {
			attr.Type = cfg.flatten1(attr.Type, push)
			if b, ok := attr.Type.(*xsd.SimpleType); ok {
				if !b.List && len(b.Union) == 0 {
					attr.Type = xsd.Base(attr.Type)
				}
			}
			t.Attributes[i] = attr
		}
		return t
	case xsd.Builtin:
		// There are a few built-ins that do not map directly to Go types.
		// for these, we will declare them in the Go source.
		switch t {
		case xsd.ENTITIES, xsd.IDREFS, xsd.NMTOKENS:
			push(t)
		case xsd.Base64Binary, xsd.HexBinary:
			push(t)
		case xsd.Date, xsd.Time, xsd.DateTime:
			push(t)
		case xsd.GDay, xsd.GMonth, xsd.GMonthDay, xsd.GYear, xsd.GYearMonth:
			push(t)
		}
		return t
	}
	panic(fmt.Sprintf("unexpected %T", t))
}
예제 #14
0
파일: xsdgen.go 프로젝트: DrGo/go-xml
func (cfg *Config) genAST(schema xsd.Schema, extra ...xsd.Schema) (*ast.File, error) {
	var errList errorList
	decls := make(map[string]spec)

	cfg.addStandardHelpers()
	collect := make(map[xml.Name]xsd.Type)
	for k, v := range schema.Types {
		collect[k] = v
	}
	for _, schema := range extra {
		for k, v := range schema.Types {
			collect[k] = v
		}
	}
	prev := schema.Types
	schema.Types = collect
	if cfg.preprocessType != nil {
		cfg.debugf("running user-defined pre-processing functions")
		for name, t := range prev {
			if t := cfg.preprocessType(schema, t); t != nil {
				prev[name] = t
			}
		}
	}
	schema.Types = prev

	cfg.debugf("generating Go source for schema %q", schema.TargetNS)
	typeList := cfg.flatten(schema.Types)

	for _, t := range typeList {
		specs, err := cfg.genTypeSpec(t)
		if err != nil {
			errList = append(errList, fmt.Errorf("generate type %q: %v", xsd.XMLName(t).Local, err))
		} else {
			for _, s := range specs {
				decls[s.name] = s
			}
		}
	}
	if cfg.postprocessType != nil {
		cfg.debugf("running user-defined post-processing functions")
		for name, s := range decls {
			decls[name] = cfg.postprocessType(s)
		}
	}

	if len(errList) > 0 {
		return nil, errList
	}
	var result []ast.Decl
	keys := make([]string, 0, len(decls))
	for name := range decls {
		keys = append(keys, name)
	}
	sort.Strings(keys)
	for _, name := range keys {
		info := decls[name]
		typeDecl := &ast.GenDecl{
			Tok: token.TYPE,
			Specs: []ast.Spec{
				&ast.TypeSpec{
					Name: ast.NewIdent(name),
					Type: info.expr,
				},
			},
		}
		result = append(result, typeDecl)
		for _, f := range info.methods {
			result = append(result, f)
		}
	}
	if cfg.pkgname == "" {
		cfg.pkgname = "ws"
	}
	file := &ast.File{
		Decls: result,
		Name:  ast.NewIdent(cfg.pkgname),
		Doc:   nil,
	}
	return file, nil
}