package di import ( "fmt" "log" "reflect" "strings" oa "git.loafle.net/overflow/annotation-go" "git.loafle.net/overflow/di-go/annotation" our "git.loafle.net/overflow/util-go/reflect" ) type Registry 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) } func New(parent Registry) Registry { r := &InstanceRegistry{ parent: parent, definitionByType: make(map[reflect.Type]*TypeDefinition, 0), definitionByName: make(map[string]*TypeDefinition, 0), instanceByType: make(map[reflect.Type]interface{}, 0), instanceByName: make(map[string]interface{}, 0), } if nil == r.parent { r.parent = AppRegistry } return r } var AppRegistry = &InstanceRegistry{ parent: nil, definitionByType: make(map[reflect.Type]*TypeDefinition, 0), definitionByName: make(map[string]*TypeDefinition, 0), instanceByType: make(map[reflect.Type]interface{}, 0), instanceByName: make(map[string]interface{}, 0), } type InstanceRegistry struct { parent Registry definitionByType map[reflect.Type]*TypeDefinition definitionByName map[string]*TypeDefinition instanceByType map[reflect.Type]interface{} instanceByName map[string]interface{} } func RegisterType(t reflect.Type) { AppRegistry.RegisterType(t) } func (r *InstanceRegistry) RegisterType(t reflect.Type) { if nil == t { log.Panicf("t[reflect.Type] is nil") } if !our.IsTypeKind(t, reflect.Struct, true) { log.Panicf("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("The type[%s] of Component is exist already", td.FullName) } r.definitionByType[td.Type] = td name := td.TypeName a, err := oa.GetTypeAnnotation(td.Type, annotation.InjectableAnnotationType) if nil != err { log.Panicf("%v", err) } if nil != a { ia := a.(*annotation.InjectableAnnotation) if "" != strings.Trim(ia.Name, " ") { name = ia.Name } } if eTD, ok := r.definitionByName[name]; ok { log.Panicf("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 AppRegistry.RegisterResource(name, resource) } func (r *InstanceRegistry) RegisterResource(name string, resource interface{}) error { if _, ok := r.instanceByName[name]; ok { return fmt.Errorf("Resource[%s] is already exist", name) } r.instanceByName[name] = resource return nil } func (r *InstanceRegistry) buildDefinition(t reflect.Type) (*TypeDefinition, error) { if nil == t { return nil, fmt.Errorf("t[reflect.Type] is nil") } rt, pkgName, tName := our.GetTypeInfo(t) td := &TypeDefinition{} td.FullName = FullName(pkgName, tName) td.PkgName = pkgName td.TypeName = tName td.Type = t td.RealType = rt return td, nil } // GetInstance returns instance of type // t must be pointer of struct func GetInstance(t reflect.Type) (interface{}, error) { return AppRegistry.GetInstance(t) } func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, err error) { if nil == t { return nil, fmt.Errorf("t[reflect.Type] is nil") } if reflect.Ptr != t.Kind() { return nil, fmt.Errorf("t[reflect.Type] must be pointer of struct") } if reflect.Struct != t.Elem().Kind() { return nil, fmt.Errorf("t[reflect.Type] must be pointer of struct") } var ( td *TypeDefinition comV interface{} ok bool ) rt, _, _ := our.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 ( fV interface{} ) ass, err := oa.GetAllFieldAnnotations(td.Type) if nil != err { return nil, fmt.Errorf("%v", err) } if nil != ass { for n, as := range ass { f := rv.FieldByName(n) if !f.IsValid() { err = fmt.Errorf("Field[%s] is not valid", n) return } if !f.CanSet() { err = fmt.Errorf("Field[%s] can not set", n) return } _, ok = as[annotation.InjectAnnotationType] if ok { if fV, err = r.GetInstance(f.Type()); nil != err { return } } if nil != err { return } f.Set(reflect.ValueOf(fV)) } } return } func GetInstanceByName(name string) (interface{}, error) { return AppRegistry.GetInstanceByName(name) } func (r *InstanceRegistry) GetInstanceByName(name string) (interface{}, error) { v, ok := r.instanceByName[name] if ok { return v, nil } return nil, fmt.Errorf("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 AppRegistry.GetInstances(ts) } func (r *InstanceRegistry) 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(at reflect.Type) ([]interface{}, error) { return AppRegistry.GetInstancesByAnnotationType(at) } func (r *InstanceRegistry) GetInstancesByAnnotationType(at reflect.Type) ([]interface{}, error) { var ( a oa.Annotation i interface{} err error ) instances := make([]interface{}, 0) for _, td := range r.definitionByType { a, err = oa.GetTypeAnnotation(td.Type, at) if nil != err { return nil, err } if nil != a { i, err = r.GetInstance(td.Type) if nil != err { return nil, err } instances = append(instances, i) } } return instances, nil }