This commit is contained in:
crusader 2018-07-17 19:58:14 +09:00
commit b37874d849
8 changed files with 414 additions and 0 deletions

69
.gitignore vendored Normal file
View File

@ -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

32
.vscode/launch.json vendored Normal file
View File

@ -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
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
// Place your settings in this file to overwrite default and user settings.
{
}

17
annotation.go Normal file
View File

@ -0,0 +1,17 @@
package annotation
import (
"regexp"
)
const (
AnnotationTag = "annotation"
NameTag = "@name"
DefaultTag = "@default"
)
var (
AnnotationRGX = regexp.MustCompile(`@((?s).*?)\(((?s).*?)\)`)
AnnotationBodyRGX = regexp.MustCompile(`'([^\\\\']|\\')*'`)
)

8
definition.go Normal file
View File

@ -0,0 +1,8 @@
package annotation
import "reflect"
type Definition struct {
t reflect.Type
rt reflect.Type
}

165
registry.go Normal file
View File

@ -0,0 +1,165 @@
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"`
}

92
registry_test.go Normal file
View File

@ -0,0 +1,92 @@
package annotation
import (
"reflect"
"testing"
)
func TestAnnotationRegistry_Register(t *testing.T) {
type fields struct {
parent Registry
definitions map[string]*Definition
}
type args struct {
t reflect.Type
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &AnnotationRegistry{
parent: tt.fields.parent,
definitions: tt.fields.definitions,
}
if err := r.Register(tt.args.t); (err != nil) != tt.wantErr {
t.Errorf("AnnotationRegistry.Register() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestAnnotationRegistry_Get(t *testing.T) {
type fields struct {
parent Registry
definitions map[string]*Definition
}
type args struct {
f *reflect.StructField
}
tests := []struct {
name string
fields fields
args args
want map[reflect.Type]Annotation
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &AnnotationRegistry{
parent: tt.fields.parent,
definitions: tt.fields.definitions,
}
got, err := r.Get(tt.args.f)
if (err != nil) != tt.wantErr {
t.Errorf("AnnotationRegistry.Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("AnnotationRegistry.Get() = %v, want %v", got, tt.want)
}
})
}
}
func Test_findAnnotatedFields(t *testing.T) {
type args struct {
t reflect.Type
ft reflect.Type
deep bool
}
tests := []struct {
name string
args args
want map[string]*reflect.StructField
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := findAnnotatedFields(tt.args.t, tt.args.ft, tt.args.deep); !reflect.DeepEqual(got, tt.want) {
t.Errorf("findAnnotatedFields() = %v, want %v", got, tt.want)
}
})
}
}

28
type.go Normal file
View File

@ -0,0 +1,28 @@
package annotation
import "reflect"
var AnnotationType = reflect.TypeOf((*Annotation)(nil)).Elem()
const AnnotationName = "Annotation"
type Annotation interface {
}
var TypeAnnotationType = reflect.TypeOf((*TypeAnnotation)(nil)).Elem()
type TypeAnnotation interface {
Annotation
}
var ConstructorAnnotationType = reflect.TypeOf((*ConstructorAnnotation)(nil)).Elem()
type ConstructorAnnotation interface {
Annotation
}
var MethodAnnotationType = reflect.TypeOf((*MethodAnnotation)(nil)).Elem()
type MethodAnnotation interface {
Annotation
}