package jdr
import (
"html/template"
"encoding/xml"
"errors"
"os"
"log"
)
type Page struct {
Items []SheetItem
}
func (self *Page) ToHTML() template.HTML {
ret := ""
for _, item := range(self.Items) {
ret += string(item.ToHTML())
}
return template.HTML(ret)
}
type SheetItem interface {
Name() string
ToHTML() template.HTML
}
type Block struct {
name string
Items []SheetItem
}
func (self *Block) Name() string {
return self.name
}
func (self *Block) ToHTML() template.HTML {
begin := `
`
ret := template.HTML(begin)
if self.Name() != "" {
ret += template.HTML(legend)
}
for _, item := range(self.Items) {
ret += item.ToHTML()
}
ret += template.HTML(end)
return ret
}
type Variable struct {
Type VariableType
name string
}
func (self *Variable) Name() string {
return self.name
}
func (self *Variable) ToHTML() template.HTML {
label := ""
if self.name != "" {
label = ``
}
html, err := self.Type.ToHTML(self.Type.DefaultValue())
if err != nil {
log.Fatal(err)
}
return template.HTML(label) + html + template.HTML(`
`)
}
type CharacterSheet struct {
types map[string]VariableType
Pages []*Page
}
func (self *CharacterSheet) Render() template.HTML {
ret := template.HTML("")
for _, page := range(self.Pages) {
ret += page.ToHTML()
}
return ret
}
func (self *CharacterSheet) AddType(t VariableType) {
if self.types == nil {
self.types = make(map[string]VariableType)
}
name := t.Name()
self.types[name] = t
}
func (self *CharacterSheet) AddDefaultTypes() {
self.AddType(&VariableType_bool{})
self.AddType(&VariableType_int{})
self.AddType(&VariableType_float{})
self.AddType(&VariableType_string{})
self.AddType(&VariableType_d{})
}
func (self *CharacterSheet) getType(typename string) (VariableType, error) {
t, ok := self.types[typename]
if !ok {
return &VariableType_badType{}, errors.New("unknown type \"" + typename + "\"")
}
return t, nil
}
type xmlNode struct {
XMLName xml.Name
Attrs []xml.Attr `xml:"-"`
Content []byte `xml:",innerxml"`
Nodes []xmlNode `xml:",any"`
}
func (n *xmlNode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
n.Attrs = start.Attr
type node xmlNode
return d.DecodeElement((*node)(n), &start)
}
func parseVariable(root xmlNode, sheet *CharacterSheet, defaults *Variable) (*Variable, error) {
ret := &Variable{}
typeOk := false
nameOk := false
if defaults != nil {
switch defaults.Type.(type) {
case *VariableType_badType:
typeOk = false
default:
typeOk = true
ret.Type = defaults.Type
}
}
for i := 0; i < len(root.Attrs); i++ {
attrName := root.Attrs[i].Name.Local
attrVal := root.Attrs[i].Value
switch attrName {
case "type":
t, err := sheet.getType(attrVal)
if err != nil {
return nil, err
}
ret.Type = t
typeOk = true
case "name":
ret.name = attrVal
nameOk = true
}
}
if !nameOk {
return nil, errors.New("Unnamed variable")
}
if !typeOk {
return nil, errors.New("No type provided for variable \"" + ret.Name() + "\"")
}
return ret, nil
}
func parseBlock(root xmlNode, sheet *CharacterSheet) (*Block, error) {
ret := &Block{}
for i := 0; i < len(root.Attrs); i++ {
attrName := root.Attrs[i].Name.Local
attrVal := root.Attrs[i].Value
switch attrName {
case "name":
ret.name = attrVal
}
}
for i := 0; i < len(root.Nodes); i++ {
items, err := parseItem(root.Nodes[i], sheet)
if err != nil {
return nil, err
}
for _, item := range(items) {
ret.Items = append(ret.Items, item)
}
}
return ret, nil
}
func parseVariableGroup(root xmlNode, sheet *CharacterSheet, defaults *Variable) ([]*Variable, error) {
ret := []*Variable{}
typeOk := false
if defaults == nil {
defaults = &Variable{}
} else {
typeOk = true
}
for i := 0; i < len(root.Attrs); i++ {
attrName := root.Attrs[i].Name.Local
attrVal := root.Attrs[i].Value
switch attrName {
case "default-type":
t, err := sheet.getType(attrVal)
if err != nil {
return nil, err
}
defaults.Type = t
typeOk = true
}
}
if !typeOk {
defaults.Type = &VariableType_badType{}
}
for i := 0; i < len(root.Nodes); i++ {
node := root.Nodes[i]
switch node.XMLName.Local {
case "variable":
item, err := parseVariable(node, sheet, defaults)
if err != nil {
return nil, err
}
ret = append(ret, item)
case "variable-group":
items, err := parseVariableGroup(node, sheet, defaults)
if err != nil {
return nil, err
}
for j := 0; j < len(items); j++ {
ret = append(ret, items[j])
}
default:
return nil, errors.New("Unknown variable-group item: \"" + node.XMLName.Local + "\"")
}
}
return ret, nil
}
func parseItem(root xmlNode, sheet *CharacterSheet) ([]SheetItem, error) {
switch root.XMLName.Local {
case "block":
block, err := parseBlock(root, sheet)
if err != nil {
return nil, err
}
return []SheetItem{block}, nil
case "variable":
variable, err := parseVariable(root, sheet, nil)
if err != nil {
return nil, err
}
return []SheetItem{variable}, err
case "variable-group":
variables, err := parseVariableGroup(root, sheet, nil)
if err != nil {
return nil, err
}
items := make([]SheetItem, len(variables))
for i, v := range variables {
items[i] = SheetItem(v)
}
return items, nil
default:
return nil, errors.New("Unknown item: " + root.XMLName.Local)
}
}
func parsePage(root xmlNode, sheet *CharacterSheet) (*Page, error) {
ret := &Page{}
for i := 0; i < len(root.Nodes); i++ {
items, err := parseItem(root.Nodes[i], sheet)
if err != nil {
return nil, err
}
for _, item := range(items) {
ret.Items = append(ret.Items, item)
}
}
return ret, nil
}
func parseSheet(root xmlNode) (*CharacterSheet, error) {
if root.XMLName.Local != "jdr-desc" {
return nil, errors.New("Wrong root node")
}
sheet := &CharacterSheet{}
sheet.AddDefaultTypes()
for i := 0; i < len(root.Nodes); i++ {
node := root.Nodes[i]
switch node.XMLName.Local {
case "custom-types":
err := parseCustomTypes(node, sheet)
if err != nil {
return nil, err
}
case "page":
page, err := parsePage(node, sheet)
if err != nil {
return nil, err
}
sheet.Pages = append(sheet.Pages, page)
default:
return nil, errors.New("Wrong format")
}
}
return sheet, nil
}
func ReadCharacterSheet(filename string) (*CharacterSheet, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
decoder := xml.NewDecoder(file)
var n xmlNode
err = decoder.Decode(&n)
if err != nil {
return nil, err
}
sheet, err := parseSheet(n)
if err != nil {
return nil, err
}
return sheet, nil
}