Skip to content

Commit

Permalink
improve reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
quintans committed Jun 13, 2022
1 parent f003a1a commit 7ae9502
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,7 @@ translator.RegisterConverter("color", ColorConverter{})

## Embeded Structs

If we want to group columns into a complex type (`struct`) we just have to tag the firld as `sql:"embeded"`.
If we want to group columns into a complex type (`struct`) we just have to tag the field as `sql:"embeded"`.
This is handy when working with Value Objects.

Consider the table
Expand Down
69 changes: 31 additions & 38 deletions db/entity_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
)

type EntityProperty struct {
FieldName string
Position int
Type reflect.Type
InnerType reflect.Type
Expand All @@ -30,25 +29,25 @@ func (e *EntityProperty) IsMany() bool {

type setter func(instance reflect.Value) reflect.Value

func makeSetter(previous setter, fieldname string) setter {
func makeSetter(index []int) setter {
return func(instance reflect.Value) reflect.Value {
if previous != nil {
instance = previous(instance)
}
for _, x := range index {
if instance.Kind() == reflect.Ptr {
if instance.IsNil() {
t := instance.Type().Elem()
val := reflect.New(t)
instance.Set(val)
}
instance = instance.Elem()
}

if instance.Kind() == reflect.Ptr {
if instance.IsNil() {
t := instance.Type().Elem()
val := reflect.New(t)
instance.Set(val)
instance = reflect.Indirect(instance).Field(x)
if !instance.CanSet() {
// Cheat: writting to unexported fields
instance = reflect.NewAt(instance.Type(), unsafe.Pointer(instance.UnsafeAddr())).Elem()
}
instance = instance.Elem()
}
instance = reflect.Indirect(instance).FieldByName(fieldname)
if !instance.CanSet() {
// Cheat: writting to unexported fields
instance = reflect.NewAt(instance.Type(), unsafe.Pointer(instance.UnsafeAddr())).Elem()
}

return instance
}
}
Expand All @@ -72,15 +71,12 @@ func (e *EntityProperty) Set(instance reflect.Value, value reflect.Value) bool {

type getter func(instance reflect.Value) reflect.Value

func makeGetter(previous getter, fieldname string) getter {
func makeGetter(index []int) getter {
return func(instance reflect.Value) reflect.Value {
if previous != nil {
instance = previous(instance)
}
if instance.Kind() == reflect.Ptr {
instance = instance.Elem()
}
instance = reflect.Indirect(instance).FieldByName(fieldname)
instance = reflect.Indirect(instance).FieldByIndex(index)
if !instance.CanSet() && instance.CanAddr() {
// Cheat: writting to unexported fields
instance = reflect.NewAt(instance.Type(), unsafe.Pointer(instance.UnsafeAddr())).Elem()
Expand All @@ -93,20 +89,16 @@ func (e *EntityProperty) Get(instance reflect.Value) reflect.Value {
return e.getter(instance)
}

func PopulateMappingOf(prefix string, m interface{}, translator Translator) (map[string]*EntityProperty, error) {
return PopulateMapping(prefix, reflect.TypeOf(m), translator)
}

func PopulateMapping(prefix string, typ reflect.Type, translator Translator) (map[string]*EntityProperty, error) {
// create an attribute data structure as a map of types keyed by a string.
attrs := make(map[string]*EntityProperty)

err := walkTreeStruct(prefix, typ, attrs, translator, nil, nil)
err := walkTreeStruct(prefix, typ, attrs, translator, nil)

return attrs, err
}

func walkTreeStruct(prefix string, typ reflect.Type, attrs map[string]*EntityProperty, translator Translator, prevGetter getter, prevSetter setter) error {
func walkTreeStruct(prefix string, typ reflect.Type, attrs map[string]*EntityProperty, translator Translator, index []int) error {
// if a pointer to a struct is passed, get the type of the dereferenced object
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
Expand All @@ -119,7 +111,8 @@ func walkTreeStruct(prefix string, typ reflect.Type, attrs map[string]*EntityPro
}

// loop through the struct's fields and set the map
for i := 0; i < typ.NumField(); i++ {
num := typ.NumField()
for i := 0; i < num; i++ {
p := typ.Field(i)
var omit, embeded bool
var converter Converter
Expand All @@ -144,27 +137,27 @@ func walkTreeStruct(prefix string, typ reflect.Type, attrs map[string]*EntityPro
}
}
}
x := append(index, i)
if p.Anonymous {
if err := walkTreeStruct(prefix, p.Type, attrs, translator, prevGetter, prevSetter); err != nil {
if err := walkTreeStruct(prefix, p.Type, attrs, translator, x); err != nil {
return err
}
} else if embeded {
nextGetter := makeGetter(prevGetter, p.Name)
nextSetter := makeSetter(prevSetter, p.Name)
if err := walkTreeStruct(prefix, p.Type, attrs, translator, nextGetter, nextSetter); err != nil {
if err := walkTreeStruct(prefix, p.Type, attrs, translator, x); err != nil {
return err
}
} else {
ep := &EntityProperty{}
ep.getter = makeGetter(prevGetter, p.Name)
ep.setter = makeSetter(prevSetter, p.Name)
ep.getter = makeGetter(x)
ep.setter = makeSetter(x)

key := strings.ToUpper(p.Name[:1]) + p.Name[1:]
var key strings.Builder
if prefix != "" {
key = prefix + key
key.WriteString(prefix)
}
attrs[key] = ep
ep.FieldName = p.Name
key.WriteString(strings.ToUpper(p.Name[:1]))
key.WriteString(p.Name[1:])
attrs[key.String()] = ep
ep.Omit = omit
ep.converter = converter
// we want pointers. only pointer are addressable
Expand Down
3 changes: 0 additions & 3 deletions db/insert.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package db

import (
"fmt"

"github.com/quintans/faults"
coll "github.com/quintans/toolkit/collections"

Expand Down Expand Up @@ -161,7 +159,6 @@ func (i *Insert) Submit(instance interface{}) (int64, error) {
var version int64 = 1
for e := i.table.GetColumns().Enumerator(); e.HasNext(); {
column := e.Next().(*Column)
fmt.Println("===>", column.name)
if column.IsVersion() {
i.Set(column, version)
} else {
Expand Down

0 comments on commit 7ae9502

Please sign in to comment.