diff --git a/value_types.go b/value_types.go new file mode 100644 index 0000000..5bcff02 --- /dev/null +++ b/value_types.go @@ -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) +} diff --git a/values_default.go b/values_default.go new file mode 100644 index 0000000..f59ad0d --- /dev/null +++ b/values_default.go @@ -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...)) + } +} diff --git a/values_default_test.go b/values_default_test.go new file mode 100644 index 0000000..376509a --- /dev/null +++ b/values_default_test.go @@ -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) + } +}