package jdr import( "html/template" "log" "errors" "strings" "strconv" "regexp" ) var regexp_d = regexp.MustCompile(`^[1-9][0-9]*d([468]|1[02]|20|100)$`) type VariableType interface { Name() string Validate(value string) bool ToHTML(value string) (template.HTML, error) DefaultValue() string } type VariableType_badType struct {} func (*VariableType_badType) Name() string { return "unknown type" } func (*VariableType_badType) Validate(s string) bool { return false } func (*VariableType_badType) ToHTML(value string) (template.HTML, error) { return template.HTML(""), errors.New("unknown type") } func (*VariableType_badType) DefaultValue() string { return "" } type VariableType_bool struct {} func (*VariableType_bool) Name() string { return "bool" } func (*VariableType_bool) ToBool(value string) (bool, error) { switch strings.ToLower(value) { case "true": return true, nil case "false": return false, nil default: return false, errors.New("unknown bool value") } } func (self *VariableType_bool) Validate(value string) bool { _, err := self.ToBool(value) return err == nil } func (self *VariableType_bool) ToHTML(value string) (template.HTML, error) { val, err := self.ToBool(value) if err != nil { return template.HTML(""), err } ret := `` return template.HTML(ret), nil } func (self *VariableType_bool) DefaultValue() string { return "false" } type VariableType_int struct {} func (*VariableType_int) Name() string { return "int" } func (self *VariableType_int) ToInt(value string) (int64, error) { return strconv.ParseInt(value, 10, 64) } func (self *VariableType_int) Validate(value string) bool { _, err := self.ToInt(value) return err == nil } func (self *VariableType_int) ToHTML(value string) (template.HTML, error) { val, err := self.ToInt(value) if err != nil { return template.HTML(""), err } ret := `` return template.HTML(ret), nil } func (self *VariableType_int) DefaultValue() string { return "0" } type VariableType_float struct {} func (*VariableType_float) Name() string { return "float" } func (*VariableType_float) ToFloat(value string) (float64, error) { return strconv.ParseFloat(value, 64) } func (self *VariableType_float) Validate(value string) bool { _, err := self.ToFloat(value) return err == nil } func (self *VariableType_float) ToHTML(value string) (template.HTML, error) { val, err := self.ToFloat(value) if err != nil { return template.HTML(""), err } ret := `` return template.HTML(ret), nil } func (self *VariableType_float) DefaultValue() string { return "0.0" } type VariableType_string struct {} func (*VariableType_string) Name() string { return "string" } func (*VariableType_string) Validate(s string) bool { return true } func (self *VariableType_string) ToHTML(value string) (template.HTML, error) { ret := `` return template.HTML(ret), nil } func (self *VariableType_string) DefaultValue() string { return "" } type VariableType_d struct {} func (*VariableType_d) Name() string { return "d" } func (*VariableType_d) Validate(s string) bool { return regexp_d.MatchString(s) } func (self *VariableType_d) ToHTML(value string) (template.HTML, error) { //FIXME: non-generic version ret := `` return template.HTML(ret), nil } func (self *VariableType_d) DefaultValue() string { //FIXME: non-generic version return "" } func makeVariableType(s string) VariableType { switch(strings.ToLower(s)) { case "bool": return &VariableType_bool{} case "int": return &VariableType_int{} case "float": return &VariableType_float{} case "string": return &VariableType_string{} case "d": return &VariableType_d{} default: log.Println("Unknown type:", s) return &VariableType_badType{} } } type CustomEnum struct { name string BaseType VariableType Values []string } func (self *CustomEnum) Name() string { return self.name } func (self *CustomEnum) ToId(value string) (int, error) { if !self.BaseType.Validate(value) { return 0, errors.New("Invalid value") } for i := 0; i < len(self.Values); i++ { if self.Values[i] == value { return i, nil } } return 0, errors.New("No such element") } func (self *CustomEnum) Validate(value string) bool { _, err := self.ToId(value) return err == nil } func (self *CustomEnum) ToHTML(value string) (template.HTML, error) { selectItem := value != "" value_id := 0 if selectItem { var err error value_id, err = self.ToId(value) if err != nil { return template.HTML(""), err } } ret := `` return template.HTML(ret), nil } func (self *CustomEnum) DefaultValue() string { return "" } type CustomStruct struct { name string Variables []Variable } func (self *CustomStruct) Name() string { return self.name } func (self *CustomStruct) Validate(s string) bool { //FIXME log.Println("Custom struct Validate is not (yet) implemented") return false } func (self *CustomStruct) ToHTML(value string) (template.HTML, error) { //FIXME: implement return template.HTML(""), nil } func (self *CustomStruct) DefaultValue() string { //FIXME: non-generic version return "" } func parseCustomEnum(root xmlNode) (*CustomEnum, error) { customEnum := CustomEnum {} nameOk := false; typeOk := false; for i := 0; i < len(root.Attrs); i++ { attrName := root.Attrs[i].Name.Local attrVal := root.Attrs[i].Value switch attrName { case "name": nameOk = true customEnum.name = attrVal case "base-type": typeOk = true customEnum.BaseType = makeVariableType(attrVal) switch customEnum.BaseType.(type) { case *VariableType_badType: typeOk = false } } } if !nameOk { return nil, errors.New("Unnamed custom enum") } if !typeOk { return nil, errors.New("Bad base type for " + customEnum.Name()) } for i := 0; i < len(root.Nodes); i++ { node := root.Nodes[i] switch node.XMLName.Local { case "value": value := string(node.Content) if customEnum.BaseType.Validate(value) { customEnum.Values = append(customEnum.Values, value) } else { log.Println("Cannot validate value enum \"" + value + "\" with type \"" + customEnum.BaseType.Name() + "\". Discarded") } } } return &customEnum, nil } func parseCustomStruct(root xmlNode) (*CustomStruct, error) { customType := CustomStruct{} nameOk := false for i := 0; i < len(root.Attrs); i++ { attrName := root.Attrs[i].Name.Local attrVal := root.Attrs[i].Value switch attrName { case "name": nameOk = true customType.name = attrVal } } if !nameOk { return nil, errors.New("Unnamed custom type") } return &customType, nil } func parseCustomTypes(root xmlNode) ([]VariableType, error) { types := []VariableType{} for i := 0; i < len(root.Nodes); i++ { node := root.Nodes[i] switch node.XMLName.Local { case "enum": customEnum, err := parseCustomEnum(node) if err != nil { return []VariableType{}, err } types = append(types, customEnum) case "type": customType, err := parseCustomStruct(node) if err != nil { return []VariableType{}, err } types = append(types, customType) default: return []VariableType{}, errors.New("Unknown CustomType: " + root.Nodes[i].XMLName.Local) } } return types, nil }