Files
Go-StudyExample/SourceAnalysisAndTool/jinzhu/copier/copier.go
T
Huangzj b5a1909eb0 feat(Go-StudyExample):
2021/01/07:新增copier源码阅读和单测代码
2021-01-07 09:23:37 +08:00

399 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package copier
import (
"database/sql"
"errors"
"fmt"
"reflect"
"strings"
)
// These flags define options for tag handling
const (
// Denotes that a destination field must be copied to. If copying fails then a panic will ensue.
tagMust uint8 = 1 << iota
// Denotes that the program should not panic when the must flag is on and
// value is not copied. The program will return an error instead.
tagNoPanic
// Ignore a destation field from being copied to.
tagIgnore
// Denotes that the value as been copied
hasCopied
)
// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
return copy(toValue, fromValue, false)
}
type Option struct {
IgnoreEmpty bool
}
// CopyWithOption copy with option
func CopyWithOption(toValue interface{}, fromValue interface{}, option Option) (err error) {
return copy(toValue, fromValue, option.IgnoreEmpty)
}
func copy(toValue interface{}, fromValue interface{}, ignoreEmpty bool) (err error) {
var (
isSlice bool
amount = 1
from = indirect(reflect.ValueOf(fromValue)) //read note 判断是指针还是结构体类型来返回对应的参数的值
to = indirect(reflect.ValueOf(toValue)) //read note 判断是指针还是结构体类型来返回对应的参数的值
)
//read note 不可寻址的数据类型可以参考:https://github.com/hyper0x/Golang_Puzzlers/blob/master/src/puzzlers/article15/q1/demo35.go
if !to.CanAddr() {
return errors.New("copy to value is unaddressable")
}
// Return is from value is invalid
if !from.IsValid() {
return
}
//read note 返回参数的具体类型
fromType := indirectType(from.Type())
toType := indirectType(to.Type())
// Just set it if possible to assign
// And need to do copy anyway if the type is struct
//read note 判断是否非结构体并且可以直接赋值
if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
to.Set(from)
return
}
//read note from和to都是map
if fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map {
//read note 判断map的key的结构类型是否可以转换,因为这边已经判断是map,所以直接通过 .key来获取,不担心是否报错
if !fromType.Key().ConvertibleTo(toType.Key()) {
return
}
//read note 判断要转换的Map是否为空,为空则进行初始化
if to.IsNil() {
to.Set(reflect.MakeMapWithSize(toType, from.Len()))
}
//read note 遍历Map的所有key
for _, k := range from.MapKeys() {
//read note 根据to的key类型创建一个新的key
toKey := indirect(reflect.New(toType.Key()))
//read note 设置key的值
if !set(toKey, k) {
continue
}
//read note 设置value值
toValue := indirect(reflect.New(toType.Elem()))
if !set(toValue, from.MapIndex(k)) {
//read note 对嵌套的结构体进行copy
err = Copy(toValue.Addr().Interface(), from.MapIndex(k).Interface())
if err != nil {
continue
}
}
to.SetMapIndex(toKey, toValue)
}
}
//read note 只要有一个数据结构不是结构体直接返回?
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
return
}
//read note 切片处理:设置切片的长度
//read note 如果Result是Slice类型
if to.Kind() == reflect.Slice {
//read note 设置isSlice
isSlice = true
//read note amount初始是1,如果被复制的对象也是数组,则这边修改成数组长度,否则1表示只处理结构体
if from.Kind() == reflect.Slice {
amount = from.Len()
}
}
//read note 循环被复制的切片的长度
for i := 0; i < amount; i++ {
var dest, source reflect.Value
//read note Result的结果是数组
if isSlice {
// source
// read note 如果Origin的类型是数组,需要根据index进行获取结构体
if from.Kind() == reflect.Slice {
source = indirect(from.Index(i))
} else {
// read note 如果Origin的类型是结构体,直接获取该结构体
source = indirect(from)
}
// dest
dest = indirect(reflect.New(toType).Elem())
} else {
//read note Result的结果不是数组,直接获取结构体
source = indirect(from)
dest = indirect(to)
}
// Get tag options
//read note 获取tag的所有标签
tagBitFlags := map[string]uint8{}
if dest.IsValid() {
//read note 根据结构体type获取所有的Field对应的tag标签
tagBitFlags = getBitFlags(toType)
}
// check source
//read note 如果是非零值
if source.IsValid() {
//read note 获取结构体的所有Field的信息(数组)
fromTypeFields := deepFields(fromType)
// fmt.Printf("%#v", fromTypeFields)
// Copy from field to field or method
//todo 循环所有的Field处理
for _, field := range fromTypeFields {
name := field.Name
// Get bit flags for field
//read note 根据name获取tag数据
fieldFlags, _ := tagBitFlags[name]
// Check if we should ignore copying
//read note ignore标签对结构体的影响处理
if (fieldFlags & tagIgnore) != 0 {
continue
}
//read note Origin的方法和Result字段同名的处理
if fromField := source.FieldByName(name); fromField.IsValid() && !shouldIgnore(fromField, ignoreEmpty) {
// has field
//read note 根据名称获取Result结构体的字段.
if toField := dest.FieldByName(name); toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField) {
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
return err
}
} else {
//read note 赋值完成,设置对应的标识
if fieldFlags != 0 {
// Note that a copy was made
//read note 设置复制标识
tagBitFlags[name] = fieldFlags | hasCopied
}
}
}
} else {
// try to set to method
var toMethod reflect.Value
//read note 通过名称找到对应的Method
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(name)
} else {
toMethod = dest.MethodByName(name)
}
//read note 【被转换对象的方法调用】的校验还比较严格,这边可以看出来只能有一个字段,并且字段类型要对应上
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
//read note 调用声明的方法
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
//read note Result方法 与Origin字段同名的处理
// Copy from method to field
//read note 处理目标结构体的方法,目标结构体的方法要和被复制结构体的字段名一致,就是这边控制的
for _, field := range deepFields(toType) {
name := field.Name
//read note 根据Result的字段,获取Origin同名的方法
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(name)
} else {
fromMethod = source.MethodByName(name)
}
//read note 如果方法符合规则,没有入参,有一个出参,则进行对应方法的调用处理
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, ignoreEmpty) {
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
//read note 进行字段的设值
set(toField, values[0])
}
}
}
}
}
//read note 转换结果Result是切片的处理:分成两种情况,被复制的是 结构体指针 和 结构体
if isSlice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest.Addr()))
} else if dest.Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest))
}
}
//read note 这边是不是会有一个问题,就是err是不是会被覆盖,前面的字段有错误,最后一个没有错误则会覆盖之前的error
err = checkBitFlags(tagBitFlags)
}
return
}
func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
if !ignoreEmpty {
return false
}
return v.IsZero()
}
//read note 根据结构体类型,获取结构体对应的Field切片,注意这边的【Anonymous】表示的匿名变量,匿名变量的Field这边需要特殊处理
func deepFields(reflectType reflect.Type) []reflect.StructField {
var fields []reflect.StructField
//read note 判断是不是结构体,只能对结构体进行处理
if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
//read note 循环处理对应的所有Field
for i := 0; i < reflectType.NumField(); i++ {
v := reflectType.Field(i)
//read note 对【嵌入(匿名)字段】结构体 的所有结构体进行添加
if v.Anonymous {
fields = append(fields, deepFields(v.Type)...)
} else {
fields = append(fields, v)
}
}
}
return fields
}
func indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
return reflectValue
}
func indirectType(reflectType reflect.Type) reflect.Type {
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
reflectType = reflectType.Elem()
}
return reflectType
}
func set(to, from reflect.Value) bool {
//read note IsValid返回是否非零值的结果.所以这边的处理是针对非零值
if from.IsValid() {
//read note 前置条件:处理to的类型,如果from为空,则直接设置空值返回
// to是指针类型特殊处理
if to.Kind() == reflect.Ptr {
// set `to` to nil if from is nil
//read note 如果from是空,则直接设置to为零值返回
if from.Kind() == reflect.Ptr && from.IsNil() {
to.Set(reflect.Zero(to.Type()))
return true
} else if to.IsNil() {
//read note 如果to是nil且不满足上面的from为nil的条件,这个时候要给to设置默认值
to.Set(reflect.New(to.Type().Elem()))
}
//read note 指针的转换处理
to = to.Elem()
}
//read note from和to类型的转换处理,这边当from是ptr类型的时候,会调用set进行递归处理
//read note 如果类型可以进行转换,则要设置对应的值(具体什么类型可以转换需要看一下源码,这里不多赘述)
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
//read note sql.Scanner 这个不知道具体是干嘛的.
err := scanner.Scan(from.Interface())
if err != nil {
return false
}
} else if from.Kind() == reflect.Ptr {
//read note from是指针类型,处理成结构体进行赋值(相当于递归再往下走)
return set(to, from.Elem())
} else {
//read note 其他不能转换的直接返回false
return false
}
}
//read note 零值直接返回true,零值不处理
return true
}
// parseTags Parses struct tags and returns uint8 bit flags.
func parseTags(tag string) (flags uint8) {
for _, t := range strings.Split(tag, ",") {
switch t {
case "-":
flags = tagIgnore
return
case "must":
flags = flags | tagMust
case "nopanic":
flags = flags | tagNoPanic
}
}
return
}
// getBitFlags Parses struct tags for bit flags.
func getBitFlags(toType reflect.Type) map[string]uint8 {
//read note 存储的结构是 FieldName->tag对应的二进制数据(tag标签转换成程序标识)
flags := map[string]uint8{}
//read note 根据结构体的类型获取对应的Field切片
toTypeFields := deepFields(toType)
// Get a list dest of tags
//read note 循环Field切片,获取切片对应的tag数据
for _, field := range toTypeFields {
tags := field.Tag.Get("copier") //tag标签是【copier】
if tags != "" {
//read note tag标签转换成程序处理标识(这边也是使用二进制的处理方式)
flags[field.Name] = parseTags(tags)
}
}
return flags
}
// checkBitFlags Checks flags for error or panic conditions.
func checkBitFlags(flagsList map[string]uint8) (err error) {
// Check flag conditions were met
//read note 循环mapFieldName->tag对应的二进制数据)
for name, flags := range flagsList {
//read note 如果字段没有被复制
if flags&hasCopied == 0 {
switch {
case flags&tagMust != 0 && flags&tagNoPanic != 0:
//read note 处理1:返回错误信息
err = fmt.Errorf("Field %s has must tag but was not copied", name)
return
case flags&(tagMust) != 0:
//read note 处理2:直接报错
panic(fmt.Sprintf("Field %s has must tag but was not copied", name))
}
}
}
return
}