commit c3e8cd5718ad225234442d6eb64976eedcecd545 Author: Richard Park Date: Wed Nov 13 22:34:36 2019 +0900 ing diff --git a/annotations/component.go b/annotations/component.go new file mode 100644 index 0000000..6c508dc --- /dev/null +++ b/annotations/component.go @@ -0,0 +1,22 @@ +package annotations + +// @Inject(name? string) + +import ( + "reflect" + + "git.loafle.net/loafer/annotation-go" +) + +var ComponentAnnotationType = reflect.TypeOf((*ComponentAnnotation)(nil)) + +func init() { + annotation.Register(ComponentAnnotationType) +} + +type ComponentAnnotation struct { + annotation.TypeAnnotation `@annotation:"@Component"` + Name string `json:"name"` + InitMethod string `json:"initMethod"` // func (receiver interface{}, cr ComponentRegistry) error + DestroyMethod string `json:"destroyMethod"` // func (receiver interface{}, cr ComponentRegistry) error +} diff --git a/annotations/inject.go b/annotations/inject.go new file mode 100644 index 0000000..946e8dd --- /dev/null +++ b/annotations/inject.go @@ -0,0 +1,21 @@ +package annotations + +// @Inject(name? string) + +import ( + "reflect" + + "git.loafle.net/loafer/annotation-go" +) + +var InjectAnnotationType = reflect.TypeOf((*InjectAnnotation)(nil)) + +func init() { + annotation.Register(InjectAnnotationType) +} + +type InjectAnnotation struct { + annotation.TypeAnnotation `@annotation:"@Inject"` + + Name string `json:"name"` +} diff --git a/annotations/resource.go b/annotations/resource.go new file mode 100644 index 0000000..10f27da --- /dev/null +++ b/annotations/resource.go @@ -0,0 +1,18 @@ +package annotations + +import ( + "reflect" + + "git.loafle.net/loafer/annotation-go" +) + +var ResourceAnnotationType = reflect.TypeOf((*ResourceAnnotation)(nil)) + +func init() { + annotation.Register(ResourceAnnotationType) +} + +type ResourceAnnotation struct { + annotation.TypeAnnotation `@annotation:"@Resource"` + Name string `json:"name"` +} diff --git a/definitions.go b/definitions.go new file mode 100644 index 0000000..fd4d78c --- /dev/null +++ b/definitions.go @@ -0,0 +1,135 @@ +package di + +import ( + "fmt" + "reflect" + + "git.loafle.net/loafer/annotation-go" + luReflect "git.loafle.net/loafer/util-go/reflect" +) + +type TypeDefinition struct { + FullName string + PkgName string + TypeName string + Type reflect.Type + RealType reflect.Type + + TypeAnnotations map[reflect.Type]annotation.Annotation + MethodAnnotations map[string]map[reflect.Type]annotation.Annotation + Fields []*FieldDefinition +} + +func (td *TypeDefinition) GetTypeAnnotationByType(at reflect.Type, includeEmbedding bool) annotation.Annotation { + if nil == td.TypeAnnotations { + return nil + } + + if !includeEmbedding { + return td.TypeAnnotations[at] + } + + for _, v := range td.TypeAnnotations { + if at == reflect.TypeOf(v) { + return v + } + + if includeEmbedding { + if checkAnnotation(reflect.TypeOf(v), at) { + return v + } + } + } + + return nil +} + +func (td *TypeDefinition) GetMethodAnnotationByType(at reflect.Type, methodName string) annotation.Annotation { + if nil == td.MethodAnnotations { + return nil + } + + ms, ok := td.MethodAnnotations[methodName] + if !ok { + return nil + } + + return ms[at] +} + +func (td *TypeDefinition) GetMethodAnnotationsByType(at reflect.Type) map[string]annotation.Annotation { + if nil == td.MethodAnnotations { + return nil + } + + mas := make(map[string]annotation.Annotation) + for k, v := range td.MethodAnnotations { + a, ok := v[at] + if !ok { + continue + } + mas[k] = a + } + + return mas +} + +func checkAnnotation(t reflect.Type, st reflect.Type) bool { + rt, _, _ := luReflect.GetTypeInfo(t) + if reflect.Struct != rt.Kind() { + return false + } + + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + + if f.Anonymous { + if f.Type == st { + return true + } + if checkAnnotation(f.Type, st) { + return true + } + } + } + + return false +} + +type FieldDefinition struct { + FieldName string + PkgName string + TypeName string + Type reflect.Type + RealType reflect.Type + + Annotations map[reflect.Type]annotation.Annotation +} + +func (fd *FieldDefinition) GetAnnotationByType(at reflect.Type, includeEmbedding bool) annotation.Annotation { + if nil == fd.Annotations { + return nil + } + + if !includeEmbedding { + return fd.Annotations[at] + } + + for _, v := range fd.Annotations { + if at == reflect.TypeOf(v) { + return v + } + + if includeEmbedding { + if checkAnnotation(reflect.TypeOf(v), at) { + return v + } + } + } + + return nil +} + +func FullName(pkgName, typeName string) string { + return fmt.Sprintf("%s/%s", pkgName, typeName) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..51bf90d --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.loafle.net/loafer/di-go + +go 1.13 + +require ( + git.loafle.net/loafer/annotation-go v0.0.0-20191112145817-e44b732fea76 + git.loafle.net/loafer/util-go v0.0.0-20191113132317-6eeae49d258d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..434af39 --- /dev/null +++ b/go.sum @@ -0,0 +1,5 @@ +git.loafle.net/loafer/annotation-go v0.0.0-20191112145817-e44b732fea76 h1:fUggcxR3GQLUeDVt2vuLJ7NW20sdr+Cnk/Q0uOioBLI= +git.loafle.net/loafer/annotation-go v0.0.0-20191112145817-e44b732fea76/go.mod h1:1yow6wwbB3nWq6Asgt3BAPfXJTjZeqgMYF+VVPlj9Xk= +git.loafle.net/loafer/util-go v0.0.0-20191112142134-9a567d18b779/go.mod h1:HGVw9FNJIc/UFDIzxmoIj5K2+D9Eadal5jjHOq0NFOU= +git.loafle.net/loafer/util-go v0.0.0-20191113132317-6eeae49d258d h1:ESDbDHHzH2Ysq+thQrO/OQtyDkVhzNzshjn0SJIqa0g= +git.loafle.net/loafer/util-go v0.0.0-20191113132317-6eeae49d258d/go.mod h1:HGVw9FNJIc/UFDIzxmoIj5K2+D9Eadal5jjHOq0NFOU= diff --git a/registry.go b/registry.go new file mode 100644 index 0000000..1a85e1a --- /dev/null +++ b/registry.go @@ -0,0 +1,426 @@ +package di + +import ( + "fmt" + "log" + "reflect" + "runtime" + "strings" + + "git.loafle.net/loafer/annotation-go" + "git.loafle.net/loafer/di-go/annotations" + luReflect "git.loafle.net/loafer/util-go/reflect" +) + +func init() { + registry = newRegistry() +} + +// Regist is interface +type Regist interface { + RegisterType(t reflect.Type) + RegisterResource(name string, resource interface{}) error + + GetInstance(t reflect.Type) (interface{}, error) + GetInstances(ts []reflect.Type) ([]interface{}, error) + GetInstanceByName(name string) (interface{}, error) + GetInstancesByAnnotationType(t reflect.Type) ([]interface{}, error) + + GetTypeAnnotation(instanceType reflect.Type, annotationType reflect.Type) annotation.Annotation + GetMethodAnnotation(instanceType reflect.Type, annotationType reflect.Type, methodName string) annotation.Annotation + GetMethodAnnotations(instanceType reflect.Type, annotationType reflect.Type) map[string]annotation.Annotation +} + +// Registry is struct +type Registry struct { + definitionByType map[reflect.Type]*TypeDefinition + definitionByName map[string]*TypeDefinition + instanceByType map[reflect.Type]interface{} + resourceByName map[string]interface{} +} + +var registry Regist + +func newRegistry() Regist { + r := &Registry{} + r.definitionByType = make(map[reflect.Type]*TypeDefinition, 0) + r.definitionByName = make(map[string]*TypeDefinition, 0) + r.instanceByType = make(map[reflect.Type]interface{}, 0) + r.resourceByName = make(map[string]interface{}, 0) + + return r +} + +// RegisterType is function +func RegisterType(t reflect.Type) { + pc, _, _, ok := runtime.Caller(1) + details := runtime.FuncForPC(pc) + if ok && details != nil { + log.Printf("called from %s", details.Name()) + } + + registry.RegisterType(t) +} + +func (r *Registry) RegisterType(t reflect.Type) { + if nil == t { + log.Panicf("DI: t[reflect.Type] is nil") + } + if !luReflect.IsTypeKind(t, reflect.Struct, true) { + log.Panicf("DI: t[reflect.Type] must be specified but is %v", t) + } + + td, err := r.buildDefinition(t) + if nil != err { + log.Panicf("DI: %v", err) + } + + if _, ok := r.definitionByType[td.Type]; ok { + log.Panicf("DI: The type[%s] of Component is exist already", td.FullName) + } + r.definitionByType[td.Type] = td + + name := td.TypeName + + if a := td.GetTypeAnnotationByType(annotations.ComponentAnnotationType, true); nil != a { + ca := a.(*annotations.ComponentAnnotation) + if "" != strings.Trim(ca.Name, " ") { + name = ca.Name + } + } + + if eTD, ok := r.definitionByName[name]; ok { + log.Panicf("DI: The name[%s] of Component is exist already type[%s]", name, eTD.FullName) + } + r.definitionByName[name] = td +} + +func RegisterResource(name string, resource interface{}) error { + return registry.RegisterResource(name, resource) +} +func (r *Registry) RegisterResource(name string, resource interface{}) error { + if _, ok := r.resourceByName[name]; ok { + return fmt.Errorf("DI: Resource[%s] is already exist", name) + } + r.resourceByName[name] = resource + + return nil +} + +// GetInstance returns instance of type +// t must be pointer of struct +func GetInstance(t reflect.Type) (interface{}, error) { + return registry.GetInstance(t) +} +func (r *Registry) GetInstance(t reflect.Type) (instance interface{}, err error) { + if nil == t { + return nil, fmt.Errorf("DI: t[reflect.Type] is nil") + } + + if reflect.Ptr != t.Kind() { + return nil, fmt.Errorf("DI: t[reflect.Type] must be pointer of struct") + } + + if reflect.Struct != t.Elem().Kind() { + return nil, fmt.Errorf("DI: t[reflect.Type] must be pointer of struct") + } + + var ( + td *TypeDefinition + comV interface{} + ok bool + ) + + rt, _, _ := luReflect.GetTypeInfo(t) + if td, ok = r.definitionByType[t]; !ok { + if td, err = r.buildDefinition(t); nil != err { + return nil, fmt.Errorf("DI: %v", err) + } + } + + if comV, ok = r.instanceByType[td.RealType]; ok { + return comV, nil + } + + v := reflect.New(rt) + rv := v.Elem() + + instance = v.Interface() + r.instanceByType[td.RealType] = instance + err = nil + defer func() { + if nil != err { + instance = nil + delete(r.instanceByType, td.RealType) + } + }() + + var ( + annotation annotation.Annotation + fV interface{} + ) + + for _, fd := range td.Fields { + f := rv.FieldByName(fd.FieldName) + + if !f.IsValid() { + err = fmt.Errorf("DI: Field[%s] is not valid", fd.FieldName) + return + } + if !f.CanSet() { + err = fmt.Errorf("DI: Field[%s] can not set", fd.FieldName) + return + } + + if annotation = fd.GetAnnotationByType(annotations.InjectAnnotationType, false); nil != annotation { + if fV, err = r.GetInstance(fd.Type); nil != err { + return + } + } + + if annotation = fd.GetAnnotationByType(annotations.ResourceAnnotationType, false); nil != annotation { + n := annotation.(*annotations.ResourceAnnotation).Name + if "" == n { + n = fd.FieldName + } + if fV, err = r.GetInstanceByName(n); nil != err { + return + } + } + + if nil != err { + return + } + f.Set(reflect.ValueOf(fV)) + } + + return +} + +func GetInstanceByName(name string) (interface{}, error) { + return registry.GetInstanceByName(name) +} +func (r *Registry) GetInstanceByName(name string) (interface{}, error) { + v, ok := r.resourceByName[name] + if ok { + return v, nil + } + + td, ok := r.definitionByName[name] + if !ok { + return nil, fmt.Errorf("DI: Instance[%s] is not exist", name) + } + + v, err := r.GetInstance(td.Type) + if nil != err { + return nil, fmt.Errorf("DI: Instance[%s] is not exist -> %s", name, err.Error()) + } + + return v, nil +} + +// GetInstances returns instance of annotated +// n must be name of registered annotation +func GetInstances(ts []reflect.Type) ([]interface{}, error) { + return registry.GetInstances(ts) +} +func (r *Registry) GetInstances(ts []reflect.Type) ([]interface{}, error) { + var ( + i interface{} + err error + ) + instances := make([]interface{}, 0) + for _, t := range ts { + if i, err = r.GetInstance(t); nil != err { + return nil, err + } + instances = append(instances, i) + } + + return instances, nil +} + +func GetInstancesByAnnotationType(t reflect.Type) ([]interface{}, error) { + return registry.GetInstancesByAnnotationType(t) +} +func (r *Registry) GetInstancesByAnnotationType(t reflect.Type) ([]interface{}, error) { + var ( + i interface{} + err error + ) + instances := make([]interface{}, 0) + + for _, td := range r.definitionByType { + if nil != td.GetTypeAnnotationByType(t, true) { + if i, err = r.GetInstance(td.Type); nil != err { + return nil, err + } + instances = append(instances, i) + } + } + + return instances, nil +} + +func GetTypeAnnotation(instanceType reflect.Type, annotationType reflect.Type) annotation.Annotation { + return registry.GetTypeAnnotation(instanceType, annotationType) +} +func (r *Registry) GetTypeAnnotation(instanceType reflect.Type, annotationType reflect.Type) annotation.Annotation { + def, ok := r.definitionByType[instanceType] + if !ok { + return nil + } + + return def.GetTypeAnnotationByType(annotationType, false) +} +func GetMethodAnnotation(instanceType reflect.Type, annotationType reflect.Type, methodName string) annotation.Annotation { + return registry.GetMethodAnnotation(instanceType, annotationType, methodName) +} +func (r *Registry) GetMethodAnnotation(instanceType reflect.Type, annotationType reflect.Type, methodName string) annotation.Annotation { + def, ok := r.definitionByType[instanceType] + if !ok { + return nil + } + + return def.GetMethodAnnotationByType(annotationType, methodName) +} + +func GetMethodAnnotations(instanceType reflect.Type, annotationType reflect.Type) map[string]annotation.Annotation { + return registry.GetMethodAnnotations(instanceType, annotationType) +} +func (r *Registry) GetMethodAnnotations(instanceType reflect.Type, annotationType reflect.Type) map[string]annotation.Annotation { + def, ok := r.definitionByType[instanceType] + if !ok { + return nil + } + + return def.GetMethodAnnotationsByType(annotationType) +} + +func (r *Registry) buildDefinition(t reflect.Type) (*TypeDefinition, error) { + if nil == t { + return nil, fmt.Errorf("t[reflect.Type] is nil") + } + + rt, pkgName, tName := luReflect.GetTypeInfo(t) + td := &TypeDefinition{} + td.FullName = FullName(pkgName, tName) + td.PkgName = pkgName + td.TypeName = tName + td.Type = t + td.RealType = rt + td.Fields = make([]*FieldDefinition, 0) + + parseFields(rt, td) + + return td, nil +} + +func parseFields(t reflect.Type, td *TypeDefinition) { + // fields := make([]*FieldDefinition, 0) + rt, _, _ := luReflect.GetTypeInfo(t) + if reflect.Struct != rt.Kind() { + return + } + + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + + if f.Anonymous { + if parseAnonymousField(&f, td) { + continue + } + parseFields(f.Type, td) + } else { + if parseMethodAnnotation(&f, td) { + continue + } + as, err := annotation.Parse(f.Tag) + if nil != err { + return + } + if nil != as && 0 < len(as) { + fRT, fPkgName, fTName := luReflect.GetTypeInfo(f.Type) + + fd := &FieldDefinition{ + FieldName: f.Name, + PkgName: fPkgName, + TypeName: fTName, + Type: f.Type, + RealType: fRT, + Annotations: as, + } + td.Fields = append(td.Fields, fd) + } + } + } +} + +func parseAnonymousField(f *reflect.StructField, td *TypeDefinition) bool { + if parseTypeAnnotation(f, td) { + return true + } + return false +} + +func parseTypeAnnotation(f *reflect.StructField, td *TypeDefinition) bool { + // if !haveEmbeddingOf(cda.TypeAnnotationType, f.Type) { + // return + // } + if annotation.TypeAnnotationType != f.Type { + return false + } + + as, err := annotation.Parse(f.Tag) + if nil != err { + return false + } + + if nil != as && 0 < len(as) { + td.TypeAnnotations = as + return true + } + return false +} + +func parseMethodAnnotation(f *reflect.StructField, td *TypeDefinition) bool { + if annotation.MethodAnnotationType != f.Type { + return false + } + + as, err := annotation.Parse(f.Tag) + if nil != err { + return false + } + + if nil != as && 0 < len(as) { + if nil == td.MethodAnnotations { + td.MethodAnnotations = make(map[string]map[reflect.Type]annotation.Annotation, 0) + } + td.MethodAnnotations[f.Name[1:]] = as + return true + } + return false +} + +func haveEmbeddingOf(t reflect.Type, target reflect.Type) bool { + if t == target { + return true + } + + rt, _, _ := luReflect.GetTypeInfo(target) + if reflect.Struct != rt.Kind() { + return false + } + + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + + if f.Anonymous { + if haveEmbeddingOf(t, f.Type) { + return true + } + } + } + return false +}