package jdr import( "html/template" "fmt" "log" "errors" "strings" "strconv" "regexp" ) var regexp_d = regexp.MustCompile(`^[1-9][0-9]*d([468]|1[02]|20|100)$`) type JsonValue interface{} type VariableType interface { Name() string Validate(value JsonValue) bool ToHTML(value JsonValue) (template.HTML, error) DefaultValue() JsonValue } type VariableType_badType struct {} func (*VariableType_badType) Name() string { return "unknown type" } func (*VariableType_badType) Validate(s JsonValue) bool { return false } func (*VariableType_badType) ToHTML(value JsonValue) (template.HTML, error) { return template.HTML(""), errors.New("unknown type") } func (*VariableType_badType) DefaultValue() JsonValue { return "" } type VariableType_bool struct {} func (*VariableType_bool) Name() string { return "bool" } func (*VariableType_bool) ToBool(value JsonValue) (bool, error) { val, isBool := value.(bool) if !isBool { return false, errors.New("Non boolean value") } return val, nil } func (self *VariableType_bool) Validate(value JsonValue) bool { _, err := self.ToBool(value) return err == nil } func (self *VariableType_bool) ToHTML(value JsonValue) (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() JsonValue { return false } type VariableType_int struct {} func (*VariableType_int) Name() string { return "int" } func (self *VariableType_int) ToInt(value JsonValue) (int64, error) { val, isInt := value.(int64) if !isInt { val2, isInt := value.(int) if isInt { val = int64(val2) } else { return 0, errors.New("Bad int type: " + fmt.Sprintf("%T", value)) } } return val, nil } func (self *VariableType_int) Validate(value JsonValue) bool { _, err := self.ToInt(value) return err == nil } func (self *VariableType_int) ToHTML(value JsonValue) (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() JsonValue { return 0 } type VariableType_float struct {} func (*VariableType_float) Name() string { return "float" } func (*VariableType_float) ToFloat(value JsonValue) (float64, error) { val, isFloat := value.(float64) if !isFloat { return 0.0, errors.New("Not a float") } return val, nil } func (self *VariableType_float) Validate(value JsonValue) bool { _, err := self.ToFloat(value) return err == nil } func (self *VariableType_float) ToHTML(value JsonValue) (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() JsonValue { return 0.0 } type VariableType_string struct {} func (*VariableType_string) Name() string { return "string" } func (self *VariableType_string) ToString(value JsonValue) (string, error) { val, isString := value.(string) if !isString { return "", errors.New("Not a string") } return val, nil } func (self *VariableType_string) Validate(value JsonValue) bool { _, err := self.ToString(value) return err != nil } func (self *VariableType_string) ToHTML(value JsonValue) (template.HTML, error) { str, err := self.ToString(value) if err != nil { return template.HTML(""), err } ret := `` return template.HTML(ret), nil } func (self *VariableType_string) DefaultValue() JsonValue { return "" } type VariableType_d struct {} func (*VariableType_d) Name() string { return "d" } func (*VariableType_d) Validate(value JsonValue) bool { //FIXME return false } func (self *VariableType_d) ToHTML(value JsonValue) (template.HTML, error) { //FIXME: non-generic version ret := `` return template.HTML(ret), nil } func (self *VariableType_d) DefaultValue() JsonValue { //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 Values []string } func (self *CustomEnum) Name() string { return self.name } func (self *CustomEnum) ToId(value JsonValue) (int, error) { val, isString := value.(string) if !isString { return 0, errors.New("Enum value not a string") } for i := 0; i < len(self.Values); i++ { if self.Values[i] == val { return i, nil } } return 0, errors.New("No such element") } func (self *CustomEnum) Validate(value JsonValue) bool { _, err := self.ToId(value) return err == nil } func (self *CustomEnum) ToHTML(value JsonValue) (template.HTML, error) { val, isString := value.(string) if !isString { return template.HTML(""), errors.New("Bad enum value") } selectItem := val != "" 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() JsonValue { return "" } type JsonObject map[string]interface{} type CustomStruct struct { name string Variables []*Variable } func (self *CustomStruct) Name() string { return self.name } func (self *CustomStruct) Validate(value JsonValue) bool { obj, isObj := value.(JsonObject) if !isObj { log.Println("Struct \"" + self.Name() + "\": value is not a JSON object") return false } for _, v := range self.Variables { if !v.Type.Validate(obj[v.Name()]) { log.Println("Struct \"" + self.Name() + "\": value \"" + v.Name() + "\" was not validated") return false } } return true } func (self *CustomStruct) ToHTML(value JsonValue) (template.HTML, error) { if !self.Validate(value) { return template.HTML(""), errors.New("Cannot validate struct") } obj, _ := value.(JsonObject) ret := template.HTML("") ret += `
` for _, v := range self.Variables { log.Println("variable:", v.Name()) html, err := v.Type.ToHTML(obj[v.Name()]) if err != nil { return template.HTML(""), err } ret += html } ret += `
` return ret, nil } func (self *CustomStruct) DefaultValue() JsonValue { ret := make(JsonObject) for _, v := range self.Variables { ret[v.Name()] = v.Type.DefaultValue() } return ret } func parseCustomEnum(root xmlNode) (*CustomEnum, error) { customEnum := CustomEnum {} 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 customEnum.name = attrVal } } if !nameOk { return nil, errors.New("Unnamed custom enum") } for i := 0; i < len(root.Nodes); i++ { node := root.Nodes[i] switch node.XMLName.Local { case "value": customEnum.Values = append(customEnum.Values, string(node.Content)) } } return &customEnum, nil } func parseCustomStruct(root xmlNode, sheet *CharacterSheet) (*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") } for i := 0; i < len(root.Nodes); i++ { node := root.Nodes[i] switch node.XMLName.Local { case "variable": variable, err := parseVariable(node, sheet, nil) if err != nil { return nil, err } customType.Variables = append(customType.Variables, variable) default: return nil, errors.New("Unknown tag in custom type: " + node.XMLName.Local) } } return &customType, nil } func parseCustomTypes(root xmlNode, sheet *CharacterSheet) (error) { 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 err } sheet.AddType(customEnum) case "type": customType, err := parseCustomStruct(node, sheet) if err != nil { return err } sheet.AddType(customType) default: return errors.New("Unknown CustomType: " + root.Nodes[i].XMLName.Local) } } return nil }