You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

378 lines
8.6 KiB

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 := `<input type="checkbox"`
if(val) {
ret += ` checked`
}
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 := `<input type="number" step="1" value="` + strconv.FormatInt(val, 10) + `"/>`
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 := `<input type="number" value="` + strconv.FormatFloat(val, 'g', -1, 64) + `"/>`
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 := `<input type="text" value="` + template.HTMLEscapeString(value) + `"/>`
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 := `<input type="text" value="` + template.HTMLEscapeString(value) +`"/>`
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 := `<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() 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
}