Exemplo n.º 1
0
// O(n²) is OK since you'll never see more than ~40 attributes...
// right?
func mergeAttributes(src, base *xsd.ComplexType) []xsd.Attribute {
Loop:
	for _, baseattr := range base.Attributes {
		for _, srcattr := range src.Attributes {
			if srcattr.Name == baseattr.Name {
				continue Loop
			}
		}
		src.Attributes = append(src.Attributes, baseattr)
	}
	return src.Attributes
}
Exemplo n.º 2
0
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
}