-
Notifications
You must be signed in to change notification settings - Fork 5
/
container.go
190 lines (157 loc) · 4.05 KB
/
container.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
package pgo2
import (
"reflect"
"strings"
"sync"
"github.com/pinguo/pgo2/iface"
)
type bindItem struct {
pool sync.Pool // object pool
info interface{} // binding info
zero reflect.Value // zero value
cmIdx int // construct index
pmIdx int // prepare index
}
const (
EnablePoolOn = "on"
// EnablePoolOff = "off"
)
// Container the container component, configuration:
// container:
// enablePool: on/off
func NewContainer(enable string) *Container {
if enable == "" {
enable = EnablePoolOn
}
return &Container{
items: make(map[string]*bindItem),
enablePool: enable,
}
}
type Container struct {
enablePool string
items map[string]*bindItem
}
// Bind bind template object to class,
// param i must be a pointer of struct.
func (c *Container) Bind(i interface{}) string {
iv := reflect.ValueOf(i)
if iv.Kind() != reflect.Ptr {
panic("Container: invalid type, need pointer")
}
// initialize binding
rt := iv.Elem().Type()
item := bindItem{zero: reflect.Zero(rt), cmIdx: -1, pmIdx: -1}
item.pool.New = func() interface{} { return reflect.New(rt) }
// get binding info
if bind, ok := i.(iface.IBind); ok {
item.info = bind.GetBindInfo(i)
}
// get method index
it := iv.Type()
nm := it.NumMethod()
for i := 0; i < nm; i++ {
switch it.Method(i).Name {
case ConstructMethod:
item.cmIdx = i
case PrepareMethod:
item.pmIdx = i
}
}
// get class name
pkgPath := rt.PkgPath()
if index := strings.Index(pkgPath, "/"+ControllerWebPkg); index >= 0 {
pkgPath = pkgPath[index+1:]
}
if index := strings.Index(pkgPath, "/"+ControllerCmdPkg); index >= 0 {
pkgPath = pkgPath[index+1:]
}
name := pkgPath + "/" + rt.Name()
if len(name) > VendorLength && name[:VendorLength] == VendorPrefix {
name = name[VendorLength:]
}
c.items[name] = &item
return name
}
// Has check if the class exists in container
func (c *Container) Has(name string) bool {
_, ok := c.items[name]
return ok
}
// GetInfo get class binding info
func (c *Container) GetInfo(name string) interface{} {
if item, ok := c.items[name]; ok {
return item.info
}
panic("Container: class not found, " + name)
}
// GetType get class reflect type
func (c *Container) GetType(name string) reflect.Type {
if item, ok := c.items[name]; ok {
return item.zero.Type()
}
panic("Container: class not found, " + name)
}
// getNew get new class object
func (c *Container) getNew(name string) reflect.Value {
item, ok := c.items[name]
if !ok {
panic("Container: class not found, " + name)
}
// get new object from pool
return item.pool.Get().(reflect.Value)
}
// Get get new class object. name is class name, config is properties map,
// params is optional construct parameters.
func (c *Container) Get(name string, ctx iface.IContext, params ...interface{}) reflect.Value {
item, ok := c.items[name]
if !ok {
panic("Container: class not found, " + name)
}
// get new object from pool
rv := item.pool.Get().(reflect.Value)
if c.enablePool == EnablePoolOn {
// reset properties
rv.Elem().Set(item.zero)
ctx.Cache(name, rv)
}
if obj, ok := rv.Interface().(iface.IObject); ok {
// inject context
obj.SetContext(ctx)
}
// call Prepare()
if item.pmIdx != -1 {
if im := rv.Method(item.pmIdx); im.IsValid() {
in := make([]reflect.Value, 0)
if im.Type().NumIn() > 0 {
in = make([]reflect.Value, len(params))
for k, arg := range params {
in[k] = reflect.ValueOf(arg)
}
}
im.Call(in)
}
}
return rv
}
// Put put back reflect value to object pool
func (c *Container) Put(name string, rv reflect.Value) {
if item, ok := c.items[name]; ok {
item.pool.Put(rv)
return
}
panic("Container: class not found, " + name)
}
// PathList Gets a list of paths with the specified path prefix
func (c *Container) PathList(prefix, suffix string) map[string]interface{} {
list := make(map[string]interface{})
for k, item := range c.items {
//if strings.Index(k, prefix) == 0 && strings.Index(k, suffix) > 0 {
// list[k] = item.info
//}
if strings.Index(k, prefix) == 0 {
list[k] = item.info
}
}
return list
}