forked from cocotyty/summer
/
basket.go
274 lines (269 loc) · 8.11 KB
/
basket.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package summer
import (
"errors"
"reflect"
"sort"
"strings"
)
var NotSupportStructErr = errors.New("sorry we not support struct now")
var NotSupportContainsDot = errors.New("sorry we not support name contains a dot")
var CannotResolveDependencyErr = errors.New("sorry,stone's dependency missed")
type Basket struct {
kv map[string][]*Holder
delayFields map[string][]*DelayField
plugins map[PluginWorkTime]pluginList
}
func (this *Basket)PutDelayField(field *DelayField) {
list, has := this.delayFields[field.tagOption.prefix]
if !has {
list = []*DelayField{}
}
this.delayFields[field.tagOption.prefix] = append(list, field)
}
func NewBasket() *Basket {
return &Basket{make(map[string][]*Holder), make(map[string][]*DelayField), make(map[PluginWorkTime]pluginList)}
}
// add a stone to basket,the stone must be struct's pointer
func (this *Basket) Add(name string, stone Stone) {
if strings.Contains(name, ".") {
panic(NotSupportContainsDot)
}
t := reflect.TypeOf(stone)
if t.Kind() != reflect.Ptr {
panic(NotSupportStructErr)
}
if holders, found := this.kv[name]; found {
this.kv[name] = append(holders, newHolder(stone, this))
} else {
this.kv[name] = []*Holder{newHolder(stone, this)}
}
}
// put a stone into basket ,the stone must be struct's pointer,the stone name will be that's type's name with first character lowercase
// for example,if stone's type is Foo then the stone will get a name that is "foo"
func (this *Basket) Put(stone Stone) {
t := reflect.TypeOf(stone)
var name string
if t.Kind() == reflect.Ptr {
name = t.Elem().Name()
} else if t.Kind() == reflect.Func {
name = "funcs"
} else {
panic(NotSupportStructErr)
}
name = strings.ToLower(name[:1]) + name[1:]
logger.Debug("regitor ", name)
if types, found := this.kv[name]; found {
this.kv[name] = append(types, newHolder(stone, this))
} else {
this.kv[name] = []*Holder{newHolder(stone, this)}
}
}
// register a plugin to basket
func (this *Basket) PluginRegister(plugin Plugin, t PluginWorkTime) {
logger.Debug("[plugin register][", plugin.Prefix(), "]", t)
list, ok := this.plugins[t]
if !ok {
list = pluginList{}
}
list = append(list, plugin)
this.plugins[t] = list
}
func (this *Basket) resolveStonesDirectlyDependents() {
this.Each(func(holder *Holder) {
holder.ResolveDirectlyDependents()
})
}
func (this *Basket) pluginWorks(worktime PluginWorkTime) {
logger.Debug("[plugin][start-tag-map]")
sort.Sort(this.plugins[worktime])
// choose which plugins will work at this worktime
list := this.plugins[worktime]
for _, plugin := range list {
logger.Debug("[plugin][load][", worktime, "]:", plugin.Prefix())
delayList := this.delayFields[plugin.Prefix()]
for _, field := range delayList {
this.pluginWork(plugin, field)
}
}
logger.Debug("[plugin][finish]")
}
func (this *Basket) pluginWork(plugin Plugin, field *DelayField) {
// find the value we need from plugin
foundValue := plugin.Look(field.Holder, field.tagOption.path, &field.filedInfo)
// verify value
if !foundValue.IsValid() {
logger.Error(plugin.Prefix(), ".", field.tagOption.path, " not found")
return
}
// verify if the field can set a value
if !field.filedValue.CanSet() {
logger.Error("can not set the value ", field.filedInfo.Name, " tag:", field.filedInfo.Tag, ",may be an unexported value ")
return
}
logger.Debug("[plugin][path]", field.Holder.Class, field.tagOption.path, foundValue.Interface())
if field.filedInfo.Type.Kind() == foundValue.Kind() {
field.filedValue.Set(foundValue)
return
}
if field.filedInfo.Type.Kind() == reflect.Interface {
if foundValue.Type().AssignableTo(field.filedInfo.Type)&&foundValue.Type().ConvertibleTo(field.filedInfo.Type) {
field.filedValue.Set(foundValue)
return
}
}
if field.filedInfo.Type.Kind() == reflect.Ptr && foundValue.Kind() != reflect.Ptr {
field.filedValue.Set(foundValue.Addr())
return
}
if field.filedInfo.Type.Kind() != reflect.Ptr && foundValue.Kind() == reflect.Ptr {
field.filedValue.Set(foundValue.Elem())
return
}
if ( field.filedInfo.Type.Kind() == reflect.Int || field.filedInfo.Type.Kind() == reflect.Int8 || field.filedInfo.Type.Kind() == reflect.Int16 || field.filedInfo.Type.Kind() == reflect.Int32 || field.filedInfo.Type.Kind() == reflect.Int64) {
switch value := foundValue.Interface().(type) {
case int:
field.filedValue.SetInt(int64(value))
case int8:
field.filedValue.SetInt(int64(value))
case int16:
field.filedValue.SetInt(int64(value))
case int32:
field.filedValue.SetInt(int64(value))
case int64:
field.filedValue.SetInt(int64(value))
default:
logger.Error("can not set the value ", field.filedInfo.Name, " tag:", field.filedInfo.Tag, " because ", field.filedInfo.Type, "!=", foundValue.Kind())
}
return
}
if ( foundValue.Kind() == reflect.Float64 || foundValue.Kind() == reflect.Float32 ) {
switch value := foundValue.Interface().(type) {
case float32:
field.filedValue.SetFloat(float64(value))
case float64:
field.filedValue.SetFloat(float64(value))
default:
logger.Error("can not set the value ", field.filedInfo.Name, " tag:", field.filedInfo.Tag, " because ", field.filedInfo.Type, "!=", foundValue.Kind())
}
return
}
logger.Error("can not set the value ", field.filedInfo.Name, " tag:", field.filedInfo.Tag, " because ", field.filedInfo.Type, "!=", foundValue.Kind())
}
// get a stone from basket
func (this *Basket) GetStone(name string, t reflect.Type) (stone Stone) {
if holder, found := this.kv[name]; found {
for _, h := range holder {
if stone, has := this.findStone(t, h); has {
return stone
}
}
}
for _, holder := range this.kv {
for _, h := range holder {
if stone, has := this.findStone(t, h); has {
return stone
}
}
}
return nil
}
// get a stone from basket
func (this *Basket) GetStoneWithName(name string) (stone Stone) {
if holders, found := this.kv[name]; found {
return holders[0].Stone
}
return nil
}
// get a stone holder from basket
func (this *Basket) GetStoneHolder(name string, t reflect.Type) (h *Holder) {
if holder, found := this.kv[name]; found {
for _, h := range holder {
if _, has := this.findStone(t, h); has {
return h
}
}
}
for _, holder := range this.kv {
for _, h := range holder {
if _, has := this.findStone(t, h); has {
return h
}
}
}
return nil
}
// get a stone holder from basket
func (this *Basket) GetStoneHolderWithName(name string) *Holder {
if holders, found := this.kv[name]; found {
return holders[0]
}
return nil
}
func (this *Basket) findStone(t reflect.Type, h *Holder) (Stone, bool) {
logger.Debug(t, h.Class)
if t.Kind() == reflect.Interface {
if reflect.TypeOf(h.Stone).Implements(t) {
return h.Stone, true
}
return nil, false
}
if t.Kind() == reflect.Struct {
t = reflect.PtrTo(t)
}
if h.PointerClass.AssignableTo(t) && h.PointerClass.ConvertibleTo(t) {
return h.Stone, true
}
return nil, false
}
// start work
//
// # 1 : summer will resolve the direct dependency
//
// # 2 : summer will call all stones's Init method<br/>
// if a directly depend on b, b directly depend on c and d ,then a will init after b init,and b will after c and d
//
// # 3 : summer will call all stones's Ready method<br/>
// if a depend on b, b depend on c and d ,then a will init after b init,and b will after c and d
//
func (this *Basket) Start() {
this.resolveStonesDirectlyDependents()
this.pluginWorks(BeforeInit)
this.initStones()
this.pluginWorks(AfterInit)
this.tellStoneReady()
}
func (this *Basket)initStones() {
set := map[*Holder]bool{}
this.Each(func(holder *Holder) {
holder.init(set)
})
}
func (this *Basket)tellStoneReady() {
set := map[*Holder]bool{}
this.Each(func(holder *Holder) {
holder.ready(set)
})
}
// shutdown will call all stone's Destroy method
func (this *Basket) ShutDown() {
set := map[*Holder]bool{}
this.Each(func(holder *Holder) {
holder.destroy(set)
})
}
func (this *Basket)Each(fn func(holder *Holder)) {
for _, holders := range this.kv {
for _, holder := range holders {
fn(holder)
}
}
}
func (this *Basket)EachHolder(fn func(name string, holder *Holder) bool) {
for name, holders := range this.kv {
for _, holder := range holders {
if fn(name, holder) {
return
}
}
}
}