f5/metatable.go
aozhiwei c3814ee3af 1
2024-08-03 18:20:08 +08:00

258 lines
5.1 KiB
Go

package f5
import (
"encoding/json"
"fmt"
"math/rand"
"q5"
"reflect"
)
type MetaTable interface {
IsNoLoad() bool
Load()
PreInit1()
ElementsInit(int)
PostInit1()
}
type LoadFromKeyValue interface {
LoadFromKv(map[string]interface{})
}
type RawMetaTable[T any] struct {
FileName string
PrimKey string
NoLoad bool
rawList []*T
}
type IdMetaTable[T any] struct {
RawMetaTable[T]
idHash *q5.ConcurrentMap[int64, *T]
}
type NameMetaTable[T any] struct {
RawMetaTable[T]
nameHash *q5.ConcurrentMap[string, *T]
}
type CustomMetaTable struct {
}
func (this *CustomMetaTable) IsNoLoad() bool {
return false
}
func (this *CustomMetaTable) Load() {
}
func (this *CustomMetaTable) PreInit1() {
}
func (this *CustomMetaTable) ElementsInit(int) {
}
func (this *CustomMetaTable) PostInit1() {
}
func (this *RawMetaTable[T]) Traverse(cb func(*T) bool) {
for _, val := range this.rawList {
if !cb(val) {
break
}
}
}
func (this *RawMetaTable[T]) IsNoLoad() bool {
return this.NoLoad
}
func (this *RawMetaTable[T]) PreInit1() {
}
func (this *RawMetaTable[T]) PostInit1() {
}
func (this *RawMetaTable[T]) RandElement() *T {
if len(this.rawList) > 0 {
return this.rawList[rand.Intn(len(this.rawList))]
}
return nil
}
func (this *RawMetaTable[T]) ElementsInit(round int) {
type RoundInit1 interface {
Init1()
}
type RoundInit2 interface {
Init2()
}
type RoundInit3 interface {
Init3()
}
this.Traverse(func(obj *T) bool {
var x interface{} = obj
switch round {
case 0:
if init, ok := x.(RoundInit1); ok {
init.Init1()
}
case 1:
if init, ok := x.(RoundInit2); ok {
init.Init2()
}
case 2:
if init, ok := x.(RoundInit3); ok {
init.Init3()
}
}
return true
})
}
func (this *IdMetaTable[T]) GetById(id int64) *T {
if v, ok := this.idHash.Load(id); ok {
return *v
} else {
return nil
}
}
func (this *IdMetaTable[T]) RandElementExclude(cb func(*T) bool) *T {
if len(this.rawList) > 0 {
tryCount := 1
for tryCount < 1000 {
meta := this.rawList[rand.Intn(len(this.rawList))]
if cb(meta) {
return meta
}
}
}
return nil
}
func (this *RawMetaTable[T]) Load() {
if this.NoLoad {
return
}
if jsonStr, err := ReadJsonFile(this.FileName); err == nil {
switch q5.JsonStrType(jsonStr) {
case q5.JSON_ARRAY:
break
case q5.JSON_OBJECT:
jsonStr = "[" + jsonStr + "]"
default:
panic(fmt.Sprintf("error json format %s", this.FileName))
}
var rows []map[string]interface{}
err1 := json.Unmarshal([]byte(jsonStr), &rows)
if err1 != nil {
panic(fmt.Sprintf("load metafile json decode aerror %s %s", this.FileName, err1))
}
for _, row := range rows {
var obj = new(T)
var x interface{} = obj
if loader, ok := x.(LoadFromKeyValue); ok {
loader.LoadFromKv(row)
}
this.rawList = append(this.rawList, obj)
}
} else {
panic(fmt.Sprintf("load metafile error %s %s", this.FileName, err))
}
}
func (this *IdMetaTable[T]) Load() {
this.RawMetaTable.Load()
this.idHash = new(q5.ConcurrentMap[int64, *T])
i := int64(0)
getFuncName := "Get" + q5.ConvertUpperCamelCase(this.PrimKey)
this.Traverse(func(obj *T) bool {
if this.PrimKey == "" {
this.idHash.Store(i, obj)
} else {
in := []reflect.Value{}
method := reflect.ValueOf(obj).MethodByName(getFuncName)
out := method.Call(in)
if key, err := q5.ToInt64Ex(out[0].Interface()); err == nil {
this.idHash.Store(key, obj)
} else {
panic("IdMetaTable load PrimKey error")
}
}
i++
return true
})
}
func (this *NameMetaTable[T]) GetByName(name string) *T {
if v, ok := this.nameHash.Load(name); ok {
return *v
} else {
return nil
}
}
func (this *NameMetaTable[T]) Load() {
this.RawMetaTable.Load()
this.nameHash = new(q5.ConcurrentMap[string, *T])
i := int64(0)
getFuncName := "Get" + q5.ConvertUpperCamelCase(this.PrimKey)
this.Traverse(func(obj *T) bool {
in := []reflect.Value{}
method := reflect.ValueOf(obj).MethodByName(getFuncName)
out := method.Call(in)
if key, err := q5.ToStringEx(out[0].Interface()); err == nil {
this.nameHash.Store(key, obj)
} else {
panic("NameMetaTable load PrimKey error")
}
i++
return true
})
}
func ReadMetaTableField[T string | int | int32 | int64 | float32 | float64](
fieldPtr *T, fieldName string, flags *uint64, flagIdx uint64,
kv map[string]interface{}) {
if val, ok := kv[fieldName]; ok {
if !q5.DuckToSimple(val, fieldPtr) {
panic("ReadMetaTableField error")
}
*flags |= uint64(1) << flagIdx
}
}
func LoadMetaTable(table interface{}) {
ele := reflect.ValueOf(table).Elem()
for i := 0; i < ele.NumField(); i++ {
var tbl MetaTable = ele.Field(i).Interface().(MetaTable)
if !tbl.IsNoLoad() {
tbl.PreInit1()
}
}
for i := 0; i < ele.NumField(); i++ {
var tbl MetaTable = ele.Field(i).Interface().(MetaTable)
if !tbl.IsNoLoad() {
tbl.Load()
}
}
for i := 0; i < 3; i++ {
for ii := 0; ii < ele.NumField(); ii++ {
var tbl MetaTable = ele.Field(ii).Interface().(MetaTable)
tbl.ElementsInit(i)
}
}
for i := 0; i < ele.NumField(); i++ {
var tbl MetaTable = ele.Field(i).Interface().(MetaTable)
if !tbl.IsNoLoad() {
tbl.PostInit1()
}
}
}