305 lines
5.8 KiB
Go
305 lines
5.8 KiB
Go
|
package analyze
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"encoding/xml"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"legendsbrowser/util"
|
||
|
"log"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
|
||
|
"github.com/iancoleman/strcase"
|
||
|
)
|
||
|
|
||
|
func Analyze(filex string) {
|
||
|
files, err := filepath.Glob("*.xml")
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
fmt.Println(files)
|
||
|
|
||
|
files = []string{filex}
|
||
|
|
||
|
a := NewAnalyzeData()
|
||
|
|
||
|
for _, file := range files {
|
||
|
xmlFile, err := os.Open(file)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
}
|
||
|
|
||
|
fmt.Println("Successfully Opened", file)
|
||
|
defer xmlFile.Close()
|
||
|
|
||
|
converter := util.NewConvertReader(xmlFile)
|
||
|
analyze(converter, a)
|
||
|
}
|
||
|
|
||
|
createMetadata(a)
|
||
|
}
|
||
|
|
||
|
type analyzeData struct {
|
||
|
path []string
|
||
|
types *map[string]bool
|
||
|
fields *map[string]bool
|
||
|
isString *map[string]bool
|
||
|
multiple *map[string]bool
|
||
|
}
|
||
|
|
||
|
func NewAnalyzeData() analyzeData {
|
||
|
path := make([]string, 0)
|
||
|
types := make(map[string]bool, 0)
|
||
|
fields := make(map[string]bool, 0)
|
||
|
isString := make(map[string]bool, 0)
|
||
|
multiple := make(map[string]bool, 0)
|
||
|
|
||
|
return analyzeData{
|
||
|
path: path,
|
||
|
types: &types,
|
||
|
fields: &fields,
|
||
|
isString: &isString,
|
||
|
multiple: &multiple,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func analyzeElement(d *xml.Decoder, a analyzeData) error {
|
||
|
if len(a.path) > 1 {
|
||
|
(*a.fields)[strings.Join(a.path, ">")] = true
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
data []byte
|
||
|
)
|
||
|
value := true
|
||
|
|
||
|
fields := make(map[string]bool)
|
||
|
|
||
|
Loop:
|
||
|
for {
|
||
|
tok, err := d.Token()
|
||
|
if err == io.EOF {
|
||
|
break Loop
|
||
|
} else if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch t := tok.(type) {
|
||
|
case xml.StartElement:
|
||
|
value = false
|
||
|
|
||
|
(*a.types)[strings.Join(a.path, ">")] = true
|
||
|
|
||
|
a2 := a
|
||
|
a2.path = append(a.path, t.Name.Local)
|
||
|
|
||
|
if _, ok := fields[t.Name.Local]; ok {
|
||
|
(*a.multiple)[strings.Join(a2.path, ">")] = true
|
||
|
}
|
||
|
fields[t.Name.Local] = true
|
||
|
|
||
|
analyzeElement(d, a2)
|
||
|
|
||
|
case xml.CharData:
|
||
|
data = append(data, t...)
|
||
|
|
||
|
case xml.EndElement:
|
||
|
if value {
|
||
|
if _, err := strconv.Atoi(string(data)); err != nil {
|
||
|
(*a.isString)[strings.Join(a.path, ">")] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if t.Name.Local == "type" {
|
||
|
a.path[len(a.path)-2] = a.path[len(a.path)-2] + strcase.ToCamel(string(data))
|
||
|
fmt.Println(a.path)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func analyze(r io.Reader, a analyzeData) error {
|
||
|
d := xml.NewDecoder(r)
|
||
|
return analyzeElement(d, a)
|
||
|
}
|
||
|
|
||
|
func createMetadata(a analyzeData) {
|
||
|
ts := util.Keys(*a.types)
|
||
|
sort.Strings(ts)
|
||
|
|
||
|
fs := util.Keys(*a.fields)
|
||
|
sort.Strings(fs)
|
||
|
|
||
|
objects := make(map[string]Object, 0)
|
||
|
|
||
|
for _, k := range ts {
|
||
|
if ok, _ := isArray(k, fs); !ok {
|
||
|
n := k
|
||
|
if strings.Contains(k, ">") {
|
||
|
n = k[strings.LastIndex(k, ">")+1:]
|
||
|
}
|
||
|
|
||
|
if n == "" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
objFields := make(map[string]Field, 0)
|
||
|
|
||
|
fmt.Println("\n", n)
|
||
|
for _, f := range fs {
|
||
|
if strings.HasPrefix(f, k+">") {
|
||
|
fn := f[len(k)+1:]
|
||
|
if !strings.Contains(fn, ">") {
|
||
|
fmt.Println(" ", fn)
|
||
|
|
||
|
if ok, elements := isArray(f, fs); ok {
|
||
|
el := elements[strings.LastIndex(elements, ">")+1:]
|
||
|
objFields[fn] = Field{
|
||
|
Name: strcase.ToCamel(fn),
|
||
|
Type: "array",
|
||
|
ElementType: &(el),
|
||
|
}
|
||
|
} else if ok, _ := isObject(f, fs); ok {
|
||
|
objFields[fn] = Field{
|
||
|
Name: strcase.ToCamel(fn),
|
||
|
Type: "object",
|
||
|
Multiple: (*a.multiple)[f],
|
||
|
}
|
||
|
} else if (*a.isString)[f] {
|
||
|
objFields[fn] = Field{
|
||
|
Name: strcase.ToCamel(fn),
|
||
|
Type: "string",
|
||
|
Multiple: (*a.multiple)[f],
|
||
|
}
|
||
|
} else {
|
||
|
objFields[fn] = Field{
|
||
|
Name: strcase.ToCamel(fn),
|
||
|
Type: "int",
|
||
|
Multiple: (*a.multiple)[f],
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
objects[n] = Object{
|
||
|
Name: strcase.ToCamel(n),
|
||
|
Id: (*a.fields)[k+">id"],
|
||
|
Named: (*a.fields)[k+">name"],
|
||
|
Typed: (*a.fields)[k+">type"],
|
||
|
Fields: objFields,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
file, _ := json.MarshalIndent(objects, "", " ")
|
||
|
_ = ioutil.WriteFile("model.json", file, 0644)
|
||
|
|
||
|
f, err := os.Create("contributors.go")
|
||
|
defer f.Close()
|
||
|
|
||
|
err = packageTemplate.Execute(f, struct {
|
||
|
Objects map[string]Object
|
||
|
}{
|
||
|
Objects: objects,
|
||
|
})
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isArray(typ string, types []string) (bool, string) {
|
||
|
fc := 0
|
||
|
elements := ""
|
||
|
|
||
|
for _, t := range types {
|
||
|
if !strings.HasPrefix(t, typ+">") {
|
||
|
continue
|
||
|
}
|
||
|
f := t[len(typ)+1:]
|
||
|
if strings.Contains(f, ">") {
|
||
|
continue
|
||
|
}
|
||
|
fc++
|
||
|
elements = t
|
||
|
}
|
||
|
return fc == 1, elements
|
||
|
}
|
||
|
|
||
|
func isObject(typ string, types []string) (bool, string) {
|
||
|
fc := 0
|
||
|
|
||
|
for _, t := range types {
|
||
|
if !strings.HasPrefix(t, typ+">") {
|
||
|
continue
|
||
|
}
|
||
|
fc++
|
||
|
}
|
||
|
return fc > 0, typ
|
||
|
}
|
||
|
|
||
|
type Object struct {
|
||
|
Name string `json:"name"`
|
||
|
Id bool `json:"id,omitempty"`
|
||
|
Named bool `json:"named,omitempty"`
|
||
|
Typed bool `json:"typed,omitempty"`
|
||
|
Fields map[string]Field `json:"fields"`
|
||
|
}
|
||
|
|
||
|
type Field struct {
|
||
|
Name string `json:"name"`
|
||
|
Type string `json:"type"`
|
||
|
Multiple bool `json:"multiple,omitempty"`
|
||
|
ElementType *string `json:"elements,omitempty"`
|
||
|
}
|
||
|
|
||
|
func (f Field) TypeLine(objects map[string]Object) string {
|
||
|
n := f.Name
|
||
|
|
||
|
if n == "Id" || n == "Name" {
|
||
|
n = n + "_"
|
||
|
}
|
||
|
|
||
|
m := ""
|
||
|
if f.Multiple {
|
||
|
m = "[]"
|
||
|
}
|
||
|
t := f.Type
|
||
|
if f.Type == "array" {
|
||
|
t = "map[int]*" + objects[*f.ElementType].Name
|
||
|
}
|
||
|
if f.Type == "object" {
|
||
|
t = f.Name
|
||
|
}
|
||
|
j := "`json:\"" + strcase.ToLowerCamel(f.Name) + "\"`"
|
||
|
return fmt.Sprintf("%s %s%s %s", n, m, t, j)
|
||
|
}
|
||
|
|
||
|
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
||
|
package generate
|
||
|
|
||
|
{{- range $name, $obj := .Objects }}
|
||
|
type {{ $obj.Name }} struct {
|
||
|
{{- range $fname, $field := $obj.Fields }}
|
||
|
{{ $field.TypeLine $.Objects }}
|
||
|
{{- end }}
|
||
|
}
|
||
|
|
||
|
{{- if $obj.Id }}
|
||
|
func (x *{{ $obj.Name }}) Id() int { return x.Id_ }
|
||
|
{{- end }}
|
||
|
{{- if $obj.Named }}
|
||
|
func (x *{{ $obj.Name }}) Name() string { return x.Name_ }
|
||
|
{{- end }}
|
||
|
|
||
|
{{- end }}
|
||
|
`))
|