From c40b3be3cc5c8a6b1790749d4ca371af0eb02e39 Mon Sep 17 00:00:00 2001 From: geek Date: Tue, 3 Apr 2018 18:02:31 +0900 Subject: [PATCH] project init --- .gitignore | 69 ++++++ .vscode/launch.json | 32 +++ .vscode/settings.json | 3 + annotation/annotation.go | 148 ++++++++++++ annotation/definition.go | 40 ++++ annotation/meta.go | 87 +++++++ annotation/type.go | 5 + constants.go | 25 ++ di.go | 1 + glide.yaml | 2 + injection/annotation/component.go | 26 +++ injection/annotation/inject.go | 22 ++ injection/annotation/inject_test.go | 29 +++ injection/annotation/resource.go | 20 ++ injection/annotation/resource_test.go | 29 +++ registry/definition.go | 111 +++++++++ registry/registry.go | 313 ++++++++++++++++++++++++++ registry/registry_test.go | 68 ++++++ 18 files changed, 1030 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 annotation/annotation.go create mode 100644 annotation/definition.go create mode 100644 annotation/meta.go create mode 100644 annotation/type.go create mode 100644 constants.go create mode 100644 di.go create mode 100644 glide.yaml create mode 100644 injection/annotation/component.go create mode 100644 injection/annotation/inject.go create mode 100644 injection/annotation/inject_test.go create mode 100644 injection/annotation/resource.go create mode 100644 injection/annotation/resource_test.go create mode 100644 registry/definition.go create mode 100644 registry/registry.go create mode 100644 registry/registry_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac65c48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,69 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Go template +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ +*.iml + +vendor/ +glide.lock +.DS_Store +dist/ +debug +debug.test diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2ca2b1d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${workspaceRoot}/main.go", + "env": {}, + "args": [], + "showLog": true + }, + { + "name": "File Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${fileDirname}", + "env": {}, + "args": [], + "showLog": true + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..20af2f6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +// Place your settings in this file to overwrite default and user settings. +{ +} \ No newline at end of file diff --git a/annotation/annotation.go b/annotation/annotation.go new file mode 100644 index 0000000..7d6622b --- /dev/null +++ b/annotation/annotation.go @@ -0,0 +1,148 @@ +package annotation + +import ( + "fmt" + "reflect" + "strings" +) + +const ( + AnnotationTag = "annotation" + AnnotationMetaTag = "annotation-meta" + AnnotationChar = "@" + AnnotationStartChar = "(" + AnnotationEndChar = ")" + AnnotationSpliter = ";" + AnnotationAttributeSpliter = "," + AnnotationKeyValueSpliter = "=" +) + +type Annotation interface { +} + +// @Inject(name? string) +// @Resource(name? string) +func ParseAnnotation(tag reflect.StructTag) (map[string]Annotation, error) { + s := strings.Trim(tag.Get(AnnotationTag), " ") + if "" == s { + return nil, nil + } + + annotations := strings.Split(s, AnnotationSpliter) + if nil == annotations || 0 == len(annotations) { + return nil, nil + } + + rKVs := make(map[string]Annotation, 0) + for _, a := range annotations { + a = strings.Trim(a, " ") + if "" == a { + continue + } + aName, attributes, err := parseAnnotationItem(a) + if nil != err { + return nil, err + } + if "" == aName { + continue + } + annotation, err := newAnnotation(aName, attributes) + if nil != err { + return nil, err + } + rKVs[aName] = annotation + } + + return rKVs, nil +} + +func newAnnotation(name string, attributes map[string]string) (Annotation, error) { + def, ok := annotationRegistry[name] + if !ok { + return nil, fmt.Errorf("There is no annotation[%s]", name) + } + + v := reflect.New(def.rt) + i := v.Interface().(Annotation) + + if nil != attributes { + setMetaAttributes(def, v.Elem(), attributes) + } + return i, nil +} + +func parseAnnotationItem(a string) (name string, attributes map[string]string, err error) { + s := strings.Trim(a, " ") + if "" == s { + return + } + + i := strings.Index(s, AnnotationChar) + if -1 == i { + err = fmt.Errorf("Syntax error: annotation must be started %s", AnnotationChar) + return + } + + aStart := strings.Index(s, AnnotationStartChar) + if -1 == aStart { + // This is pure annotation ex)@Resource + name = s + return + } + + name = s[:aStart] + + aEnd := strings.Index(s, AnnotationEndChar) + if -1 == aEnd { + // This is syntax error ex)@Resource( + err = fmt.Errorf("Syntax error: annotation must be ended %s", AnnotationEndChar) + return + } + + if 1 >= aEnd-aStart { + return + } + + attr := s[aStart+1 : aEnd] + attr = strings.Trim(attr, " ") + + if "" == attr { + return + } + + attrs, pErr := parseAttributeString(s[aStart+1 : aEnd]) + if nil != pErr { + err = pErr + return + } + + attributes = attrs + + return +} + +func parseAttributeString(s string) (map[string]string, error) { + attr := strings.Trim(s, " ") + if "" == attr { + return nil, nil + } + + kvs := strings.Split(attr, AnnotationAttributeSpliter) + if nil == kvs || 0 == len(kvs) { + return nil, nil + } + + rKVs := make(map[string]string) + for i := 0; i < len(kvs); i++ { + s := strings.Trim(kvs[i], " ") + if "" == s { + continue + } + kv := strings.Split(s, AnnotationKeyValueSpliter) + k := strings.Trim(kv[0], " ") + v := strings.Trim(kv[1], " ") + rKVs[k] = v + } + + return rKVs, nil +} diff --git a/annotation/definition.go b/annotation/definition.go new file mode 100644 index 0000000..524fab5 --- /dev/null +++ b/annotation/definition.go @@ -0,0 +1,40 @@ +package annotation + +import ( + "fmt" + "reflect" + + cur "git.loafle.net/commons/util-go/reflect" +) + +var annotationRegistry map[string]*AnnotationDefinition + +func init() { + annotationRegistry = make(map[string]*AnnotationDefinition, 0) +} + +func RegisterAnnotation(name string, t reflect.Type) error { + if _, ok := annotationRegistry[name]; ok { + return fmt.Errorf("DI: name[%s] of annotation exist already", name) + } + + meta := getMetaFields(t) + fRT, _, _ := cur.GetTypeInfo(t) + + def := &AnnotationDefinition{ + t: t, + rt: fRT, + fields: meta, + } + + annotationRegistry[name] = def + + return nil +} + +type AnnotationDefinition struct { + t reflect.Type + rt reflect.Type + + fields map[string]*AnnotationFieldMeta +} diff --git a/annotation/meta.go b/annotation/meta.go new file mode 100644 index 0000000..c7f3e80 --- /dev/null +++ b/annotation/meta.go @@ -0,0 +1,87 @@ +package annotation + +import ( + "reflect" + "strings" + + cur "git.loafle.net/commons/util-go/reflect" +) + +type AnnotationFieldMeta struct { + fieldName string + options anotationMetaOptions +} + +type anotationMetaOptions string + +func (o anotationMetaOptions) Contains(optionName string) bool { + if len(o) == 0 { + return false + } + s := string(o) + for s != "" { + var next string + i := strings.Index(s, AnnotationAttributeSpliter) + if i >= 0 { + s, next = s[:i], s[i+1:] + } + if s == optionName { + return true + } + s = next + } + return false +} + +func getMetaFields(t reflect.Type) map[string]*AnnotationFieldMeta { + fields := make(map[string]*AnnotationFieldMeta, 0) + rt, _, _ := cur.GetTypeInfo(t) + if reflect.Struct != rt.Kind() { + return fields + } + + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + + if f.Anonymous { + pFields := getMetaFields(f.Type) + for k, v := range pFields { + fields[k] = v + } + } else { + name, metaOptions := parseAnnotationMeta(f.Tag) + if "" == name { + continue + } + fields[name] = &AnnotationFieldMeta{ + fieldName: f.Name, + options: metaOptions, + } + } + } + + return fields +} + +func parseAnnotationMeta(tag reflect.StructTag) (string, anotationMetaOptions) { + s := strings.Trim(tag.Get(AnnotationMetaTag), " ") + if "" == s { + return "", "" + } + + if idx := strings.Index(s, AnnotationAttributeSpliter); idx != -1 { + return s[:idx], anotationMetaOptions(s[idx+1:]) + } + + return s, anotationMetaOptions("") +} + +func setMetaAttributes(def *AnnotationDefinition, rv reflect.Value, attributes map[string]string) { + for k, v := range attributes { + meta := def.fields[k] + + f := rv.FieldByName(meta.fieldName) + + f.Set(reflect.ValueOf(v)) + } +} diff --git a/annotation/type.go b/annotation/type.go new file mode 100644 index 0000000..28b3416 --- /dev/null +++ b/annotation/type.go @@ -0,0 +1,5 @@ +package annotation + +type TypeAnnotation interface { + Annotation +} diff --git a/constants.go b/constants.go new file mode 100644 index 0000000..5d2ce45 --- /dev/null +++ b/constants.go @@ -0,0 +1,25 @@ +package di + +// `annotation:"@Inject(name? string)"` +// field 에 적용 +// 1. 타입으로 매칭 없으면 에러 2. 여러개의 타입이 검색되면 그 중에 name으로 매칭 + +// `annotation:"@Resource(name? string)"` +// field 에 적용 +// 1. 이름으로 매칭 2. 타입으로 매칭 +// 이름이 지정되지 않으면 field 이름으로 먼저 찾고 없으면 타입으로 매칭 + +// @Component +// Component 등록 시에 파라미터로 제공 +// names []string +// initMethod string +// destroyMethod string +// scope enum singleton, transiant + +type ScopeType string + +const ( + ScopeTypeDefault ScopeType = "Default" // == ScopeTypeSingleton + ScopeTypeSingleton ScopeType = "Singleton" + ScopeTypeTransiant ScopeType = "Transiant" +) diff --git a/di.go b/di.go new file mode 100644 index 0000000..4e7baf6 --- /dev/null +++ b/di.go @@ -0,0 +1 @@ +package di diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..ca5592e --- /dev/null +++ b/glide.yaml @@ -0,0 +1,2 @@ +package: git.loafle.net/commons/di-go +import: [] diff --git a/injection/annotation/component.go b/injection/annotation/component.go new file mode 100644 index 0000000..673d4e7 --- /dev/null +++ b/injection/annotation/component.go @@ -0,0 +1,26 @@ +package annotation + +// @Inject(name? string) + +import ( + "reflect" + + "git.loafle.net/commons/di-go" + cda "git.loafle.net/commons/di-go/annotation" +) + +const ( + ComponentTag = "@Component" +) + +func init() { + cda.RegisterAnnotation(ComponentTag, reflect.TypeOf((*Component)(nil))) +} + +type Component struct { + cda.Annotation + Name string `annotation-meta:"name"` + InitMethod string `annotation-meta:"initMethod"` // func (receiver interface{}, cr ComponentRegistry) error + DestroyMethod string `annotation-meta:"destroyMethod"` // func (receiver interface{}, cr ComponentRegistry) error + Scope di.ScopeType `annotation-meta:"scope"` +} diff --git a/injection/annotation/inject.go b/injection/annotation/inject.go new file mode 100644 index 0000000..708dabc --- /dev/null +++ b/injection/annotation/inject.go @@ -0,0 +1,22 @@ +package annotation + +// @Inject(name? string) + +import ( + "reflect" + + cda "git.loafle.net/commons/di-go/annotation" +) + +const ( + InjectTag = "@Inject" +) + +func init() { + cda.RegisterAnnotation(InjectTag, reflect.TypeOf((*Inject)(nil))) +} + +type Inject struct { + cda.Annotation + Name string `annotation-meta:"name"` +} diff --git a/injection/annotation/inject_test.go b/injection/annotation/inject_test.go new file mode 100644 index 0000000..80d6073 --- /dev/null +++ b/injection/annotation/inject_test.go @@ -0,0 +1,29 @@ +package annotation + +import ( + "testing" +) + +func TestParseInject(t *testing.T) { + // a1 := "@Inject" + // a, err := ParseInject(a1) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + + // a2 := "@Inject()" + // a, err = ParseInject(a2) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + + // a3 := "@Inject(name=string)" + // a, err = ParseInject(a3) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + +} diff --git a/injection/annotation/resource.go b/injection/annotation/resource.go new file mode 100644 index 0000000..4edbf81 --- /dev/null +++ b/injection/annotation/resource.go @@ -0,0 +1,20 @@ +package annotation + +import ( + "reflect" + + cda "git.loafle.net/commons/di-go/annotation" +) + +const ( + ResourceTag = "@Resource" +) + +func init() { + cda.RegisterAnnotation(ResourceTag, reflect.TypeOf((*Resource)(nil))) +} + +type Resource struct { + cda.Annotation + Name string `annotation-meta:"name"` +} diff --git a/injection/annotation/resource_test.go b/injection/annotation/resource_test.go new file mode 100644 index 0000000..0e46987 --- /dev/null +++ b/injection/annotation/resource_test.go @@ -0,0 +1,29 @@ +package annotation + +import ( + "testing" +) + +func TestParseResource(t *testing.T) { + // a1 := "@Resource" + // a, err := ParseResource(a1) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + + // a2 := "@Resource( )" + // a, err = ParseResource(a2) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + + // a3 := "@Resource(name=string1232)" + // a, err = ParseResource(a3) + // if nil != err { + // log.Printf("%v", err) + // } + // log.Printf("%v", a) + +} diff --git a/registry/definition.go b/registry/definition.go new file mode 100644 index 0000000..93363a0 --- /dev/null +++ b/registry/definition.go @@ -0,0 +1,111 @@ +package registry + +import ( + "fmt" + "reflect" + + cda "git.loafle.net/commons/di-go/annotation" + cur "git.loafle.net/commons/util-go/reflect" +) + +type TypeDefinition struct { + FullName string + PkgName string + TypeName string + Type reflect.Type + RealType reflect.Type + + TypeAnnotations map[string]cda.Annotation + Fields []*FieldDefinition +} + +func (td *TypeDefinition) GetAnnotation(name string) cda.Annotation { + if nil == td.TypeAnnotations { + return nil + } + + return td.TypeAnnotations[name] +} + +func (td *TypeDefinition) GetAnnotationByType(at reflect.Type, includeEmbedding bool) cda.Annotation { + if nil == td.TypeAnnotations { + return nil + } + + 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 checkAnnotation(t reflect.Type, st reflect.Type) bool { + rt, _, _ := cur.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[string]cda.Annotation +} + +func (fd *FieldDefinition) GetAnnotation(name string) cda.Annotation { + if nil == fd.Annotations { + return nil + } + return fd.Annotations[name] +} + +func (fd *FieldDefinition) GetAnnotationByType(at reflect.Type, includeEmbedding bool) cda.Annotation { + if nil == fd.Annotations { + return nil + } + + 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/registry/registry.go b/registry/registry.go new file mode 100644 index 0000000..f1be966 --- /dev/null +++ b/registry/registry.go @@ -0,0 +1,313 @@ +package registry + +import ( + "fmt" + "log" + "reflect" + "runtime" + "strings" + + cda "git.loafle.net/commons/di-go/annotation" + cdia "git.loafle.net/commons/di-go/injection/annotation" + cur "git.loafle.net/commons/util-go/reflect" +) + +func init() { + registry = newRegistry() +} + +var registry ComponentRegistry + +type ComponentRegistry 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) + GetInstancesByAnnotationName(n string) ([]interface{}, error) +} + +func newRegistry() ComponentRegistry { + r := &defaultComponentRegistry{} + 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 +} + +type defaultComponentRegistry struct { + definitionByType map[reflect.Type]*TypeDefinition + definitionByName map[string]*TypeDefinition + instanceByType map[reflect.Type]interface{} + resourceByName map[string]interface{} +} + +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 (cr *defaultComponentRegistry) RegisterType(t reflect.Type) { + if nil == t { + log.Panicf("DI: t[reflect.Type] is nil") + } + if !cur.IsTypeKind(t, reflect.Struct, true) { + log.Panicf("DI: t[reflect.Type] must be specified but is %v", t) + } + + td, err := cr.buildDefinition(t) + if nil != err { + log.Panicf("DI: %v", err) + } + + if _, ok := cr.definitionByType[td.RealType]; ok { + log.Panicf("DI: The type[%s] of Component is exist already", td.FullName) + } + cr.definitionByType[td.RealType] = td + + name := td.TypeName + + if a := td.GetAnnotationByType(reflect.TypeOf((*cdia.Component)(nil)), true); nil != a { + ca := a.(*cdia.Component) + if "" != strings.Trim(ca.Name, " ") { + name = ca.Name + } + } + + if eTD, ok := cr.definitionByName[name]; ok { + log.Panicf("DI: The name[%s] of Component is exist already type[%s]", name, eTD.FullName) + } + cr.definitionByName[name] = td +} + +func RegisterResource(name string, resource interface{}) error { + return registry.RegisterResource(name, resource) +} +func (cr *defaultComponentRegistry) RegisterResource(name string, resource interface{}) error { + if _, ok := cr.resourceByName[name]; ok { + return fmt.Errorf("DI: Resource[%s] is already exist", name) + } + cr.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 (cr *defaultComponentRegistry) GetInstance(t reflect.Type) (interface{}, 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 + err error + ) + + rt, _, _ := cur.GetTypeInfo(t) + if td, ok = cr.definitionByType[rt]; !ok { + if td, err = cr.buildDefinition(t); nil != err { + return nil, fmt.Errorf("DI: %v", err) + } + } + + if comV, ok = cr.instanceByType[td.RealType]; ok { + return comV, nil + } + + v := reflect.New(rt) + rv := v.Elem() + + var ( + annotation cda.Annotation + fV interface{} + ) + + for _, fd := range td.Fields { + f := rv.FieldByName(fd.FieldName) + + if !f.IsValid() { + return nil, fmt.Errorf("DI: Field[%s] is not valid", fd.FieldName) + } + if !f.CanSet() { + return nil, fmt.Errorf("DI: Field[%s] can not set", fd.FieldName) + } + + if annotation = fd.GetAnnotationByType(reflect.TypeOf((*cdia.Inject)(nil)), false); nil != annotation { + if fV, err = cr.GetInstance(fd.Type); nil != err { + return nil, err + } + } + + if annotation = fd.GetAnnotationByType(reflect.TypeOf((*cdia.Resource)(nil)), false); nil != annotation { + n := annotation.(*cdia.Resource).Name + if "" == n { + n = fd.FieldName + } + if fV, err = cr.GetInstanceByName(n); nil != err { + return nil, err + } + } + + if nil != err { + return nil, fmt.Errorf("DI: %v", err) + } + f.Set(reflect.ValueOf(fV)) + } + + cr.instanceByType[td.RealType] = v.Interface() + + return v.Interface(), nil +} + +func GetInstanceByName(name string) (interface{}, error) { + return registry.GetInstanceByName(name) +} +func (cr *defaultComponentRegistry) GetInstanceByName(name string) (interface{}, error) { + v, ok := cr.resourceByName[name] + if ok { + return v, nil + } + return nil, fmt.Errorf("DI: Resource[%s] is not exist", name) +} + +// GetInstances returns instance of annotated +// n must be name of registered annotation +func GetInstances(ts []reflect.Type) ([]interface{}, error) { + return registry.GetInstances(ts) +} +func (cr *defaultComponentRegistry) GetInstances(ts []reflect.Type) ([]interface{}, error) { + var ( + i interface{} + err error + ) + instances := make([]interface{}, 0) + for _, t := range ts { + if i, err = cr.GetInstance(t); nil != err { + return nil, err + } + instances = append(instances, i) + } + + return instances, nil +} + +// GetInstancesByAnnotationName returns instance of annotated +// n must be name of registered annotation +func GetInstancesByAnnotationName(n string) ([]interface{}, error) { + return registry.GetInstancesByAnnotationName(n) +} +func (cr *defaultComponentRegistry) GetInstancesByAnnotationName(n string) ([]interface{}, error) { + var ( + i interface{} + err error + ) + instances := make([]interface{}, 0) + + for _, td := range cr.definitionByType { + if nil != td.GetAnnotation(n) { + if i, err = cr.GetInstance(td.Type); nil != err { + return nil, err + } + instances = append(instances, i) + } + } + + return instances, nil +} + +func (cr *defaultComponentRegistry) buildDefinition(t reflect.Type) (*TypeDefinition, error) { + if nil == t { + return nil, fmt.Errorf("t[reflect.Type] is nil") + } + + rt, pkgName, tName := cur.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, _, _ := cur.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 { + as, err := cda.ParseAnnotation(f.Tag) + if nil != err { + return + } + if nil != as && 0 < len(as) { + fRT, fPkgName, fTName := cur.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 { + ct := reflect.TypeOf((*cda.TypeAnnotation)(nil)).Elem() + + if f.Type != ct { + return false + } + + as, err := cda.ParseAnnotation(f.Tag) + if nil != err { + return false + } + + if nil != as && 0 < len(as) { + td.TypeAnnotations = as + return true + } + + return false +} diff --git a/registry/registry_test.go b/registry/registry_test.go new file mode 100644 index 0000000..4af1174 --- /dev/null +++ b/registry/registry_test.go @@ -0,0 +1,68 @@ +package registry + +import ( + "log" + "reflect" + "testing" + + "git.loafle.net/commons/di-go/annotation" + cdia "git.loafle.net/commons/di-go/injection/annotation" +) + +func TestRegisterType(t *testing.T) { + var ( + err error + css []interface{} + cs interface{} + ) + RegisterType(reflect.TypeOf((*AService)(nil))) + RegisterType(reflect.TypeOf((*BService)(nil))) + // RegisterType(reflect.TypeOf((*TestStruct2)(nil)), &cda.ComponentAnnotation{ + // Name: "test1", + // }) + // RegisterType(reflect.TypeOf((*TestStruct3)(nil)), &cda.ComponentAnnotation{ + // Name: "test2", + // }) + + // fs := getFields(reflect.TypeOf((*TestStruct3)(nil))) + // log.Printf("%v", fs) + + RegisterResource("List", []string{"dfdkf", "skgkfg"}) + + if css, err = GetInstancesByAnnotationName(cdia.ComponentTag); nil != err { + log.Printf("%v \n", err) + } + log.Printf("%v", css) + + if css, err = GetInstances([]reflect.Type{ + reflect.TypeOf((*AService)(nil)), + reflect.TypeOf((*BService)(nil)), + }); nil != err { + log.Printf("%v \n", err) + } + log.Printf("%v", css) + + if cs, err = GetInstance(reflect.TypeOf((*CService)(nil))); nil != err { + log.Printf("%v \n", err) + } + log.Printf("%v", cs.(*CService)) + +} + +type AService struct { + annotation.TypeAnnotation `annotation:"@Component(name=dkdkdf)"` + + NameA string +} + +type BService struct { + annotation.TypeAnnotation `annotation:"@Component()"` + + NameB string +} + +type CService struct { + AService *AService `annotation:"@Inject()"` + BService *BService `annotation:"@Inject()"` + List []string `annotation:"@Resource()"` +}