package jdr import ( //"html/template" "encoding/xml" "errors" "os" ) type Page struct { Items []SheetItem } type SheetItem interface{ } type Block struct { Items []SheetItem } type Variable struct { Type VariableType Name string } type CharacterSheet struct { types map[string]VariableType Pages []*Page } 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) (*Variable, error) { ret := &Variable{} typeOk := false nameOk := false 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.Nodes); i++ { item, err := parseItem(root.Nodes[i], sheet) if err != nil { return nil, err } ret.Items = append(ret.Items, item) } return ret, nil } func parseItem(root xmlNode, sheet *CharacterSheet) (SheetItem, error) { switch root.XMLName.Local { case "block": return parseBlock(root, sheet) case "variable": return parseVariable(root, sheet) 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++ { item, err := parseItem(root.Nodes[i], sheet) if err != nil { return nil, err } 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": types, err := parseCustomTypes(node) if err != nil { return nil, err } for j := 0; j < len(types); j++ { sheet.AddType(types[j]) } 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 }