package annotation import ( "encoding/json" "fmt" "reflect" "strings" cur "git.loafle.net/commons/util-go/reflect" ) type Registry interface { Register(t reflect.Type) error Get(f *reflect.StructField) (map[reflect.Type]Annotation, error) } var SystemRegistry = &AnnotationRegistry{ parent: nil, definitions: make(map[string]*Definition, 0), } func New(parent Registry) Registry { r := &AnnotationRegistry{ parent: parent, definitions: make(map[string]*Definition, 0), } if nil == r.parent { r.parent = SystemRegistry } return r } type AnnotationRegistry struct { parent Registry definitions map[string]*Definition } func Register(t reflect.Type) error { return SystemRegistry.Register(t) } func (r *AnnotationRegistry) Register(t reflect.Type) error { rt, _, _ := cur.GetTypeInfo(t) fields := findAnnotatedFields(t, AnnotationType, false) switch len(fields) { case 0: return fmt.Errorf("type[%s] is not Annotation", rt.Name()) case 1: default: return fmt.Errorf("type[%s] have only one Annotation", rt.Name()) } f := fields[AnnotationName] name := strings.TrimSpace(f.Tag.Get(NameTag)) if "" == name { return fmt.Errorf("annotation name of type[%s] is not valid", rt.Name()) } if _, ok := r.definitions[name]; ok { return fmt.Errorf("name[%s] of annotation exist already", name) } r.definitions[name] = &Definition{ t: t, rt: rt, } return nil } func Get(f *reflect.StructField) (map[reflect.Type]Annotation, error) { return SystemRegistry.Get(f) } func (r *AnnotationRegistry) Get(f *reflect.StructField) (map[reflect.Type]Annotation, error) { annotations := make(map[reflect.Type]Annotation, 0) tag := strings.TrimSpace(f.Tag.Get(AnnotationTag)) if "" == tag { return annotations, nil } if !AnnotationRGX.MatchString(tag) { return nil, fmt.Errorf("Tag of annotation[%s] is not match", tag) } rss := AnnotationRGX.FindAllStringSubmatch(tag, -1) if nil == rss || 0 == len(rss) { return annotations, nil } for _, rs := range rss { if 3 != len(rs) { return nil, fmt.Errorf("Tag of annotation[%s] is not valid", rs[0]) } name := fmt.Sprintf("@%s", strings.TrimSpace(rs[1])) body := rs[2] if !AnnotationBodyRGX.MatchString(body) { return nil, fmt.Errorf("Body[%s] of annotation[%s] is not valid", body, name) } body = AnnotationBodyRGX.ReplaceAllStringFunc(body, func(token string) string { switch len(token) { case 0, 1, 2: return "\"\"" default: return strings.Replace(fmt.Sprintf("\"%s\"", token[1:len(token)-1]), "\\'", "'", -1) } }) body = fmt.Sprintf("{%s}", strings.TrimSpace(body)) def, ok := r.definitions[name] if !ok { return nil, fmt.Errorf("annotation[%s] is not exist", name) } v := reflect.New(def.rt) i := v.Interface() err := json.Unmarshal([]byte(body), i) if nil != err { return nil, fmt.Errorf("Unmarshal failed %v", err) } annotations[def.t] = i } return annotations, nil } func findAnnotatedFields(t reflect.Type, ft reflect.Type, deep bool) map[string]*reflect.StructField { fields := make(map[string]*reflect.StructField, 0) rt, _, _ := cur.GetTypeInfo(t) if reflect.Struct != rt.Kind() { return fields } LOOP: for i := 0; i < rt.NumField(); i++ { f := rt.Field(i) if f.Anonymous { if f.Type == ft { fields[f.Name] = &f continue LOOP } if deep { _fields := findAnnotatedFields(f.Type, ft, deep) for _n, _f := range _fields { fields[_n] = _f } } } } return fields } var InjectableAnnotationType = reflect.TypeOf((*InjectableAnnotation)(nil)) type InjectableAnnotation struct { Annotation `@name:"@Component"` Name string `json:"name" @default:"test"` }