Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BindDefaultValue #10

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions value_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mapper

import (
"reflect"
"strconv"
"strings"
"time"
)

func strFunc(str string) reflect.Value {
return reflect.ValueOf(str)
}

//Support computing
func int64Func(str string) reflect.Value {
str = strings.TrimSpace(str)
if strings.Contains(str, "*") {
ss := strings.Split(str, "*")
var r int64
r = 1
for _, s := range ss {
r = r * int64Func(s).Int()
}
return reflect.ValueOf(r)
} else {
r, _ := strconv.ParseInt(str, 10, 64)
return reflect.ValueOf(r)
}
}
func uint64Func(str string) reflect.Value {
r, _ := strconv.ParseUint(str, 10, 64)
return reflect.ValueOf(r)
}
func boolFunc(str string) reflect.Value {
b, _ := strconv.ParseBool(str)
return reflect.ValueOf(b)
}

func float64Func(str string) reflect.Value {
r, _ := strconv.ParseFloat(str, 64)
return reflect.ValueOf(r)
}

func dateTimeFunc(str string) reflect.Value {
s := strings.Split(str, ";")
var t time.Time
if len(s) == 1 {
t, _ = time.ParseInLocation(defaultTimeLayout, str, time.Local)
} else if len(s) == 2 {
t, _ = time.ParseInLocation(s[1], s[0], time.Local)
}
return reflect.ValueOf(t)
}
206 changes: 206 additions & 0 deletions values_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package mapper

import (
"reflect"
"sync"
"time"
)

type DefaultInitValue interface {
Default() reflect.Value
}

var (
_defaultTag = "default"
_notDefaultTag = "-"
)
var defaultTimeLayout = "2006-01-02 15:04:05"

func SetDefaultTimeLayout(layout string) {
mux.Lock()
defer mux.Unlock()
defaultTimeLayout = layout
}
func SetDefaultTag(tag string) {
mux.Lock()
defer mux.Unlock()
_defaultTag = tag
}

type DefaultValueFunc func(str string) reflect.Value

var regTypeFuncMap map[reflect.Type]DefaultValueFunc
var regKindFuncMap map[reflect.Kind]DefaultValueFunc
var mux sync.RWMutex

func RegisterTypeForDefaultValue(t reflect.Type, f DefaultValueFunc) {
mux.Lock()
defer mux.Unlock()
regTypeFuncMap[t] = f
}

func RegisterKindForDefaultValue(t reflect.Kind, f DefaultValueFunc) {
mux.Lock()
defer mux.Unlock()
regKindFuncMap[t] = f
}

func init() {
regTypeFuncMap = make(map[reflect.Type]DefaultValueFunc)
regKindFuncMap = make(map[reflect.Kind]DefaultValueFunc)
RegisterTypeForDefaultValue(reflect.TypeOf(time.Time{}), dateTimeFunc)

RegisterKindForDefaultValue(reflect.Int64, int64Func)
RegisterKindForDefaultValue(reflect.Uint64, uint64Func)
}
func getFuncByKind(k reflect.Kind) DefaultValueFunc {
mux.RLock()
defer mux.RUnlock()
return regKindFuncMap[k]
}
func getFuncByType(k reflect.Type) DefaultValueFunc {
mux.RLock()
defer mux.RUnlock()
return regTypeFuncMap[k]
}

func getDefaultValueAndCheckEmbedded(v reflect.Value) (tv reflect.Value, ok bool) {
if !implDefaultInitValue(v.Type()) || (v.Type().Kind() == reflect.Ptr && v.Elem() == ZeroValue) {
return
}
t := v.Type()
structV := v
if t.Kind() == reflect.Ptr {
t = v.Elem().Type()
structV = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
if implDefaultInitValue(f.Type) {
if structV.Field(i).Kind() == reflect.Ptr && structV.Field(i).IsNil() {
return
} else {
break
}
}
}
}
dv, _ := v.Interface().(DefaultInitValue)
tv = dv.Default()
ok = true
return
}

func bindValue(v reflect.Value, tag string) {
t := v.Type()
if t.Kind() == reflect.Ptr && v.Elem() != ZeroValue {
t = v.Elem().Type()
}
if tv, ok := getDefaultValueAndCheckEmbedded(v); ok {
//dv, _ := v.Interface().(DefaultInitValue)
////TODO 此处无法判断 指针类型带来的接口实现 会造成 default 执行报错,暂时利用深层以上方式 检测内嵌field是否实现了接口。
//tv := dv.Default()

if v.Type().Kind() == reflect.Ptr {
if tv.Type() == t {
v.Elem().Set(tv)
return
} else {
if filed, ok := v.Elem().Type().FieldByName(tv.Type().Name()); ok && canDefaultBind(filed, tag) {
v.Elem().FieldByName(tv.Type().Name()).Set(tv)
}
}
} else {
if tv.Type() == t {
v.Set(tv)
return
} else {
}
}

}
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
if v.Type().Kind() == reflect.Ptr {
if canDefaultBind(v.Elem().Type().Field(i), tag) && !checkSampleValue(v.Elem().Field(i), v.Elem().Type().Field(i), tag) {
bindValue(v.Elem().Field(i), tag)
}
} else {
if canDefaultBind(v.Type().Field(i), tag) && !checkSampleValue(v.Field(i), v.Type().Field(i), tag) {
bindValue(v.Field(i), tag)
}
}
}
}
}
func canDefaultBind(field reflect.StructField, tag string) bool {
if t, ok := field.Tag.Lookup(tag); ok {
return t != _notDefaultTag
}
return true
}
func checkSampleValue(v reflect.Value, field reflect.StructField, tag string) (ok bool) {
if tagValue, ok := field.Tag.Lookup(tag); ok {
if tagValue == _notDefaultTag {
return true
} else {
if f := getFuncByType(v.Type()); f != nil {
if tv := f(tagValue); tv.Type() == v.Type() {
v.Set(tv)
ok = true
}
} else {
if f := getFuncByKind(v.Kind()); f != nil {
t := f(tagValue)
if v.Kind() == t.Kind() {
v.Set(t)
ok = true
}
} else {
switch v.Kind() {
case reflect.String:
v.SetString(strFunc(tagValue).String())
ok = true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(int64Func(tagValue).Int())
ok = true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.SetUint(uint64Func(tagValue).Uint())
ok = true
case reflect.Bool:
v.SetBool(boolFunc(tagValue).Bool())
ok = true
case reflect.Float32, reflect.Float64:
v.SetFloat(float64Func(tagValue).Float())
ok = true
}
}
}
}
}
return
}

func implDefaultInitValue(t reflect.Type) bool {
if t.Kind() == reflect.Ptr {
return t.Elem().Implements(reflect.TypeOf((*DefaultInitValue)(nil)).Elem())
} else {
return t.Implements(reflect.TypeOf((*DefaultInitValue)(nil)).Elem())
}
}

func defaultTag(tags ...string) string {
if len(tags) > 0 {
return tags[0]
} else {
return _defaultTag
}
}

//default tagKey d
func BindDefaultValue(target interface{}, tags ...string) {
if reflect.TypeOf(target).Kind() == reflect.Ptr {
bindValue(reflect.ValueOf(target), defaultTag(tags...))
}
}
82 changes: 82 additions & 0 deletions values_default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package mapper

import (
"encoding/json"
"log"
"reflect"
"testing"
"time"
)

func init() {
SetDefaultTag("default")
log.SetFlags(log.Lshortfile | log.LstdFlags)
}

//func (s InfoForDefaultValueFunc) Default() reflect.Value {
// return reflect.ValueOf(InfoForDefaultValueFunc{Name: "test name"})
//}

type InfoForDefaultValueFunc struct {
Ext string `default:"aaa"`
Name string `default:"123"`
AA string `default:"aaaaaa"`
Int int `default:"12*11"`
}
type Info struct {
Name string `default:"123"`
Ext string `default:"aaa"`
BoolFalse bool `default:"false"`
BoolTrue bool `default:"true"`
Float float32 `default:"12.21"`
Float64 float64 `default:"12.51"`
Int int `default:"12*11"`
Int64 int64 `default:"12*11"`
NumberUint uint `default:"12"`
}

func (s AnyInfo) Default() reflect.Value {
return reflect.ValueOf(AnyInfo{AnyInfoName: "test anyInfo name~~~~"})
}

type AnyInfo struct {
AnyInfoName string `default:"dddddanyInfo"`
}
type ReStr string
type ReInt int
type ReInfo Info
type ReBool bool
type ShowInfo struct {
Info
*AnyInfo
ReInfo ReInfo
AnyInfo2 AnyInfo
ReInt ReInt `default:"10"`
ReStr ReStr `default:"reStr"`
ReBool ReBool `default:"true"`
InfoForDefaultValueFunc InfoForDefaultValueFunc
CreateTime time.Time `default:"2020-01-02 03:04:01"`
}

func TestBindDefaultValue(t *testing.T) {
t0 := time.Now()
var s ShowInfo
BindDefaultValue(&s)
data, _ := json.Marshal(s)
log.Printf("%+v", s)
log.Printf("%s %v", data, time.Now().Sub(t0))
}

func BenchmarkBindDefaultValue(b *testing.B) {
var s InfoForDefaultValueFunc
for i := 0; i < b.N; i++ {
BindDefaultValue(&s)
}
}
func BenchmarkBindDefaultValue2(b *testing.B) {
data := []byte(`{"Name":"123","Ext":"aaa","BoolFalse":false,"BoolTrue":true,"Float":12.21,"Float64":12.51,"Int":132,"Int64":132,"NumberUint":12,"AnyInfo2":{"AnyInfoName":"test anyInfo name~~~~"},"InfoForDefaultValueFunc":{"Ext":"aaa","Name":"123"},"CreateTime":"2020-01-02T03:04:01+08:00"}`)
for i := 0; i < b.N; i++ {
var s ShowInfo
_ = json.Unmarshal(data, &s)
}
}