annotation-go/registry.go
2019-11-13 22:53:42 +09:00

174 lines
3.3 KiB
Go

package annotation
import (
"encoding/json"
"fmt"
"reflect"
"strings"
luReflect "git.loafle.net/loafer/util-go/reflect"
)
func init() {
registry = newRegistry()
}
// Definition is struct
type Definition struct {
t reflect.Type
rt reflect.Type
}
// Regist is interface
type Regist interface {
// Register is method
Register(t reflect.Type) error
// Parse is method
Parse(tag reflect.StructTag) (map[reflect.Type]Annotation, error)
}
// Registry is struct
type Registry struct {
definitions map[string]*Definition
}
var registry Regist
func newRegistry() Regist {
r := &Registry{}
r.definitions = make(map[string]*Definition, 0)
return r
}
// Register is method
func Register(t reflect.Type) error {
return registry.Register(t)
}
// Register is method
func (r *Registry) Register(t reflect.Type) error {
rt, _, _ := luReflect.GetTypeInfo(t)
f := r.getTypeAnnotationField(t)
if nil == f {
return fmt.Errorf("Annotation: This type[%s] is not Annotation. use TypeAnnotation", rt.Name())
}
name := r.parseAnnotationMeta(f.Tag)
if _, ok := r.definitions[name]; ok {
return fmt.Errorf("Annotation: name[%s] of annotation exist already", name)
}
def := &Definition{
t: t,
rt: rt,
}
r.definitions[name] = def
return nil
}
// Parse is method
func Parse(tag reflect.StructTag) (map[reflect.Type]Annotation, error) {
return registry.Parse(tag)
}
// Parse is method
func (r *Registry) Parse(tag reflect.StructTag) (map[reflect.Type]Annotation, error) {
s := strings.Trim(tag.Get(AnnotationTag), " ")
if "" == s {
return nil, nil
}
am, err := r.splitAnnotation(s)
if nil != err {
return nil, err
}
if nil == am || 0 == len(am) {
return nil, nil
}
rKVs := make(map[reflect.Type]Annotation, 0)
for name, attributes := range am {
t, annotation, err := r.buildAnnotation(name, attributes)
if nil != err {
return nil, err
}
rKVs[t] = annotation
}
return rKVs, nil
}
func (r *Registry) getTypeAnnotationField(t reflect.Type) *reflect.StructField {
rt, _, _ := luReflect.GetTypeInfo(t)
if reflect.Struct != rt.Kind() {
return nil
}
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
if f.Anonymous {
if f.Type == TypeAnnotationType {
return &f
}
}
}
return nil
}
func (r *Registry) parseAnnotationMeta(tag reflect.StructTag) string {
return strings.Trim(tag.Get(AnnotationMetaTag), " ")
}
func (r *Registry) splitAnnotation(s string) (map[string]string, error) {
ss := make(map[string]string, 0)
ts := s
for {
as := strings.Index(ts, AnnotationChar)
if -1 == as {
break
}
aas := strings.Index(ts, AnnotationStartChar)
aae := strings.Index(ts, AnnotationEndChar)
aName := ts[as:aas]
aAttributes := ts[aas+1 : aae]
if 0 < len(aAttributes) {
ss[aName] = aAttributes
} else {
ss[aName] = ""
}
if len(ts) <= (aae + 1) {
break
}
ts = ts[aae+1:]
}
return ss, nil
}
func (r *Registry) buildAnnotation(name string, attributes string) (reflect.Type, Annotation, error) {
def, ok := r.definitions[name]
if !ok {
return nil, nil, fmt.Errorf("There is no annotation[%s]", name)
}
v := reflect.New(def.rt)
i := v.Interface().(Annotation)
if "" != attributes {
_json := fmt.Sprintf("{%s}", attributes)
if err := json.Unmarshal([]byte(_json), i); nil != err {
return nil, nil, err
}
}
return def.t, i, nil
}