package jdr
|
|
|
|
import (
|
|
"html/template"
|
|
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
func validateCast(err error) bool {
|
|
if err != nil {
|
|
log.Println(err)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
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 validateCast(err)
|
|
}
|
|
|
|
func (self *VariableType_bool) ToHTML(value JsonValue) (template.HTML, error) {
|
|
val, err := self.ToBool(value)
|
|
if err != nil {
|
|
return template.HTML(""), err
|
|
}
|
|
|
|
ret := `<input type="checkbox"`
|
|
if val {
|
|
ret += ` checked`
|
|
}
|
|
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 validateCast(err)
|
|
}
|
|
|
|
func (self *VariableType_int) ToHTML(value JsonValue) (template.HTML, error) {
|
|
val, err := self.ToInt(value)
|
|
if err != nil {
|
|
return template.HTML(""), err
|
|
}
|
|
|
|
ret := `<input type="number" step="1" value="` + strconv.FormatInt(val, 10) + `"/>`
|
|
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 validateCast(err)
|
|
}
|
|
|
|
func (self *VariableType_float) ToHTML(value JsonValue) (template.HTML, error) {
|
|
val, err := self.ToFloat(value)
|
|
if err != nil {
|
|
return template.HTML(""), err
|
|
}
|
|
|
|
ret := `<input type="number" value="` + strconv.FormatFloat(val, 'g', -1, 64) + `"/>`
|
|
|
|
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 validateCast(err)
|
|
}
|
|
|
|
func (self *VariableType_string) ToHTML(value JsonValue) (template.HTML, error) {
|
|
str, err := self.ToString(value)
|
|
if err != nil {
|
|
return template.HTML(""), err
|
|
}
|
|
|
|
ret := `<input type="text" value="` + template.HTMLEscapeString(str) + `"/>`
|
|
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 := `<input type="text" value="` /*+ template.HTMLEscapeString(value)*/ + `"/>`
|
|
|
|
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 validateCast(err)
|
|
}
|
|
|
|
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 := `<select>`
|
|
for i, v := range self.Values {
|
|
ret += `<option`
|
|
if selectItem && i == value_id {
|
|
ret += ` selected`
|
|
}
|
|
ret += `>`
|
|
ret += template.HTMLEscapeString(v)
|
|
ret += `</option>`
|
|
}
|
|
ret += `</select>`
|
|
|
|
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 += `<div>` //FIXME: better tag
|
|
for _, v := range self.Variables {
|
|
if v.Name() != "" {
|
|
label := `<label>` + template.HTMLEscapeString(v.Name()) + `</label>`
|
|
ret += template.HTML(label)
|
|
}
|
|
|
|
html, err := v.Type.ToHTML(obj[v.Name()])
|
|
if err != nil {
|
|
return template.HTML(""), err
|
|
}
|
|
|
|
ret += html
|
|
}
|
|
ret += `</div>`
|
|
|
|
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
|
|
}
|